1 /*--------------------------------------------------------------------
2  *
3  *	Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4  *	See LICENSE.TXT file for copying and redistribution conditions.
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU Lesser General Public License as published by
8  *	the Free Software Foundation; version 3 or any later version.
9  *
10  *	This program is distributed in the hope that it will be useful,
11  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *	GNU Lesser General Public License for more details.
14  *
15  *	Contact info: www.generic-mapping-tools.org
16  *--------------------------------------------------------------------*/
17 /*
18  *			G M T _ M A P . C
19  *
20  *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
21  * GMT_map.c contains code related to generic coordinate transformation.
22  * For the actual projection functions, see gmt_proj.c
23  *
24  *	Map Transformation Setup Routines
25  *	These routines initializes the selected map transformation
26  *	The names and main function are listed below
27  *	NB! Note that the transformation function does not check that they are
28  *	passed valid lon,lat numbers. I.e asking for log10 scaling using values
29  *	<= 0 results in problems.
30  *
31  * The ellipsoid used is selectable by editing the gmt.conf in your
32  * home directory.  If no such file, create one by running gmtdefaults.
33  *
34  * Usage: Initialize system by calling gmt_g (separate module), and
35  * then just use gmt_geo_to_xy() and gmt_xy_to_geo() functions.
36  *
37  * Author:	Paul Wessel
38  * Date:	1-JAN-2010
39  * Version:	5
40  */
41 
42 /*!
43  * \file gmt_map.c
44  * \brief gmt_map.c contains code related to generic coordinate transformation.
45  *
46  * PUBLIC GMT Functions include:
47  *
48  *	gmt_azim_to_angle :	Converts azimuth to angle on the map\n
49  *	gmt_clip_to_map :	Force polygon points to be inside map\n
50  *	gmt_compact_line :	Remove redundant pen movements\n
51  *	gmt_geo_to_xy :		Generic lon/lat to x/y\n
52  *	gmt_geo_to_xy_line :	Same for polygons\n
53  *	gmt_geoz_to_xy :	Generic 3-D lon/lat/z to x/y\n
54  *	gmt_grd_project :	Generalized grid projection with interpolation\n
55  *	GMT_great_circle_dist :	Returns great circle distance in degrees\n
56  *	gmt_img_project :	Generalized image projection with interpolation\n
57  *	gmt_map_outside :	Generic function determines if we're outside map boundary\n
58  *	gmtlib_map_path :	Return gmtlib_latpath or gmtlib_lonpath\n
59  *	gmt_map_setup :		Initialize map projection\n
60  *	gmt_project_init :	Initialize parameters for grid/image transformations\n
61  *	gmt_xy_to_geo :		Generic inverse x/y to lon/lat projection\n
62  *	gmt_xyz_to_xy :		Generic xyz to xy projection\n
63  *
64  * Internal GMT Functions include:
65  *
66  *	gmtmap_get_origin :		Find origin of projection based on pole and 2nd point\n
67  *	gmtmap_get_rotate_pole :	Find rotation pole based on two points on great circle\n
68  *	gmtmap_ilinearxy :		Inverse linear projection\n
69  *	gmtmap_init_three_D :		Initializes parameters needed for 3-D plots\n
70  *	gmtmap_crossing :		Generic function finds crossings between line and map boundary\n
71  *	gmtlib_latpath :		Return path between 2 points of equal latitude\n
72  *	gmtlib_lonpath :		Return path between 2 points of equal longitude\n
73  *	gmtmap_radial_crossing :	Determine map crossing in the Lambert azimuthal equal area projection\n
74  *	gmtlib_left_boundary :		Return left boundary in x-inches\n
75  *	gmtmap_linearxy :		Linear xy projection\n
76  *	gmtmap_lon_inside :		Accounts for wrap-around in longitudes and checks for inside\n
77  *	gmtmap_ellipse_crossing :	Find map crossings in the Mollweide projection\n
78  *	gmtmap_move_to_rect :		Move an outside point straight in to nearest edge\n
79  *	gmtmap_polar_outside :		Determines if a point is outside polar projection region\n
80  *	gmtmap_pole_rotate_forward :	Compute positions from oblique coordinates\n
81  *	gmtmap_radial_clip :		Clip path outside radial region\n
82  *	gmtmap_radial_outside :		Determine if point is outside radial region\n
83  *	gmtmap_radial_overlap :		Determine overlap, always true for his projection\n
84  *	gmtmap_rect_clip :		Clip to rectangular region\n
85  *	gmtmap_rect_crossing :		Find crossing between line and rect region\n
86  *	gmtmap_rect_outside :		Determine if point is outside rect region\n
87  *	gmtmap_rect_outside2 :		Determine if point is outside rect region (azimuthal proj only)\n
88  *	gmtmap_rect_overlap :		Determine overlap between rect regions\n
89  *	gmtlib_right_boundary :		Return x value of right map boundary\n
90  *	gmtmap_xy_search :		Find xy map boundary\n
91  *	map_wesn_clip:			Clip polygon to wesn boundaries\n
92  *	gmtmap_wesn_crossing :		Find crossing between line and lon/lat rectangle\n
93  *	gmtmap_wesn_outside :		Determine if a point is outside a lon/lat rectangle\n
94  *	gmtmap_wesn_overlap :		Determine overlap between lon/lat rectangles\n
95  *	gmt_wesn_search :		Search for extreme coordinates\n
96  *	GMT_wrap_around_check_{x,tm} :	Check if line wraps around due to Greenwich\n
97  *	gmt_x_to_xx :			Generic linear x projection\n
98  *	map_xx_to_x :			Generic inverse linear x projection\n
99  *	gmt_y_to_yy :			Generic linear y projection\n
100  *	map_yy_to_y :			Generic inverse linear y projection\n
101  *	gmt_z_to_zz :			Generic linear z projection\n
102  *	map_zz_to_z :			Generic inverse linear z projection\n
103  */
104 
105 #include "gmt_dev.h"
106 #include "gmt_internals.h"
107 
108 /* We put all the declaration of external functions from gmt_proj.h here since
109  * (a) they are only called in gmt_map.c so no need to go in gmt_internals.h
110  * (b) this will change completely when proj4 is relacing gmt_proj in GMT 6.
111  */
112 
113 #include "gmt_proj.c"
114 
115 /*! CCW order of side in some tests */
116 enum GMT_side {
117 	GMT_BOTTOM = 0,
118 	GMT_RIGHT = 1,
119 	GMT_TOP = 2,
120 	GMT_LEFT = 3};
121 
122 /* Basic error reporting when things go badly wrong. This Error_and_return macro can be
123  * used in stead of regular return(code) to print out what the code is before
124  * it returns.  We assume the GMT pointer is available in the function!
125  */
126 #define Error_and_return(code,err) { GMT_Report(GMT->parent,GMT_MSG_ERROR,"%s\n",gmt_error_string[code]); return (err);}
127 
128 /* Note by P. Wessel, 18-Oct-2012, updated 08-JAN-2014:
129  * In the olden days, GMT only did great circle distances.  In GMT 4 we implemented geodesic
130  * distances by Rudoe's formula as given in Bomford [1971].  However, that geodesic is not
131  * exactly what we wanted as it is a normal section and does not strictly follow the geodesic.
132  * Other candidates are Vincenty [1975], which is widely used and Karney [2012], which is super-
133  * accurate.  At this point their differences are in the micro-meter level.  For GMT 5 we have
134  * now switched to the Vincenty algorithm as provided by Gerald Evenden, USGS [author of proj4],
135  * which is a modified translation of the NGS algorithm and not exactly what is in proj4's geod
136  * program (which Evenden thinks is inferior.)  I ran a comparison between many algorithms that
137  * either were available via codes or had online calculators.  I sought the geodesic distance
138  * from (0,0) to (10,10) on WGS-84; the results were (in meters):
139  *
140  *	GMT4 (Rudoe):		1565109.099232116
141  *	proj4:			1565109.095557918
142  *	vdist(0,0,10,10) [0]	1565109.09921775
143  *	Karney [1]: 		1565109.09921789
144  *	Vincenty [2]:		1565109.099218036
145  *	NGS [3]			1565109.0992
146  *	Andoyer [4]		1565092.276857755
147  *
148  * [0] via Joaquim Luis, supposedly Vincenty [2012]
149  * [1] via online calculator at max precision http://geographiclib.sourceforge.net/cgi-bin/Geod
150  * [2] downloading, compiling and running http://article.gmane.org/gmane.comp.gis.proj-4.devel/3478.
151  *     This is not identical to Vincenty in proj4 but written by Evenden (proj.4 author)
152  * [3] via online calculator http://www.ngs.noaa.gov/cgi-bin/Inv_Fwd/inverse2.prl. Their notes says
153  *     this is Vincenty; unfortunately I cannot control the output precision.
154  * [4] Andoyer approximate from Astronomical Algorithms, Jean Meeus, 2009, second edition, Willmann-Bell, Inc.
155  *
156  * Based on these comparisons we decided to implement the Vincenty [2] code as given.  The older Rudoe
157  * code is also accessible, as is the approximation by Andoyer which is good to a few tens of m.
158  * The choice was based on the readily available C code versus having to reimplement Karney in C.
159  * When GMT 6 is started we expect to use proj4
160  */
161 
162 static char *GEOD_TEXT[3] = {"Vincenty", "Andoyer", "Rudoe"};
163 
164 /*! . */
gmtmap_get_angle(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)165 GMT_LOCAL double gmtmap_get_angle (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
166 	double x1, y1, x2, y2, angle, direction;
167 
168 	gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
169 	gmt_geo_to_xy (GMT, lon2, lat2, &x2, &y2);
170 	if (doubleAlmostEqualZero (y1, y2) && doubleAlmostEqualZero (x1, x2)) {	/* Special case that only(?) occurs at N or S pole or r=0 for GMT_POLAR */
171 		if (fabs (fmod (lon1 - GMT->common.R.wesn[XLO] + 360.0, 360.0)) > fabs (fmod (lon1 - GMT->common.R.wesn[XHI] + 360.0, 360.0))) {	/* East */
172 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], &x1, &y1);
173 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &x2, &y2);
174 			GMT->current.map.corner = 1;
175 		}
176 		else {
177 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &x1, &y1);
178 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &x2, &y2);
179 			GMT->current.map.corner = 3;
180 		}
181 		angle = d_atan2d (y2-y1, x2-x1) - 90.0;
182 		if (GMT->current.proj.got_azimuths) angle += 180.0;
183 		if (GMT->current.proj.flip) angle += 180.0;
184 	}
185 	else
186 		angle = d_atan2d (y2 - y1, x2 - x1);
187 
188 	if (abs (GMT->current.map.prev_x_status) == 2 && abs (GMT->current.map.prev_y_status) == 2)	/* Last point outside */
189 		direction = angle + 180.0;
190 	else if (GMT->current.map.prev_x_status == 0 && GMT->current.map.prev_y_status == 0)		/* Last point inside */
191 		direction = angle;
192 	else {
193 		if (abs (GMT->current.map.this_x_status) == 2 && abs (GMT->current.map.this_y_status) == 2)	/* This point outside */
194 			direction = angle;
195 		else if (GMT->current.map.this_x_status == 0 && GMT->current.map.this_y_status == 0)		/* This point inside */
196 			direction = angle + 180.0;
197 		else {	/* Special case of corners and sides only */
198 			if (GMT->current.map.prev_x_status == GMT->current.map.this_x_status)
199 				direction = (GMT->current.map.prev_y_status == 0) ? angle : angle + 180.0;
200 			else if (GMT->current.map.prev_y_status == GMT->current.map.this_y_status)
201 				direction = (GMT->current.map.prev_x_status == 0) ? angle : angle + 180.0;
202 			else
203 				direction = angle;
204 
205 		}
206 	}
207 
208 	if (direction < 0.0) direction += 360.0;
209 	if (direction >= 360.0) direction -= 360.0;
210 	return (direction);
211 }
212 
213 
214 /*! . */
gmtlib_left_boundary(struct GMT_CTRL * GMT,double y)215 double gmtlib_left_boundary (struct GMT_CTRL *GMT, double y) {
216 	return ((*GMT->current.map.left_edge) (GMT, y));
217 }
218 
219 /*! . */
gmtlib_right_boundary(struct GMT_CTRL * GMT,double y)220 double gmtlib_right_boundary (struct GMT_CTRL *GMT, double y) {
221 	return ((*GMT->current.map.right_edge) (GMT, y));
222 }
223 
224 /* Private functions internal to gmt_map.c */
225 
226 /*! . */
gmtmap_quickconic(struct GMT_CTRL * GMT)227 GMT_LOCAL bool gmtmap_quickconic (struct GMT_CTRL *GMT) {
228 	/* Returns true if area/scale are large/small enough
229 	 * so that we can use spherical equations with authalic
230 	 * or conformal latitudes instead of the full ellipsoidal
231 	 * equations.
232 	 */
233 
234 	double s, dlon, width;
235 
236 	if (GMT->current.proj.gave_map_width) {	/* Gave width */
237 		dlon = GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO];
238 		width = GMT->current.proj.pars[4] * GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_M];	/* Convert to meters */
239 		s = (dlon * GMT->current.proj.M_PR_DEG) / width;
240 	}
241 	else if (GMT->current.proj.units_pr_degree) {	/* Gave scale */
242 		/* Convert to meters */
243 		s = GMT->current.proj.M_PR_DEG / (GMT->current.proj.pars[4] * GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_M]);
244 	}
245 	else {	/* Got 1:xxx that was changed */
246 		s = (1.0 / GMT->current.proj.pars[4]) / GMT->current.proj.unit;
247 	}
248 
249 	if (s > 1.0e7) {	/* if s in 1:s exceeds 1e7 we do the quick thing */
250 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Using spherical projection with conformal latitudes\n");
251 		return (true);
252 	}
253 	else /* Use full ellipsoidal terms */
254 		return (false);
255 }
256 
257 /*! . */
gmtmap_quicktm(struct GMT_CTRL * GMT,double lon0,double limit)258 GMT_LOCAL bool gmtmap_quicktm (struct GMT_CTRL *GMT, double lon0, double limit) {
259 	/* Returns true if the region chosen is too large for the
260 	 * ellipsoidal series to be valid; hence use spherical equations
261 	 * with authalic latitudes instead.
262 	 * We let +-limit degrees from central meridian be the cutoff.
263 	 */
264 
265 	double d_left, d_right;
266 
267 	d_left  = lon0 - GMT->common.R.wesn[XLO] - 360.0;
268 	d_right = lon0 - GMT->common.R.wesn[XHI] - 360.0;
269 	while (d_left  < -180.0) d_left  += 360.0;
270 	while (d_right < -180.0) d_right += 360.0;
271 	if (fabs (d_left) > limit || fabs (d_right) > limit) {
272 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Using spherical projection with authalic latitudes\n");
273 		return (true);
274 	}
275 	else /* Use full ellipsoidal terms */
276 		return (false);
277 }
278 
279 /*! . */
gmtmap_set_polar(struct GMT_CTRL * GMT)280 GMT_LOCAL void gmtmap_set_polar (struct GMT_CTRL *GMT) {
281 	/* Determines if the projection pole is N or S pole */
282 
283 	if (doubleAlmostEqual (fabs (GMT->current.proj.pars[1]), 90.0)) {
284 		GMT->current.proj.polar = true;
285 		GMT->current.proj.north_pole = (GMT->current.proj.pars[1] > 0.0);
286 	}
287 }
288 
gmtmap_central_meridian_not_set(struct GMT_CTRL * GMT)289 GMT_LOCAL bool gmtmap_central_meridian_not_set (struct GMT_CTRL *GMT) {
290 	/* Just to make it clearer to understand the code.  If NaN then we were never given a central meridian */
291 	return (gmt_M_is_dnan (GMT->current.proj.pars[0]));
292 }
293 
gmtmap_set_default_central_meridian(struct GMT_CTRL * GMT)294 GMT_LOCAL void gmtmap_set_default_central_meridian (struct GMT_CTRL *GMT) {
295 	/* Pick half-way between w and e, but watch for -R+r */
296 	if (GMT->common.R.oblique && GMT->common.R.wesn[XHI] < GMT->common.R.wesn[XLO])
297 		GMT->current.proj.pars[0] = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI] + 360.0);	/* Set to middle lon  but watch for e < w */
298 	else
299 		GMT->current.proj.pars[0] = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);	/* Set to middle lon */
300 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian not given, default to %g\n", GMT->current.proj.pars[0]);
301 }
302 
303 /*! . */
gmtmap_cyl_validate_clon(struct GMT_CTRL * GMT,unsigned int mode)304 GMT_LOCAL void gmtmap_cyl_validate_clon (struct GMT_CTRL *GMT, unsigned int mode) {
305 	/* Make sure that for global (360-range) cylindrical projections, the central meridian is neither west nor east.
306 	 * If so then we reset it to the middle value or we change -R:
307 	 * mode == 0: <clon> should be reset based on w/e mid-point
308 	 * mode == 1: -J<clon> is firm so w/e is centered on <c.lon>
309 	 */
310 	if (gmtmap_central_meridian_not_set (GMT))
311 		gmtmap_set_default_central_meridian (GMT);
312 	else if (GMT->current.map.is_world && (GMT->current.proj.pars[0] == GMT->common.R.wesn[XLO] || GMT->current.proj.pars[0] == GMT->common.R.wesn[XHI])) {
313 		/* Reset central meridian since cannot be 360 away from one of the boundaries since that gives xmin == xmax below */
314 		if (mode == 1) {	/* Change -R to fit central meridian */
315 			double w = GMT->current.proj.pars[0] - 180.0, e = GMT->current.proj.pars[0] + 180.0;
316 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Region for global cylindrical projection had to be reset from %g/%g to %g/%g\n",
317 				GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], w, e);
318 			GMT->common.R.wesn[XLO] = w;	GMT->common.R.wesn[XHI] = e;
319 		}
320 		else {	/* Change central meridian to fit -R */
321 			double new_lon = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
322 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian for global cylindrical projection had to be reset from %g to %g\n", GMT->current.proj.pars[0], new_lon);
323 			GMT->current.proj.pars[0] = new_lon;
324 		}
325 	}
326 	else if (!GMT->current.map.is_world) {	/* For reginal areas we cannot have clon > 180 away from either boundary */
327 		if (fabs (GMT->current.proj.pars[0] - GMT->common.R.wesn[XLO]) > 180.0 || fabs (GMT->current.proj.pars[0] - GMT->common.R.wesn[XHI]) > 180.0) {
328 			double new_lon = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
329 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian for cylindrical projection had to be reset from %g to %g\n", GMT->current.proj.pars[0], new_lon);
330 			GMT->current.proj.pars[0] = new_lon;
331 		}
332 	}
333 }
334 
335 /*! . */
gmtmap_lat_swap_init(struct GMT_CTRL * GMT)336 GMT_LOCAL void gmtmap_lat_swap_init (struct GMT_CTRL *GMT) {
337 	/* Initialize values in GMT->current.proj.lat_swap_vals based on GMT->current.proj.
338 
339 	First compute GMT->current.proj.lat_swap_vals.ra (and rm), the radii to use in
340 	spherical formulae for area (respectively, N-S distance) when
341 	using the authalic (respectively, meridional) latitude.
342 
343 	Then for each type of swap:
344 	First load GMT->current.proj.lat_swap_vals.c[itype][k], k=0,1,2,3 with the
345 	coefficient for sin(2 (k +1) phi), based on series from Adams.
346 	Next reshuffle these coefficients so they form a nested
347 	polynomial using equations (3-34) and (3-35) on page 19 of
348 	Snyder.
349 
350 	References:  J. P. Snyder, "Map projections - a working manual",
351 	U. S. Geological Survey Professional Paper #1395, 1987.
352 	O. S. Adams, "Latitude Developments Connected With Geodesy and
353 	Cartography", U. S. Coast and Geodetic Survey Special Publication
354 	number 67, 1949.
355 	P. D. Thomas, "Conformal Projections in Geodesy and Cartography",
356 	US CGS Special Pub #251, 1952.
357 	See also other US CGS Special Pubs (#53, 57, 68, 193, and 251).
358 
359 	Latitudes are named as follows (this only partly conforms to
360 	names in the literature, which are varied):
361 
362 	Geodetic   = G, angle between ellipsoid normal and equator
363 	geocentric = O, angle between radius from Origin and equator
364 	Parametric = P, angle such that x=a*cos(phi), y=b*sin(phi) is on ellipse
365 	Authalic   = A, angle to use in equal area   development of ellipsoid
366 	Conformal  = GMT, angle to use in conformal    development of ellipsoid
367 	Meridional = M, angle to use in N-S distance calculation of ellipsoid
368 
369 	(The parametric latitude is the one used in orthogonal curvilinear
370 	coordinates and ellipsoidal harmonics.  The term "authalic" was coined
371 	by A. Tissot in "Memoire sur la Representations des Surfaces et les
372 	projections des cartes geographiques", Gauthier Villars, Paris, 1881;
373 	it comes from the Greek meaning equal area.)
374 
375 	The idea of latitude swaps is this:  Conformal, equal-area, and other
376 	developments of spherical surfaces usually lead to analytic formulae
377 	for the forward and inverse projections which are stable over a wide
378 	range of the values.  It is handy to use the same formulae when
379 	developing the surface of the ellipsoid.  The authalic (respectively,
380 	conformal) lat is such that when plugged into a spherical development
381 	formula, it results in an authalic (meaning equal area) (respectively,
382 	conformal) development of the ellipsoid.  The meridional lat does the
383 	same thing for measurement of N-S distances along a meridian.
384 
385 	Adams gives coefficients for series in sin(2 (k +1) phi), for k
386 	up to 2 or 3.  I have extended these to k=3 in all cases except
387 	the authalic.  I have sometimes multiplied his coefficients by
388 	(-1) so that the sense here is always to give a correction to be
389 	added to the input lat to get the output lat in gmt_lat_swap().
390 
391 	I have tested this code by checking that
392 		fabs (geocentric) < fabs (parametric) < fabs (geodetic)
393 	and also, for each pair of possible conversions, that the
394 	forward followed by the inverse returns the original lat to
395 	within a small tolerance.  This tolerance is as follows:
396 
397 	geodetic <-> authalic:      max error (degrees) = 1.253344e-08
398 	geodetic <-> conformal:     max error (degrees) = 2.321796e-07  should be better after 13nov07 fix
399 	geodetic <-> meridional:    max error (degrees) = 4.490630e-12
400 	geodetic <-> geocentric:    max error (degrees) = 1.350031e-13
401 	geodetic <-> parametric:    max error (degrees) = 1.421085e-14
402 	geocentric <-> parametric:  max error (degrees) = 1.421085e-14
403 
404 	Currently, (GMT v5) the only ones we anticipate using are
405 	geodetic, authalic, and conformal.  I have put others in here
406 	for possible future convenience.
407 
408 	Also, I made this depend on GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid]
409 	rather than on GMT->current.proj, so that it will be possible to
410 	call gmt_lat_swap() without having to pass -R and -J to
411 	gmt_map_setup(), so that in the future we will be able to use
412 	lat conversions without plotting maps.
413 
414 	W H F Smith, 10--13 May 1999.   */
415 
416 	/* PW notes: Projections only convert latitudes if GMT->current.proj.GMT_convert_latitudes is true.
417 	 *       This is set by gmt_map_setup if the ellipsoid is not a sphere.  Calling gmtmap_lat_swap_init by itself
418 	 *	 does not affect the mapping machinery.  Since various situations call for the use
419 	 *	 of auxiliary latitudes we initialize gmtmap_lat_swap_init in gmt_begin.  This means
420 	 *	 programs can use functions like gmt_lat_swap whenever needed.
421 	 */
422 
423 	unsigned int i;
424 	double x, xx[4], a, f, e2, e4, e6, e8;
425 
426 	f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
427 	a = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius;
428 
429 	if (gmt_M_is_zero (f)) {
430 		gmt_M_memset (GMT->current.proj.lat_swap_vals.c, GMT_LATSWAP_N * 4, double);
431 		GMT->current.proj.lat_swap_vals.ra = GMT->current.proj.lat_swap_vals.rm = a;
432 		GMT->current.proj.lat_swap_vals.spherical = true;
433 		return;
434 	}
435 	GMT->current.proj.lat_swap_vals.spherical = false;
436 
437 	/* Below are two sums for x to get the two radii.  I have nested the
438 	parentheses to add the terms in the order that would minimize roundoff
439 	error.  However, in double precision there may be no need to do this.
440 	I have carried these to 4 terms (eccentricity to the 8th power) because
441 	this is as far as Adams goes with anything, but it is not clear what
442 	the truncation error is, since every term in the sum has the same sign.  */
443 
444 	e2 = f * (2.0 - f);
445 	e4 = e2 * e2;
446 	e6 = e4 * e2;
447 	e8 = e4 * e4;
448 
449 	/* This expression for the Authalic radius comes from Adams [1949]  */
450 	xx[0] = 2.0 / 3.0;
451 	xx[1] = 3.0 / 5.0;
452 	xx[2] = 4.0 / 7.0;
453 	xx[3] = 5.0 / 9.0;
454 	x = xx[0] * e2 + ( xx[1] * e4 + ( xx[2] * e6 + xx[3] * e8));
455 	GMT->current.proj.lat_swap_vals.ra = a * sqrt( (1.0 + x) * (1.0 - e2));
456 
457 	/* This expression for the Meridional radius comes from Gradshteyn and Ryzhik, 8.114.1,
458 	because Adams only gets the first two terms.  This can be worked out by expressing the
459 	meridian arc length in terms of an integral in parametric latitude, which reduces to
460 	equatorial radius times Elliptic Integral of the Second Kind.  Expanding this using
461 	binomial theorem leads to Gradshteyn and Ryzhik's expression:  */
462 	xx[0] = 1.0 / 4.0;
463 	xx[1] = xx[0] * 3.0 / 16.0;
464 	xx[2] = xx[1] * 3.0 * 5.0 / 36.0;
465 	xx[3] = xx[2] * 5.0 * 7.0 / 64.0;
466 	x = xx[0] * e2 + ( xx[1] * e4 + ( xx[2] * e6 + xx[3] * e8));
467 	GMT->current.proj.lat_swap_vals.rm = a * (1.0 - x);
468 
469 
470 	/* Geodetic to authalic:  */
471 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][0] = -(e2 / 3.0 + (31.0 * e4 / 180.0 + 59.0 * e6 / 560.0));
472 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][1] = 17.0 * e4 / 360.0 + 61.0 * e6 / 1260;
473 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][2] = -383.0 * e6 / 45360.0;
474 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][3] = 0.0;
475 
476 	/* Authalic to geodetic:  */
477 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][0] = e2 / 3.0 + (31.0 * e4 / 180.0 + 517.0 * e6 / 5040.0);
478 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][1] = 23.0 * e4 / 360.0 + 251.0 * e6 / 3780;
479 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][2] = 761.0 * e6 / 45360.0;
480 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][3] = 0.0;
481 
482 	/* Geodetic to conformal:  */
483 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][0] = -(e2 / 2.0 + (5.0 * e4 / 24.0 + (3.0 * e6 / 32.0 + 281.0 * e8 / 5760.0)));
484 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][1] = 5.0 * e4 / 48.0 + (7.0 * e6 / 80.0 + 697.0 * e8 / 11520.0);
485 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][2] = -(13.0 * e6 / 480.0 + 461.0 * e8 / 13440.0);
486 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][3] = 1237.0 * e8 / 161280.0;
487 
488 	/* Conformal to geodetic:  */
489 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][0] = e2 / 2.0 + (5.0 * e4 / 24.0 + (e6 / 12.0 + 13.0 * e8 / 360.0)) ;
490 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][1] = 7.0 * e4 / 48.0 + (29.0 * e6 / 240.0 + 811.0 * e8 / 11520.0);
491 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][2] = 7.0 * e6 / 120.0 + 81.0 * e8 / 1120.0;  /* Bug fixed 13nov07 whfs */
492 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][3] = 4279.0 * e8 / 161280.0;
493 
494 
495 	/* The meridional and parametric developments use this parameter:  */
496 	x = f/(2.0 - f);		/* Adams calls this n.  It is f/(2-f), or -betaJK in my notes.  */
497 	xx[0] = x;			/* n  */
498 	xx[1] = x * x;			/* n-squared  */
499 	xx[2] = xx[1] * x;		/* n-cubed  */
500 	xx[3] = xx[2] * x;		/* n to the 4th  */
501 
502 	/* Geodetic to meridional:  */
503 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][0] = -(3.0 * xx[0] / 2.0 - 9.0 * xx[2] / 16.0);
504 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][1] = 15.0 * xx[1] / 16.0 - 15.0 * xx[3] / 32.0;
505 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][2] = -35.0 * xx[2] / 48.0;
506 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][3] = 315.0 * xx[3] / 512.0;
507 
508 	/* Meridional to geodetic:  */
509 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][0] = 3.0 * xx[0] / 2.0 - 27.0 * xx[2] / 32.0;
510 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][1] = 21.0 * xx[1] / 16.0 - 55.0 * xx[3] / 32.0;
511 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][2] = 151.0 * xx[2] / 96.0;
512 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][3] = 1097.0 * xx[3] / 512.0;
513 
514 	/* Geodetic to parametric equals parametric to geocentric:  */
515 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][0] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][0] = -xx[0];
516 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][1] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][1] = xx[1] / 2.0;
517 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][2] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][2] = -xx[2] / 3.0;
518 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][3] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][3] = xx[3] / 4.0;
519 
520 	/* Parametric to geodetic equals geocentric to parametric:  */
521 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][0] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][0] = xx[0];
522 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][1] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][1] = xx[1] / 2.0;
523 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][2] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][2] = xx[2] / 3.0;
524 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][3] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][3] = xx[3] / 4.0;
525 
526 
527 	/* The geodetic <->geocentric use this parameter:  */
528 	x = 1.0 - e2;
529 	x = (1.0 - x)/(1.0 + x);	/* Adams calls this m.  It is e2/(2-e2), or -betaJK in my notes.  */
530 	xx[0] = x;			/* m  */
531 	xx[1] = x * x;			/* m-squared  */
532 	xx[2] = xx[1] * x;		/* m-cubed  */
533 	xx[3] = xx[2] * x;		/* m to the 4th  */
534 
535 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][0] = -xx[0];
536 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][1] = xx[1] / 2.0;
537 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][2] = -xx[2] / 3.0;
538 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][3] = xx[3] / 4.0;
539 
540 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][0] = xx[0];
541 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][1] = xx[1] / 2.0;
542 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][2] = xx[2] / 3.0;
543 	GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][3] = xx[3] / 4.0;
544 
545 
546 	/* Now do the Snyder Shuffle:  */
547 	for (i = 0; i < GMT_LATSWAP_N; i++) {
548 		GMT->current.proj.lat_swap_vals.c[i][0] = GMT->current.proj.lat_swap_vals.c[i][0] - GMT->current.proj.lat_swap_vals.c[i][2];
549 		GMT->current.proj.lat_swap_vals.c[i][1] = 2.0 * GMT->current.proj.lat_swap_vals.c[i][1] - 4.0 * GMT->current.proj.lat_swap_vals.c[i][3];
550 		GMT->current.proj.lat_swap_vals.c[i][2] *= 4.0;
551 		GMT->current.proj.lat_swap_vals.c[i][3] *= 8.0;
552 	}
553 }
554 
555 /* The *_outside routines return the status of the current point.
556  * Status is the sum of x_status and y_status.
557  *	x_status may be
558  *	0	w < lon < e
559  *	-1	lon == w
560  *	1	lon == e
561  *	-2	lon < w
562  *	2	lon > e
563  *	y_status may be
564  *	0	s < lat < n
565  *	-1	lat == s
566  *	1	lat == n
567  *	-2	lat < s
568  *	2	lat > n
569  */
570 
571 /*! . */
gmtmap_wesn_outside(struct GMT_CTRL * GMT,double lon,double lat)572 GMT_LOCAL bool gmtmap_wesn_outside (struct GMT_CTRL *GMT, double lon, double lat) {
573 	/* Determine if a point (lon,lat) is outside or on the rectangular lon/lat boundaries
574 	 * The check GMT->current.map.lon_wrap is include since we need to consider the 360
575 	 * degree periodicity of the longitude coordinate.
576 	 * When we are making basemaps and may want to ensure that a point is
577 	 * slightly outside the border without having it automatically flip by
578 	 * 360 degrees. In that case GMT->current.map.lon_wrap will be temporarily set to false.
579 	 */
580 
581 	if (GMT->current.map.lon_wrap) {
582 		while (lon < GMT->common.R.wesn[XLO] && (lon + GMT->current.map.lon_wrap_range) <= GMT->common.R.wesn[XHI]) lon += GMT->current.map.lon_wrap_range;
583 		while (lon > GMT->common.R.wesn[XHI] && (lon - GMT->current.map.lon_wrap_range) >= GMT->common.R.wesn[XLO]) lon -= GMT->current.map.lon_wrap_range;
584 	}
585 	else if (GMT->current.map.lat_wrap) {
586 		while (lat < GMT->common.R.wesn[YLO] && (lat + GMT->current.map.lon_wrap_range) <= GMT->common.R.wesn[YHI]) lat += GMT->current.map.lon_wrap_range;
587 		while (lat > GMT->common.R.wesn[YHI] && (lat - GMT->current.map.lon_wrap_range) >= GMT->common.R.wesn[YLO]) lat -= GMT->current.map.lon_wrap_range;
588 	}
589 
590 	/* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT.  Trying the latter */
591 	if (GMT->current.map.on_border_is_outside && fabs (lon - GMT->common.R.wesn[XLO]) < GMT_CONV8_LIMIT)
592 		GMT->current.map.this_x_status = -1;
593 	else if (GMT->current.map.on_border_is_outside && fabs (lon - GMT->common.R.wesn[XHI]) < GMT_CONV8_LIMIT)
594 		GMT->current.map.this_x_status = 1;
595 	else if (lon < GMT->common.R.wesn[XLO])
596 		GMT->current.map.this_x_status = -2;
597 	else if (lon > GMT->common.R.wesn[XHI])
598 		GMT->current.map.this_x_status = 2;
599 	else
600 		GMT->current.map.this_x_status = 0;
601 
602 	if (GMT->current.map.on_border_is_outside && fabs (lat - GMT->common.R.wesn[YLO]) < GMT_CONV8_LIMIT)
603 		GMT->current.map.this_y_status = -1;
604 	else if (GMT->current.map.on_border_is_outside && fabs (lat - GMT->common.R.wesn[YHI]) < GMT_CONV8_LIMIT)
605 		GMT->current.map.this_y_status = 1;
606 	else if (lat < GMT->common.R.wesn[YLO])
607 		GMT->current.map.this_y_status = -2;
608 	else if (lat > GMT->common.R.wesn[YHI])
609 		GMT->current.map.this_y_status = 2;
610 	else
611 		GMT->current.map.this_y_status = 0;
612 
613 	return (GMT->current.map.this_x_status != 0 || GMT->current.map.this_y_status != 0);
614 
615 }
616 
617 /*! . */
gmtmap_polar_outside(struct GMT_CTRL * GMT,double lon,double lat)618 GMT_LOCAL bool gmtmap_polar_outside (struct GMT_CTRL *GMT, double lon, double lat) {
619 	gmtmap_wesn_outside (GMT, lon, lat);
620 
621 	if (!GMT->current.proj.edge[1]) GMT->current.map.this_x_status = 0;	/* 360 degrees, no edge */
622 	if (GMT->current.map.this_y_status < 0 && !GMT->current.proj.edge[0]) GMT->current.map.this_y_status = 0;	/* South pole enclosed */
623 	if (GMT->current.map.this_y_status > 0 && !GMT->current.proj.edge[2]) GMT->current.map.this_y_status = 0;	/* North pole enclosed */
624 
625 	return (GMT->current.map.this_x_status != 0 || GMT->current.map.this_y_status != 0);
626 }
627 
628 /*! . */
gmtmap_radial_outside(struct GMT_CTRL * GMT,double lon,double lat)629 GMT_LOCAL bool gmtmap_radial_outside (struct GMT_CTRL *GMT, double lon, double lat) {
630 	double dist;
631 
632 	/* Test if point is more than horizon spherical degrees from origin.  For global maps, let all borders be "south" */
633 
634 	/* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT.  Trying the latter */
635 	GMT->current.map.this_x_status = 0;
636 	dist = gmtlib_great_circle_dist_degree (GMT, lon, lat, GMT->current.proj.central_meridian, GMT->current.proj.pole);
637 	if (GMT->current.map.on_border_is_outside && fabs (dist - GMT->current.proj.f_horizon) < GMT_CONV8_LIMIT)
638 		GMT->current.map.this_y_status = -1;
639 	else if (dist > GMT->current.proj.f_horizon)
640 		GMT->current.map.this_y_status = -2;
641 	else
642 		GMT->current.map.this_y_status = 0;
643 	return (GMT->current.map.this_y_status != 0);
644 }
645 
646 /*! . */
gmtmap_rect_outside(struct GMT_CTRL * GMT,double lon,double lat)647 GMT_LOCAL bool gmtmap_rect_outside (struct GMT_CTRL *GMT, double lon, double lat) {
648 	double x, y;
649 
650 	gmt_geo_to_xy (GMT, lon, lat, &x, &y);
651 
652 	return (gmt_cart_outside (GMT, x, y));
653 }
654 
655 /*! . */
gmtmap_rect_outside2(struct GMT_CTRL * GMT,double lon,double lat)656 GMT_LOCAL bool gmtmap_rect_outside2 (struct GMT_CTRL *GMT, double lon, double lat) {
657 	/* For Azimuthal proj with rect borders since gmtmap_rect_outside may fail for antipodal points */
658 	if (gmtmap_radial_outside (GMT, lon, lat)) return (true);	/* Point > 90 degrees away */
659 	return (gmtmap_rect_outside (GMT, lon, lat));	/* Must check if inside box */
660 }
661 
662 /*! . */
gmtmap_x_wesn_corner(struct GMT_CTRL * GMT,double * x)663 GMT_LOCAL void gmtmap_x_wesn_corner (struct GMT_CTRL *GMT, double *x) {
664 /*	if (fabs (fmod (fabs (*x - GMT->common.R.wesn[XLO]), 360.0)) <= GMT_CONV4_LIMIT)
665 		*x = GMT->common.R.wesn[XLO];
666 	else if (fabs (fmod (fabs (*x - GMT->common.R.wesn[XHI]), 360.0)) <= GMT_CONV4_LIMIT)
667 		*x = GMT->common.R.wesn[XHI]; */
668 
669 	/* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT.  Trying the latter */
670 	if (fabs (*x - GMT->common.R.wesn[XLO]) <= GMT_CONV8_LIMIT)
671 		*x = GMT->common.R.wesn[XLO];
672 	else if (fabs (*x - GMT->common.R.wesn[XHI]) <= GMT_CONV8_LIMIT)
673 		*x = GMT->common.R.wesn[XHI];
674 }
675 
676 /*! . */
gmtmap_y_wesn_corner(struct GMT_CTRL * GMT,double * y)677 GMT_LOCAL void gmtmap_y_wesn_corner (struct GMT_CTRL *GMT, double *y) {
678 	/* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT.  Trying the latter */
679 	if (fabs (*y - GMT->common.R.wesn[YLO]) <= GMT_CONV8_LIMIT)
680 		*y = GMT->common.R.wesn[YLO];
681 	else if (fabs (*y - GMT->common.R.wesn[YHI]) <= GMT_CONV8_LIMIT)
682 		*y = GMT->common.R.wesn[YHI];
683 }
684 
685 /*! . */
gmtmap_is_wesn_corner(struct GMT_CTRL * GMT,double x,double y)686 GMT_LOCAL bool gmtmap_is_wesn_corner (struct GMT_CTRL *GMT, double x, double y) {
687 	/* Checks if point is a corner */
688 	GMT->current.map.corner = 0;
689 
690 	if (doubleAlmostEqualZero (fmod(fabs(x), 360.0), fmod(fabs(GMT->common.R.wesn[XLO]), 360.0))) {
691 		if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YLO]))
692 			GMT->current.map.corner = 1;
693 		else if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YHI]))
694 			GMT->current.map.corner = 4;
695 	}
696 	else if (doubleAlmostEqualZero (fmod(fabs(x), 360.0), fmod(fabs(GMT->common.R.wesn[XHI]), 360.0))) {
697 		if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YLO]))
698 			GMT->current.map.corner = 2;
699 		else if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YHI]))
700 			GMT->current.map.corner = 3;
701 	}
702 	return (GMT->current.map.corner > 0);
703 }
704 
705 /*! . */
gmtmap_lon_inside(struct GMT_CTRL * GMT,double lon,double w,double e)706 GMT_LOCAL bool gmtmap_lon_inside (struct GMT_CTRL *GMT, double lon, double w, double e) {
707 	while (lon < GMT->common.R.wesn[XLO]) lon += 360.0;
708 	while (lon > GMT->common.R.wesn[XHI]) lon -= 360.0;
709 
710 	if (lon < w) return (false);
711 	if (lon > e) return (false);
712 	return (true);
713 }
714 
715 /*! . */
gmtmap_wesn_crossing(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)716 GMT_LOCAL unsigned int gmtmap_wesn_crossing (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
717 	/* Compute all crossover points of a line segment with the rectangular lat/lon boundaries
718 	 * Since it may not be obvious which side the line may cross, and since in some cases the two points may be
719 	 * entirely outside the region but still cut through it, we first find all possible candidates and then decide
720 	 * which ones are valid crossings.  We may find 0, 1, or 2 intersections */
721 
722 	unsigned int n = 0, i;
723 	double d, x0, y0;
724 
725 	/* If wrapping is allowed: first bring both points between W and E boundaries,
726 	 * then move the western-most point east if it is further than 180 degrees away.
727 	 * This may cause the points to span the eastern boundary */
728 
729 	if (GMT->current.map.lon_wrap) {
730 		while (lon0 < GMT->common.R.wesn[XLO]) lon0 += GMT->current.map.lon_wrap_range;
731 		while (lon0 > GMT->common.R.wesn[XHI]) lon0 -= GMT->current.map.lon_wrap_range;
732 		while (lon1 < GMT->common.R.wesn[XLO]) lon1 += GMT->current.map.lon_wrap_range;
733 		while (lon1 > GMT->common.R.wesn[XHI]) lon1 -= GMT->current.map.lon_wrap_range;
734 		if (fabs (lon0 - lon1) <= 0.5 * GMT->current.map.lon_wrap_range) { /* Nothing */ }
735 		else if (lon0 < lon1)
736 			lon0 += GMT->current.map.lon_wrap_range;
737 		else
738 			lon1 += GMT->current.map.lon_wrap_range;
739 	}
740 	else if (GMT->current.map.lat_wrap) {
741 		while (lat0 < GMT->common.R.wesn[YLO]) lat0 += GMT->current.map.lat_wrap_range;
742 		while (lat0 > GMT->common.R.wesn[YHI]) lat0 -= GMT->current.map.lat_wrap_range;
743 		while (lat1 < GMT->common.R.wesn[YLO]) lat1 += GMT->current.map.lat_wrap_range;
744 		while (lat1 > GMT->common.R.wesn[YHI]) lat1 -= GMT->current.map.lat_wrap_range;
745 		if (fabs (lat0 - lat1) <= 0.5 * GMT->current.map.lat_wrap_range) { /* Nothing */ }
746 		else if (lat0 < lat1)
747 			lat0 += GMT->current.map.lat_wrap_range;
748 		else
749 			lat1 += GMT->current.map.lat_wrap_range;
750 	}
751 
752 	/* Then set 'almost'-corners to corners */
753 	gmtmap_x_wesn_corner (GMT, &lon0);
754 	gmtmap_x_wesn_corner (GMT, &lon1);
755 	gmtmap_y_wesn_corner (GMT, &lat0);
756 	gmtmap_y_wesn_corner (GMT, &lat1);
757 
758 	/* Crossing South */
759 	if ((lat0 >= GMT->common.R.wesn[YLO] && lat1 <= GMT->common.R.wesn[YLO]) || (lat1 >= GMT->common.R.wesn[YLO] && lat0 <= GMT->common.R.wesn[YLO])) {
760 		sides[n] = 0;
761 		clat[n] = GMT->common.R.wesn[YLO];
762 		d = lat0 - lat1;
763 		clon[n] = (doubleAlmostEqualZero (lat0, lat1)) ? lon1 : lon1 + (lon0 - lon1) * (clat[n] - lat1) / d;
764 		gmtmap_x_wesn_corner (GMT, &clon[n]);
765 		if (fabs (d) > 0.0 && gmtmap_lon_inside (GMT, clon[n], GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) n++;
766 	}
767 	/* Crossing East */
768 	if ((lon0 >= GMT->common.R.wesn[XHI] && lon1 <= GMT->common.R.wesn[XHI]) || (lon1 >= GMT->common.R.wesn[XHI] && lon0 <= GMT->common.R.wesn[XHI])) {
769 		sides[n] = 1;
770 		clon[n] = GMT->common.R.wesn[XHI];
771 		d = lon0 - lon1;
772 		clat[n] = (doubleAlmostEqualZero (lon0, lon1)) ? lat1 : lat1 + (lat0 - lat1) * (clon[n] - lon1) / d;
773 		gmtmap_y_wesn_corner (GMT, &clat[n]);
774 		if (fabs (d) > 0.0 && clat[n] >= GMT->common.R.wesn[YLO] && clat[n] <= GMT->common.R.wesn[YHI]) n++;
775 	}
776 
777 	/* Now adjust the longitudes so that they might span the western boundary */
778 	if (GMT->current.map.lon_wrap && MAX(lon0, lon1) > GMT->common.R.wesn[XHI]) {
779 		lon0 -= GMT->current.map.lon_wrap_range; lon1 -= GMT->current.map.lon_wrap_range;
780 	}
781 	else if (GMT->current.map.lat_wrap && MAX(lat0, lat1) > GMT->common.R.wesn[YHI]) {
782 		lat0 -= GMT->current.map.lat_wrap_range; lat1 -= GMT->current.map.lat_wrap_range;
783 	}
784 
785 	/* Crossing North */
786 	if ((lat0 >= GMT->common.R.wesn[YHI] && lat1 <= GMT->common.R.wesn[YHI]) || (lat1 >= GMT->common.R.wesn[YHI] && lat0 <= GMT->common.R.wesn[YHI])) {
787 		sides[n] = 2;
788 		clat[n] = GMT->common.R.wesn[YHI];
789 		d = lat0 - lat1;
790 		clon[n] = (doubleAlmostEqualZero (lat0, lat1)) ? lon1 : lon1 + (lon0 - lon1) * (clat[n] - lat1) / d;
791 		gmtmap_x_wesn_corner (GMT, &clon[n]);
792 		if (fabs (d) > 0.0 && gmtmap_lon_inside (GMT, clon[n], GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) n++;
793 	}
794 	/* Crossing West */
795 	if ((lon0 <= GMT->common.R.wesn[XLO] && lon1 >= GMT->common.R.wesn[XLO]) || (lon1 <= GMT->common.R.wesn[XLO] && lon0 >= GMT->common.R.wesn[XLO])) {
796 		sides[n] = 3;
797 		clon[n] = GMT->common.R.wesn[XLO];
798 		d = lon0 - lon1;
799 		clat[n] = (doubleAlmostEqualZero (lon0, lon1)) ? lat1 : lat1 + (lat0 - lat1) * (clon[n] - lon1) / d;
800 		gmtmap_y_wesn_corner (GMT, &clat[n]);
801 		if (fabs (d) > 0.0 && clat[n] >= GMT->common.R.wesn[YLO] && clat[n] <= GMT->common.R.wesn[YHI]) n++;
802 	}
803 
804 	if (n == 0) return (0);
805 
806 	for (i = 0; i < n; i++) {
807 		gmt_geo_to_xy (GMT, clon[i], clat[i], &xx[i], &yy[i]);
808 		if (GMT->current.proj.projection_GMT == GMT_POLAR && sides[i]%2) sides[i] = 4 - sides[i];	/*  toggle 1 <-> 3 */
809 	}
810 
811 	if (n == 1) return (1);
812 
813 	/* Check for corner xover if n == 2 */
814 
815 	if (gmtmap_is_wesn_corner (GMT, clon[0], clat[0])) return (1);
816 
817 	if (gmtmap_is_wesn_corner (GMT, clon[1], clat[1])) {
818 		clon[0] = clon[1];
819 		clat[0] = clat[1];
820 		xx[0] = xx[1];
821 		yy[0] = yy[1];
822 		sides[0] = sides[1];
823 		return (1);
824 	}
825 
826 	/* Sort the two intermediate points into the right order based on projected distances from the first point */
827 
828 	gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
829 
830 	if (hypot (x0 - xx[1], y0 - yy[1]) < hypot (x0 - xx[0], y0 - yy[0])) {
831 		gmt_M_double_swap (clon[0], clon[1]);
832 		gmt_M_double_swap (clat[0], clat[1]);
833 		gmt_M_double_swap (xx[0], xx[1]);
834 		gmt_M_double_swap (yy[0], yy[1]);
835 		gmt_M_uint_swap (sides[0], sides[1]);
836 	}
837 
838 	return (2);
839 }
840 
841 #if 0
842 GMT_LOCAL void gmtmap_x_rect_corner (struct GMT_CTRL *GMT, double *x) {
843 	if (fabs (*x) <= GMT_CONV4_LIMIT)
844 		*x = 0.0;
845 	else if (fabs (*x - GMT->current.proj.rect[XHI]) <= GMT_CONV4_LIMIT)
846 		*x = GMT->current.proj.rect[XHI];
847 }
848 
849 GMT_LOCAL void gmtmap_y_rect_corner (struct GMT_CTRL *GMT, double *y) {
850 	if (fabs (*y) <= GMT_CONV4_LIMIT)
851 		*y = 0.0;
852 	else if (fabs (*y - GMT->current.proj.rect[YHI]) <= GMT_CONV4_LIMIT)
853 		*y = GMT->current.proj.rect[YHI];
854 }
855 #endif
856 
857 /*! . */
gmtmap_x_rect_corner(struct GMT_CTRL * GMT,double * x)858 GMT_LOCAL void gmtmap_x_rect_corner (struct GMT_CTRL *GMT, double *x) {
859 	if (fabs (*x) <= GMT_CONV8_LIMIT)
860 		*x = 0.0;
861 	else if (fabs (*x - GMT->current.proj.rect[XHI]) <= GMT_CONV8_LIMIT)
862 		*x = GMT->current.proj.rect[XHI];
863 }
864 
865 /*! . */
gmtmap_y_rect_corner(struct GMT_CTRL * GMT,double * y)866 GMT_LOCAL void gmtmap_y_rect_corner (struct GMT_CTRL *GMT, double *y) {
867 	if (fabs (*y) <= GMT_CONV8_LIMIT)
868 		*y = 0.0;
869 	else if (fabs (*y - GMT->current.proj.rect[YHI]) <= GMT_CONV8_LIMIT)
870 		*y = GMT->current.proj.rect[YHI];
871 }
872 
873 /*! . */
gmtmap_is_rect_corner(struct GMT_CTRL * GMT,double x,double y)874 GMT_LOCAL bool gmtmap_is_rect_corner (struct GMT_CTRL *GMT, double x, double y) {
875 	/* Checks if point is a corner */
876 	GMT->current.map.corner = -1;
877 	if (doubleAlmostEqualZero (x, GMT->current.proj.rect[XLO])) {
878 		if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YLO]))
879 			GMT->current.map.corner = 1;
880 		else if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YHI]))
881 			GMT->current.map.corner = 4;
882 	}
883 	else if (doubleAlmostEqualZero (x, GMT->current.proj.rect[XHI])) {
884 		if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YLO]))
885 			GMT->current.map.corner = 2;
886 		else if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YHI]))
887 			GMT->current.map.corner = 3;
888 	}
889 	return (GMT->current.map.corner > 0);
890 }
891 
892 /*! . */
gmtmap_rect_crossing(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)893 GMT_LOCAL unsigned int gmtmap_rect_crossing (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
894 	/* Compute all crossover points of a line segment with the boundaries in a rectangular projection */
895 
896 	unsigned int i, j, n = 0;
897 	double x0, x1, y0, y1, d;
898 
899 	/* Since it may not be obvious which side the line may cross, and since in some cases the two points may be
900 	 * entirely outside the region but still cut through it, we first find all possible candidates and then decide
901 	 * which ones are valid crossings.  We may find 0, 1, or 2 intersections */
902 
903 	gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
904 	gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
905 
906 	/* First set 'almost'-corners to corners */
907 
908 	gmtmap_x_rect_corner (GMT, &x0);
909 	gmtmap_x_rect_corner (GMT, &x1);
910 	gmtmap_y_rect_corner (GMT, &y0);
911 	gmtmap_y_rect_corner (GMT, &y1);
912 
913 	if ((y0 >= GMT->current.proj.rect[YLO] && y1 <= GMT->current.proj.rect[YLO]) || (y1 >= GMT->current.proj.rect[YLO] && y0 <= GMT->current.proj.rect[YLO])) {
914 		sides[n] = 0;
915 		yy[n] = GMT->current.proj.rect[YLO];
916 		d = y0 - y1;
917 		xx[n] = (doubleAlmostEqualZero (y0, y1)) ? x0 : x1 + (x0 - x1) * (yy[n] - y1) / d;
918 		gmtmap_x_rect_corner (GMT, &xx[n]);
919 		if (fabs (d) > 0.0 && xx[n] >= GMT->current.proj.rect[XLO] && xx[n] <= GMT->current.proj.rect[XHI]) n++;
920 	}
921 	if ((x0 <= GMT->current.proj.rect[XHI] && x1 >= GMT->current.proj.rect[XHI]) || (x1 <= GMT->current.proj.rect[XHI] && x0 >= GMT->current.proj.rect[XHI])) {
922 		sides[n] = 1;
923 		xx[n] = GMT->current.proj.rect[XHI];
924 		d = x0 - x1;
925 		yy[n] = (doubleAlmostEqualZero (x0, x1)) ? y0 : y1 + (y0 - y1) * (xx[n] - x1) / d;
926 		gmtmap_y_rect_corner (GMT, &yy[n]);
927 		if (fabs (d) > 0.0 && yy[n] >= GMT->current.proj.rect[YLO] && yy[n] <= GMT->current.proj.rect[YHI]) n++;
928 	}
929 	if ((y0 <= GMT->current.proj.rect[YHI] && y1 >= GMT->current.proj.rect[YHI]) || (y1 <= GMT->current.proj.rect[YHI] && y0 >= GMT->current.proj.rect[YHI])) {
930 		sides[n] = 2;
931 		yy[n] = GMT->current.proj.rect[YHI];
932 		d = y0 - y1;
933 		xx[n] = (doubleAlmostEqualZero (y0, y1)) ? x0 : x1 + (x0 - x1) * (yy[n] - y1) / d;
934 		gmtmap_x_rect_corner (GMT, &xx[n]);
935 		if (fabs (d) > 0.0 && xx[n] >= GMT->current.proj.rect[XLO] && xx[n] <= GMT->current.proj.rect[XHI]) n++;
936 	}
937 	if ((x0 >= GMT->current.proj.rect[XLO] && x1 <= GMT->current.proj.rect[XLO]) || (x1 >= GMT->current.proj.rect[XLO] && x0 <= GMT->current.proj.rect[XLO])) {
938 		sides[n] = 3;
939 		xx[n] = GMT->current.proj.rect[XLO];
940 		d = x0 - x1;
941 		yy[n] = (doubleAlmostEqualZero (x0, x1)) ? y0 : y1 + (y0 - y1) * (xx[n] - x1) / d;
942 		gmtmap_y_rect_corner (GMT, &yy[n]);
943 		if (fabs (d) > 0.0 && yy[n] >= GMT->current.proj.rect[YLO] && yy[n] <= GMT->current.proj.rect[YHI]) n++;
944 	}
945 
946 	if (n == 0) return (0);
947 
948 	/* Eliminate duplicates */
949 
950 	for (i = 0; i < n; ++i) {
951 		for (j = i + 1; j < n; ++j) {
952 			if (doubleAlmostEqualZero (xx[i], xx[j]) && doubleAlmostEqualZero (yy[i], yy[j]))	/* Duplicate */
953 				sides[j] = 99;	/* Mark as duplicate */
954 		}
955 	}
956 	for (i = 1; i < n; i++) {
957 		if (sides[i] == 99) {	/* This is a duplicate, overwrite */
958 			for (j = i + 1; j < n; j++) {
959 				xx[j-1] = xx[j];
960 				yy[j-1] = yy[j];
961 				sides[j-1] = sides[j];
962 			}
963 			n--;
964 			i--;	/* Must start at same point again */
965 		}
966 	}
967 
968 	for (i = 0; i < n; i++)	gmt_xy_to_geo (GMT, &clon[i], &clat[i], xx[i], yy[i]);
969 
970 	if (gmt_M_is_cartesian (GMT, GMT_IN)) return (n);
971 
972 	if (n < 2) return (n);
973 
974 	/* Check for corner xover if n == 2 */
975 
976 	if (gmtmap_is_rect_corner (GMT, xx[0], yy[0])) return (1);
977 
978 	if (gmtmap_is_rect_corner (GMT, xx[1], yy[1])) {
979 		clon[0] = clon[1];
980 		clat[0] = clat[1];
981 		xx[0] = xx[1];
982 		yy[0] = yy[1];
983 		sides[0] = sides[1];
984 		return (1);
985 	}
986 
987 	/* Sort the two intermediate points into the right order based on projected distances from the first point */
988 
989 	if (hypot (x0 - xx[1], y0 - yy[1]) < hypot (x0 - xx[0], y0 - yy[0])) {
990 		gmt_M_double_swap (clon[0], clon[1]);
991 		gmt_M_double_swap (clat[0], clat[1]);
992 		gmt_M_double_swap (xx[0], xx[1]);
993 		gmt_M_double_swap (yy[0], yy[1]);
994 		gmt_M_uint_swap (sides[0], sides[1]);
995 	}
996 
997 	return (2);
998 }
999 
1000 /*! . */
gmtmap_radial_crossing(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)1001 GMT_LOCAL unsigned int gmtmap_radial_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
1002 	/* Computes the lon/lat of a point that is f_horizon spherical degrees from
1003 	 * the origin and lies on the great circle between points 1 and 2 */
1004 
1005 	double dist1, dist2, delta, eps, dlon;
1006 
1007 	dist1 = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon1, lat1);
1008 	dist2 = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon2, lat2);
1009 	delta = dist2 - dist1;
1010 	eps = (doubleAlmostEqualZero (dist1, dist2)) ? 0.0 : (GMT->current.proj.f_horizon - dist1) / delta;
1011 	gmt_M_set_delta_lon (lon1, lon2, dlon);
1012 	clon[0] = lon1 + dlon * eps;
1013 	clat[0] = lat1 + (lat2 - lat1) * eps;
1014 
1015 	gmt_geo_to_xy (GMT, clon[0], clat[0], &xx[0], &yy[0]);
1016 
1017 	sides[0] = 1;
1018 
1019 	return (1);
1020 }
1021 
1022 /*! . */
gmtmap_genper_crossing(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)1023 GMT_LOCAL unsigned int gmtmap_genper_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
1024 	/* Awkward case of windowing which gives a boundary that is a clipped circle.
1025 	 * Here, the rectangular and circular boundaries take turns in being the definitive boundary.
1026 	 * Approach: Determine how the points do relative to either boundary. */
1027 
1028 	bool h_out[2], r_out[2];
1029 
1030 	/* Check if point 1 is beyond horizon: */
1031 	h_out[0] = gmtmap_radial_outside (GMT, lon1, lat1);	/* true if point 1 is beyond the horizon */
1032 	h_out[1] = gmtmap_radial_outside (GMT, lon2, lat2);	/* true if point 2 is beyond the horizon */
1033 	r_out[0] = gmtmap_rect_outside (GMT, lon1, lat1);		/* true if point 1 is beyond the map box */
1034 	r_out[1] = gmtmap_rect_outside (GMT, lon2, lat2);		/* true if point 2 is beyond the map box */
1035 	if (h_out[0] == false && h_out[1] == false)	/* We are not beyond horizon so technically inside unless clipped by the window: */
1036 		return (gmtmap_rect_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1037 	if (r_out[0] == false && r_out[1] == false)	/* We are not outside the map box but might be beyond horizon: */
1038 		return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1039 	/* Here we have a mixed bag.  Try our best */
1040 	if (h_out[0] == false && r_out[0] == true)	/* Point 1 is outside box but inside horizon.  That means point 2 is inside and we use rect_crossing */
1041 		return (gmtmap_rect_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1042 	if (h_out[0] == true && r_out[0] == false)	/* Point 1 is beyond horizon but inside box.  That means point 2 is inside and we use gmtmap_radial_crossing */
1043 		return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1044 	if (h_out[1] == false && r_out[1] == true)	/* Point 2 is outside box but inside horizon.  That means point 1 is inside and we use rect_crossing */
1045 		return (gmtmap_rect_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1046 	if (h_out[1] == true && r_out[1] == false)	/* Point 2 is beyond horizon but inside box.  That means point 1 is inside and we use gmtmap_radial_crossing */
1047 		return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1048 	/* Don't think we should get here... */
1049 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in gmtmap_genper_crossing: None of the cases matched crossing scenario");
1050 	return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1051 
1052 }
1053 
gmtmap_jump_x(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)1054 GMT_LOCAL int gmtmap_jump_x (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
1055 	/* true if x-distance between points exceeds 1/2 map width at this y value */
1056 	double dx, map_half_size, half_lon_range;
1057 
1058 	if (!(gmt_M_is_cylindrical (GMT) || gmt_M_is_perspective (GMT) || gmt_M_is_misc (GMT))) return (0);	/* Only projections with periodic boundaries may apply */
1059 
1060 	if (gmt_M_is_cartesian (GMT, GMT_IN) || fabs (GMT->common.R.wesn[XLO] - GMT->common.R.wesn[XHI]) < 90.0) return (0);
1061 
1062 	map_half_size = MAX (gmt_half_map_width (GMT, y0), gmt_half_map_width (GMT, y1));
1063 	if (fabs (map_half_size) < GMT_CONV4_LIMIT) return (0);
1064 
1065 	half_lon_range = (GMT->common.R.oblique) ? 180.0 : 0.5 * (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]);
1066 	dx = x1 - x0;
1067 	if (fabs (dx) > map_half_size) {	/* Possible jump; let's see how far apart those longitudes really are */
1068 		/* This test on longitudes was added to deal with issue #672, also see test/psxy/nojump.sh */
1069 		double last_lon = 0.0, this_lon = 0.0, dummy = 0.0, dlon;
1070 		gmt_xy_to_geo (GMT, &last_lon, &dummy, x0, y0);
1071 		gmt_xy_to_geo (GMT, &this_lon, &dummy, x1, y1);
1072 		gmt_M_set_delta_lon (last_lon, this_lon, dlon);	/* Beware of jumps due to sign differences */
1073 		if (fabs (dlon) < half_lon_range) /* Not going the long way so we judge this to be no jump */
1074 			return (0);
1075 		/* Jump it is */
1076 		if (dx > map_half_size)	return (-1);	/* Cross left/west boundary */
1077 		if (dx < (-map_half_size)) return (1);	/* Cross right/east boundary */
1078 	}
1079 	return (0);
1080 }
1081 
gmtmap_jump_xy(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)1082 GMT_LOCAL int gmtmap_jump_xy (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
1083 	/* true if x- or y-distance between points exceeds 1/2 map width at this y value or map height */
1084 	double dx, dy, map_half_width, map_half_height;
1085 
1086 	if (!(gmt_M_is_cylindrical (GMT) || gmt_M_is_perspective (GMT) || gmt_M_is_misc (GMT))) return (0);	/* Only projections with periodic boundaries may apply */
1087 
1088 	if (gmt_M_is_cartesian (GMT, GMT_IN) || fabs (GMT->common.R.wesn[XLO] - GMT->common.R.wesn[XHI]) < 90.0) return (0);
1089 
1090 	map_half_width = MAX (gmt_half_map_width (GMT, y0), gmt_half_map_width (GMT, y1));
1091 	if (fabs (map_half_width) < GMT_CONV4_LIMIT) return (0);	/* Very close to a pole for some projections */
1092 	map_half_height = 0.5 * GMT->current.map.height;	/* We assume this is constant */
1093 
1094 	dx = x1 - x0;
1095 	if (fabs (dx) > map_half_width) {	/* Possible jump; let's see how far apart those longitudes really are */
1096 		/* This test on longitudes was added to deal with issue #672, also see test/psxy/nojump.sh */
1097 		double last_lon = 0.0, this_lon = 0.0, dummy = 0.0, dlon;
1098 		double half_lon_range = (GMT->common.R.oblique) ? 180.0 : 0.5 * (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]);
1099 		gmt_xy_to_geo (GMT, &last_lon, &dummy, x0, y0);
1100 		gmt_xy_to_geo (GMT, &this_lon, &dummy, x1, y1);
1101 		gmt_M_set_delta_lon (last_lon, this_lon, dlon);	/* Beware of jumps due to sign differences */
1102 		if (fabs (dlon) < half_lon_range) /* Not going the long way so we judge this to be no jump */
1103 			return (0);
1104 		/* Jump it is */
1105 		if (dx > map_half_width)	return (-1);	/* Cross left/west boundary */
1106 		if (dx < (-map_half_width)) return (1);	/* Cross right/east boundary */
1107 	}
1108 	else if (fabs ((dy = y1 - y0)) > map_half_height) {	/* Possible jump for TM or UTM */
1109 		/* Jump it is */
1110 		if (dy > map_half_height)	return (-2);	/* Cross bottom/south boundary */
1111 		if (dy < (-map_half_height)) return (2);	/* Cross top/north boundary */
1112 	}
1113 	return (0);	/* No jump */
1114 }
1115 
gmtmap_jump_not(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)1116 GMT_LOCAL int gmtmap_jump_not (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
1117 #if 0
1118 	double dx, map_half_size;
1119 	/* For Genper we believe there should not be jumps [Introduced to test issue #667] */
1120 	/* However, it does not work for all cases.  Consider this one
1121 	 * psbasemap -Rg -JG280.969616634/58.7193421224/2513/0/0/0/200/180/6i -Ba30f30g30/a5f5g5NWSE -P
1122 	 * which draws jump lines across.  I believe this is related to the fact that in this ase
1123 	 * the map region is a clipped circle and we have no effective way of determining which points
1124 	 * are outside the map of not.
1125 	 */
1126 	map_half_size = MAX (gmt_half_map_width (GMT, y0), gmt_half_map_width (GMT, y1));
1127 	if (fabs (map_half_size) < GMT_CONV4_LIMIT) return (0);
1128 	dx = x1 - x0;
1129 	if (dx > map_half_size)	return (-1);	/* Cross left/west boundary */
1130 	if (dx < (-map_half_size)) return (1);	/* Cross right/east boundary */
1131 #else
1132 	gmt_M_unused(GMT);
1133 	gmt_M_unused(x0);
1134 	gmt_M_unused(y0);
1135 	gmt_M_unused(x1);
1136 	gmt_M_unused(y1);
1137 #endif
1138 	return (0);
1139 }
1140 
1141 #if 0
1142 /* NOT USED ?? */
1143 GMT_LOCAL int gmtmap_ellipse_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, int *sides) {
1144 	/* Compute the crossover point(s) on the map boundary for rectangular projections */
1145 	int n = 0, i, jump;
1146 	double x1, x2, y1, y2;
1147 
1148 	/* Crossings here must be at the W or E borders. Lat points may only touch border */
1149 
1150 	if (lat1 <= -90.0) {
1151 		sides[n] = 0;
1152 		clon[n] = lon1;
1153 		clat[n] = lat1;
1154 		n = 1;
1155 	}
1156 	else if (lat2 <= -90.0) {
1157 		sides[n] = 0;
1158 		clon[n] = lon2;
1159 		clat[n] = lat2;
1160 		n = 1;
1161 	}
1162 	else if (lat1 >= 90.0) {
1163 		sides[n] = 2;
1164 		clon[n] = lon1;
1165 		clat[n] = lat1;
1166 		n = 1;
1167 	}
1168 	else if (lat2 >= 90.0) {
1169 		sides[n] = 2;
1170 		clon[n] = lon2;
1171 		clat[n] = lat2;
1172 		n = 1;
1173 	}
1174 	else {	/* May cross somewhere else */
1175 		gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
1176 		gmt_geo_to_xy (GMT, lon2, lat2, &x2, &y2);
1177 		if ((jump = gmtmap_jump_x (GMT, x2, y2, x1, y1))) {
1178 			(*GMT->current.map.get_crossings) (GMT, xx, yy, x2, y2, x1, y1);
1179 			if (jump == 1) {	/* Add right border point first */
1180 				gmt_M_double_swap (xx[0], xx[1]);
1181 				gmt_M_double_swap (yy[0], yy[1]);
1182 			}
1183 			gmt_xy_to_geo (GMT, &clon[0], &clat[0], xx[0], yy[0]);
1184 			gmt_xy_to_geo (GMT, &clon[1], &clat[1], xx[1], yy[1]);
1185 			n = 2;
1186 		}
1187 	}
1188 	if (n == 1) for (i = 0; i < n; i++) gmt_geo_to_xy (GMT, clon[i], clat[i], &xx[i], &yy[i]);
1189 	return (n);
1190 }
1191 
1192 /* NOT USED ?? */
1193 GMT_LOCAL bool gmtmap_eqdist_outside (struct GMT_CTRL *GMT, double lon, double lat) {
1194 	double cc, s, c;
1195 
1196 	lon -= GMT->current.proj.central_meridian;
1197 	while (lon < -180.0) lon += 360.0;
1198 	while (lon > 180.0) lon -= 360.0;
1199 	sincosd (lat, &s, &c);
1200 	cc = GMT->current.proj.sinp * s + GMT->current.proj.cosp * c * cosd (lon);
1201 	if (cc < -1.0) {
1202 		GMT->current.map.this_y_status = -1;
1203 		GMT->current.map.this_x_status = 0;
1204 	}
1205 	else
1206 		GMT->current.map.this_x_status = GMT->current.map.this_y_status = 0;
1207 	return (GMT->current.map.this_y_status != 0);
1208 }
1209 
1210 GMT_LOCAL int gmtmap_eqdist_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, int *sides) {
1211 	double angle, x, y, s, c;
1212 
1213 	/* Computes the x.y of the antipole point that lies on a radius from
1214 	 * the origin through the inside point */
1215 
1216 	if (gmtmap_eqdist_outside (GMT, lon1, lat1)) {	/* Point 1 is on perimeter */
1217 		gmt_geo_to_xy (GMT, lon2, lat2, &x, &y);
1218 		angle = d_atan2 (y - GMT->current.proj.origin[GMT_Y], x - GMT->current.proj.origin[GMT_X]);
1219 		sincos (angle, &s, &c);
1220 		xx[0] = GMT->current.proj.r * c + GMT->current.proj.origin[GMT_X];
1221 		yy[0] = GMT->current.proj.r * s + GMT->current.proj.origin[GMT_Y];
1222 		clon[0] = lon1;
1223 		clat[0] = lat1;
1224 	}
1225 	else {	/* Point 2 is on perimeter */
1226 		gmt_geo_to_xy (GMT, lon1, lat1, &x, &y);
1227 		angle = d_atan2 (y - GMT->current.proj.origin[GMT_Y], x - GMT->current.proj.origin[GMT_X]);
1228 		sincos (angle, &s, &c);
1229 		xx[0] = GMT->current.proj.r * c + GMT->current.proj.origin[GMT_X];
1230 		yy[0] = GMT->current.proj.r * s + GMT->current.proj.origin[GMT_Y];
1231 		clon[0] = lon2;
1232 		clat[0] = lat2;
1233 	}
1234 	sides[0] = 1;
1235 
1236 	return (1);
1237 }
1238 #endif
1239 
1240 /*  Routines to do with clipping */
1241 
1242 /*! . */
gmtmap_crossing(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double * xlon,double * xlat,double * xx,double * yy,unsigned int * sides)1243 GMT_LOCAL unsigned int gmtmap_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *xlon, double *xlat, double *xx, double *yy, unsigned int *sides) {
1244 	if (GMT->current.map.prev_x_status == GMT->current.map.this_x_status && GMT->current.map.prev_y_status == GMT->current.map.this_y_status) {
1245 		/* This is naive. We could have two points outside with a line drawn between crossing the plotting area. */
1246 		return (0);
1247 	}
1248 	else if ((GMT->current.map.prev_x_status == 0 && GMT->current.map.prev_y_status == 0) || (GMT->current.map.this_x_status == 0 && GMT->current.map.this_y_status == 0)) {
1249 		/* This is a crossing */
1250 	}
1251 	else if (!(*GMT->current.map.overlap) (GMT, lon1, lat1, lon2, lat2))	/* Less clearcut case, check for overlap */
1252 		return (0);
1253 
1254 	/* Now compute the crossing */
1255 
1256 	GMT->current.map.corner = -1;
1257 	return ((*GMT->current.map.crossing) (GMT, lon1, lat1, lon2, lat2, xlon, xlat, xx, yy, sides));
1258 }
1259 
1260 /*! . */
gmtmap_x_to_corner(struct GMT_CTRL * GMT,double x)1261 GMT_LOCAL double gmtmap_x_to_corner (struct GMT_CTRL *GMT, double x) {
1262 	return ( (fabs (x - GMT->current.proj.rect[XLO]) < fabs (x - GMT->current.proj.rect[XHI])) ? GMT->current.proj.rect[XLO] : GMT->current.proj.rect[XHI]);
1263 }
1264 
1265 /*! . */
gmtmap_y_to_corner(struct GMT_CTRL * GMT,double y)1266 GMT_LOCAL double gmtmap_y_to_corner (struct GMT_CTRL *GMT, double y) {
1267 	return ( (fabs (y - GMT->current.proj.rect[YLO]) < fabs (y - GMT->current.proj.rect[YHI])) ? GMT->current.proj.rect[YLO] : GMT->current.proj.rect[YHI]);
1268 }
1269 
1270 /*! . */
gmtmap_move_to_rect(struct GMT_CTRL * GMT,double * x_edge,double * y_edge,uint64_t j,uint64_t nx)1271 GMT_LOCAL uint64_t gmtmap_move_to_rect (struct GMT_CTRL *GMT, double *x_edge, double *y_edge, uint64_t j, uint64_t nx) {
1272 	uint64_t n = 0;
1273 	int key;
1274 	double xtmp, ytmp;
1275 
1276 	/* May add 0, 1, or 2 points to path */
1277 
1278 	if (GMT->current.map.this_x_status == 0 && GMT->current.map.this_y_status == 0) return (1);	/* Completely Inside */
1279 
1280 	if (!nx && j > 0 && GMT->current.map.this_x_status != GMT->current.map.prev_x_status && GMT->current.map.this_y_status != GMT->current.map.prev_y_status) {	/* Must include corner */
1281 		xtmp = x_edge[j];	ytmp = y_edge[j];
1282 		if ((GMT->current.map.this_x_status * GMT->current.map.prev_x_status) == -4 || (GMT->current.map.this_y_status * GMT->current.map.prev_y_status) == -4) {	/* the two points outside on opposite sides */
1283 			x_edge[j] = (GMT->current.map.prev_x_status < 0) ? GMT->current.proj.rect[XLO] : ((GMT->current.map.prev_x_status > 0) ? GMT->current.proj.rect[XHI] : gmtmap_x_to_corner (GMT, x_edge[j-1]));
1284 			y_edge[j] = (GMT->current.map.prev_y_status < 0) ? GMT->current.proj.rect[YLO] : ((GMT->current.map.prev_y_status > 0) ? GMT->current.proj.rect[YHI] : gmtmap_y_to_corner (GMT, y_edge[j-1]));
1285 			j++;
1286 			x_edge[j] = (GMT->current.map.this_x_status < 0) ? GMT->current.proj.rect[XLO] : ((GMT->current.map.this_x_status > 0) ? GMT->current.proj.rect[XHI] : gmtmap_x_to_corner (GMT, xtmp));
1287 			y_edge[j] = (GMT->current.map.this_y_status < 0) ? GMT->current.proj.rect[YLO] : ((GMT->current.map.this_y_status > 0) ? GMT->current.proj.rect[YHI] : gmtmap_y_to_corner (GMT, ytmp));
1288 			j++;
1289 		}
1290 		else {
1291 			key = MIN (GMT->current.map.this_x_status, GMT->current.map.prev_x_status);
1292 			x_edge[j] = (key < 0) ? GMT->current.proj.rect[XLO] : GMT->current.proj.rect[XHI];
1293 			key = MIN (GMT->current.map.this_y_status, GMT->current.map.prev_y_status);
1294 			y_edge[j] = (key < 0) ? GMT->current.proj.rect[YLO] : GMT->current.proj.rect[YHI];
1295 			j++;
1296 		}
1297 		x_edge[j] = xtmp;	y_edge[j] = ytmp;
1298 		n = 1;
1299 	}
1300 
1301 	if (GMT->current.map.outside == gmtmap_rect_outside2) {	/* Need special check because this outside2 test is screwed up... */
1302 		if (x_edge[j] < GMT->current.proj.rect[XLO]) {
1303 			x_edge[j] = GMT->current.proj.rect[XLO];
1304 			GMT->current.map.this_x_status = -2;
1305 		}
1306 		else if (x_edge[j] > GMT->current.proj.rect[XHI]) {
1307 			x_edge[j] = GMT->current.proj.rect[XHI];
1308 			GMT->current.map.this_x_status = 2;
1309 		}
1310 		if (y_edge[j] < GMT->current.proj.rect[YLO]) {
1311 			y_edge[j] = GMT->current.proj.rect[YLO];
1312 			GMT->current.map.this_y_status = -2;
1313 		}
1314 		else if (y_edge[j] > GMT->current.proj.rect[YHI]) {
1315 			y_edge[j] = GMT->current.proj.rect[YHI];
1316 			GMT->current.map.this_y_status = 2;
1317 		}
1318 	}
1319 	else {
1320 		if (GMT->current.map.this_x_status != 0) x_edge[j] = (GMT->current.map.this_x_status < 0) ? GMT->current.proj.rect[XLO] : GMT->current.proj.rect[XHI];
1321 		if (GMT->current.map.this_y_status != 0) y_edge[j] = (GMT->current.map.this_y_status < 0) ? GMT->current.proj.rect[YLO] : GMT->current.proj.rect[YHI];
1322 	}
1323 
1324 	return (n + 1);
1325 }
1326 
1327 /*! . */
gmtmap_rect_clip_old(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n,double ** x,double ** y,uint64_t * total_nx)1328 GMT_LOCAL uint64_t gmtmap_rect_clip_old (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n, double **x, double **y, uint64_t *total_nx) {
1329 	uint64_t i, j = 0;
1330 	unsigned int nx, k, sides[4];
1331 	double xlon[4], xlat[4], xc[4], yc[4];
1332 
1333 	*total_nx = 0;	/* Keep track of total of crossings */
1334 
1335 	if (n == 0) return (0);
1336 
1337 	gmt_prep_tmp_arrays (GMT, GMT_NOTSET, 1, 2);	/* Init or reallocate tmp vectors */
1338 	(void) gmt_map_outside (GMT, lon[0], lat[0]);
1339 	gmt_geo_to_xy (GMT, lon[0], lat[0], &GMT->hidden.mem_coord[GMT_X][0], &GMT->hidden.mem_coord[GMT_Y][0]);
1340 	j += gmtmap_move_to_rect (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], 0, 0);
1341 	for (i = 1; i < n; i++) {
1342 		(void) gmt_map_outside (GMT, lon[i], lat[i]);
1343 		nx = gmtmap_crossing (GMT, lon[i-1], lat[i-1], lon[i], lat[i], xlon, xlat, xc, yc, sides);
1344 		for (k = 0; k < nx; k++) {
1345 			gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j, 2);	/* Init or reallocate tmp vectors */
1346 			GMT->hidden.mem_coord[GMT_X][j] = xc[k];
1347 			GMT->hidden.mem_coord[GMT_Y][j++] = yc[k];
1348 			(*total_nx) ++;
1349 		}
1350 		gmt_geo_to_xy (GMT, lon[i], lat[i], &GMT->hidden.mem_coord[GMT_X][j], &GMT->hidden.mem_coord[GMT_Y][j]);
1351 		gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j+2, 2);	/* Init or reallocate tmp vectors */
1352 		j += gmtmap_move_to_rect (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], j, nx);	/* May add 2 points, which explains the j+2 stuff */
1353 	}
1354 
1355 	*x = gmtlib_assign_vector (GMT, j, GMT_X);
1356 	*y = gmtlib_assign_vector (GMT, j, GMT_Y);
1357 
1358 	return (j);
1359 }
1360 
1361 /* Functions and macros used for new rectangular clipping using the Sutherland/Hodgman algorithm
1362  * in which we clip the polygon against each of the 4 sides.  To avoid lots of if/switch I have
1363  * two clip functions (for x and y line) and two in/out functions that tells us if a point is
1364  * inside the polygon relative to the line.  Then, pointers to these functions are passed to
1365  * make sure the right functions are used for each side in the loop over sides.  Unless I can
1366  * figure out a more clever recursive way I need to have 2 temporary arrays to shuffle the
1367  * intermediate results around.
1368  *
1369  * P.Wessel, March 2008
1370  */
1371 
1372 /*! This macro calculates the x-coordinates where the line segment crosses the border x = border.
1373  * By swapping x and y in the call we can use it for finding the y intersection. This macro is
1374  * never called when (y_prev - y_curr) = 0 so we don't divide by zero.
1375  */
1376 #define INTERSECTION_COORD(x_curr,y_curr,x_prev,y_prev,border) x_curr + (x_prev - x_curr) * (border - y_curr) / (y_prev - y_curr)
1377 
1378 /*! . */
gmtmap_clip_sn(double x_prev,double y_prev,double x_curr,double y_curr,double x[],double y[],double border,bool (* inside)(double,double),bool (* outside)(double,double),int * cross)1379 GMT_LOCAL unsigned int gmtmap_clip_sn (double x_prev, double y_prev, double x_curr, double y_curr, double x[], double y[], double border, bool (*inside) (double, double), bool (*outside) (double, double), int *cross) {
1380 	/* Clip against the south or north boundary (i.e., a horizontal line with y = border) */
1381 	*cross = 0;
1382 	if (doubleAlmostEqualZero (x_prev, x_curr) && doubleAlmostEqualZero (y_prev, y_curr))
1383 		return (0);	/* Do nothing for duplicates */
1384 	if (outside (y_prev, border)) {	/* Previous point is outside... */
1385 		if (outside (y_curr, border)) return 0;	/* ...as is the current point. Do nothing. */
1386 		/* Here, the line segment intersects the border - return both intersection and inside point */
1387 		y[0] = border;	x[0] = INTERSECTION_COORD (x_curr, y_curr, x_prev, y_prev, border);
1388 		*cross = +1;	/* Crossing to the inside */
1389 		x[1] = x_curr;	y[1] = y_curr;	return (2);
1390 	}
1391 	/* Here x_prev is inside */
1392 	if (inside (y_curr, border)) {	/* Return current point only */
1393 		x[0] = x_curr;	y[0] = y_curr;	return (1);
1394 	}
1395 	/* Segment intersects border - return intersection only */
1396 	*cross = -1;	/* Crossing to the outside */
1397 	y[0] = border;	x[0] = INTERSECTION_COORD (x_curr, y_curr, x_prev, y_prev, border);	return (1);
1398 }
1399 
1400 /*! . */
gmtmap_clip_we(double x_prev,double y_prev,double x_curr,double y_curr,double x[],double y[],double border,bool (* inside)(double,double),bool (* outside)(double,double),int * cross)1401 GMT_LOCAL unsigned int gmtmap_clip_we (double x_prev, double y_prev, double x_curr, double y_curr, double x[], double y[], double border, bool (*inside) (double, double), bool (*outside) (double, double), int *cross) {
1402 	/* Clip against the west or east boundary (i.e., a vertical line with x = border) */
1403 	*cross = 0;
1404 	if (doubleAlmostEqualZero (x_prev, x_curr) && doubleAlmostEqualZero (y_prev, y_curr))
1405 		return (0);	/* Do nothing for duplicates */
1406 	if (outside (x_prev, border)) {	/* Previous point is outside... */
1407 		if (outside (x_curr, border)) return 0;	/* ...as is the current point. Do nothing. */
1408 		/* Here, the line segment intersects the border - return both intersection and inside point */
1409 		x[0] = border;	y[0] = INTERSECTION_COORD (y_curr, x_curr, y_prev, x_prev, border);
1410 		*cross = +1;	/* Crossing to the inside */
1411 		x[1] = x_curr;	y[1] = y_curr;	return (2);
1412 	}
1413 	/* Here x_prev is inside */
1414 	if (inside (x_curr, border)) {	/* Return current point only */
1415 		x[0] = x_curr;	y[0] = y_curr;	return (1);
1416 	}
1417 	/* Segment intersects border - return intersection only */
1418 	*cross = -1;	/* Crossing to the outside */
1419 	x[0] = border;	y[0] = INTERSECTION_COORD (y_curr, x_curr, y_prev, x_prev, border);	return (1);
1420 }
1421 
1422 /* Tiny functions to tell if a value is <, <=, >=, > than the limit */
gmtmap_inside_lower_boundary(double val,double min)1423 static inline bool gmtmap_inside_lower_boundary (double val, double min) {return (val >= min);}
gmtmap_inside_upper_boundary(double val,double max)1424 static inline bool gmtmap_inside_upper_boundary (double val, double max) {return (val <= max);}
gmtmap_outside_lower_boundary(double val,double min)1425 static inline bool gmtmap_outside_lower_boundary (double val, double min) {return (val < min);}
gmtmap_outside_upper_boundary(double val,double max)1426 static inline bool gmtmap_outside_upper_boundary (double val, double max) {return (val > max);}
1427 
1428 /*! gmtmap_rect_clip is an implementation of the Sutherland/Hodgman algorithm polygon clipping algorithm.
1429  * Basically, it compares the polygon to one boundary at the time, and clips the polygon to be inside
1430  * that boundary; this is then repeated for all boundaries.  Assumptions here are Cartesian coordinates
1431  * so all boundaries are straight lines in x or y. */
gmtmap_rect_clip(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n,double ** x,double ** y,uint64_t * total_nx)1432 GMT_LOCAL uint64_t gmtmap_rect_clip (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n, double **x, double **y, uint64_t *total_nx) {
1433 	uint64_t i, n_get, m;
1434 	size_t n_alloc = 0;
1435 	unsigned int side, in = 1, out = 0, j, np;
1436 	int cross = 0;
1437 	bool polygon;
1438 	double *xtmp[2] = {NULL, NULL}, *ytmp[2] = {NULL, NULL}, xx[2], yy[2], border[4];
1439 	unsigned int (*clipper[4]) (double, double, double, double, double *, double *, double, bool (*inside) (double, double), bool (*outside) (double, double), int *);
1440 	bool (*inside[4]) (double, double);
1441 	bool (*outside[4]) (double, double);
1442 
1443 #ifdef DEBUG
1444 	FILE *fp = NULL;
1445 	bool dump = false;
1446 #endif
1447 
1448 	if (n == 0) return (0);
1449 
1450 	polygon = (n > 2 && !gmt_polygon_is_open (GMT, lon, lat, n));	/* true if input segment is a closed polygon */
1451 
1452 	*total_nx = 1;	/* So that calling program will not discard the clipped polygon */
1453 
1454 	/* Set up function pointers.  This could be done once in gmt_begin at some point */
1455 
1456 	clipper[GMT_BOTTOM] = gmtmap_clip_sn;	clipper[GMT_RIGHT] = gmtmap_clip_we; clipper[GMT_TOP] = gmtmap_clip_sn;	clipper[GMT_LEFT] = gmtmap_clip_we;
1457 	inside[GMT_RIGHT] = inside[GMT_TOP] = gmtmap_inside_upper_boundary;	outside[GMT_RIGHT] = outside[GMT_TOP] = gmtmap_outside_upper_boundary;
1458 	inside[GMT_BOTTOM] = inside[GMT_LEFT] = gmtmap_inside_lower_boundary;		outside[GMT_BOTTOM] = outside[GMT_LEFT] = gmtmap_outside_lower_boundary;
1459 	border[GMT_BOTTOM] = border[GMT_LEFT] = 0.0;	border[GMT_RIGHT] = GMT->current.map.width;	border[GMT_TOP] = GMT->current.map.height;
1460 
1461 	n_get = lrint (1.05*n+5);	/* Anticipate just a few crossings (5%)+5, allocate more later if needed */
1462 	/* Create a pair of arrays for holding input and output */
1463 	gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], n_get, &n_alloc, double);
1464 
1465 	/* Get Cartesian map coordinates */
1466 
1467 	for (m = 0; m < n; m++) gmt_geo_to_xy (GMT, lon[m], lat[m], &xtmp[0][m], &ytmp[0][m]);
1468 
1469 #ifdef DEBUG
1470 	if (dump) {
1471 		fp = fopen ("input.d", "w");
1472 		for (i = 0; i < n; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1473 		fclose (fp);
1474 	}
1475 #endif
1476 	for (side = 0; side < 4; side++) {	/* Must clip polygon against a single border, one border at a time */
1477 		n = m;	/* Current size of polygon */
1478 		m = 0;	/* Start with nuthin' */
1479 
1480 		gmt_M_uint_swap (in, out);	/* Swap what is input and output for clipping against this border */
1481 		if (n) {
1482 			/* Must ensure we copy the very first point if it is inside the clip rectangle */
1483 			if (inside[side] ((side%2) ? xtmp[in][0] : ytmp[in][0], border[side])) {xtmp[out][0] = xtmp[in][0]; ytmp[out][0] = ytmp[in][0]; m = 1;}	/* First point is inside; add it */
1484 		}
1485 		for (i = 1; i < n; i++) {	/* For each line segment */
1486 			np = clipper[side] (xtmp[in][i-1], ytmp[in][i-1], xtmp[in][i], ytmp[in][i], xx, yy, border[side], inside[side], outside[side], &cross);	/* Returns 0, 1, or 2 points */
1487 			for (j = 0; j < np; j++) {	/* Add the np returned points to the new clipped polygon path */
1488 				if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1489 				xtmp[out][m] = xx[j]; ytmp[out][m] = yy[j]; m++;
1490 			}
1491 		}
1492 		if (polygon && gmt_polygon_is_open (GMT, xtmp[out], ytmp[out], m)) {	/* Do we need to explicitly close this clipped polygon? */
1493 			if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1494 			xtmp[out][m] = xtmp[out][0];	ytmp[out][m] = ytmp[out][0];	m++;	/* Yes. */
1495 		}
1496 	}
1497 
1498 	gmt_M_free (GMT, xtmp[1]);	/* Free the pairs of arrays that holds the last input array */
1499 	gmt_M_free (GMT, ytmp[1]);
1500 
1501 	if (m) {	/* Reallocate and return the array with the final clipped polygon */
1502 		n_alloc = m;
1503 		gmt_M_malloc2 (GMT, xtmp[0], ytmp[0], 0U, &n_alloc, double);
1504 		*x = xtmp[0];
1505 		*y = ytmp[0];
1506 #ifdef DEBUG
1507 		if (dump) {
1508 			fp = fopen ("output.d", "w");
1509 			for (i = 0; i < m; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1510 			fclose (fp);
1511 		}
1512 #endif
1513 		if (GMT->common.R.oblique) {	/* Ensure region corners are added if any poles are enclosed */
1514 			double ycoord_pole = 0;
1515 			bool do_it = true;
1516 			if (GMT->current.proj.corner[0] && GMT->current.proj.corner[1])	/* Ensure S pole is included */
1517 				ycoord_pole = GMT->current.proj.rect[YLO];
1518 			else if (GMT->current.proj.corner[2] && GMT->current.proj.corner[3])	/* Ensure N pole is included */
1519 				ycoord_pole = GMT->current.proj.rect[YHI];
1520 			else
1521 				do_it = false;
1522 
1523 			if (do_it) {
1524 				for (i = 1; i < m; i++) {
1525 					if (doubleAlmostEqual (fabs (xtmp[0][i] - xtmp[0][i-1]), GMT->current.map.width))
1526 						ytmp[0][i] = ytmp[0][i-1] = ycoord_pole;
1527 				}
1528 			}
1529 		}
1530 	}
1531 	else {	/* Nothing survived the clipping - free the output arrays */
1532 		gmt_M_free (GMT, xtmp[0]);
1533 		gmt_M_free (GMT, ytmp[0]);
1534 	}
1535 
1536 	return (m);
1537 }
1538 
1539 /* map_wesn_clip differs from gmtmap_rect_clip in that the boundaries of constant lon or lat may end up as
1540  * curved lines depending on the map projection.  Thus, if a line crosses the boundary and reenters at
1541  * another point on the boundary then the straight line between these crossing points should really
1542  * project to a curved boundary segment.  The H-S algorithm was originally rectangular so we got straight
1543  * lines.  Here, we check if (1) the particular boundary being tested is curved, and if true then we
1544  * keep track of the indices of the exit and entry points in the array, and once a boundary has been
1545  * processed we must add more points between the exit and entry pairs to properly handle the curved
1546  * segment.  The arrays x_index and x_type stores the index of the exit/entry points and the type
1547  * (+1 we enter, -1 we exit).  We then use gmtlib_map_path to compute the required segments to insert.
1548  * P. Wessel, 2--9-05-07
1549  */
1550 
1551 /*! . */
gmtmap_lon_to_corner(struct GMT_CTRL * GMT,double lon)1552 GMT_LOCAL double gmtmap_lon_to_corner (struct GMT_CTRL *GMT, double lon) {
1553 	return ( (fabs (lon - GMT->common.R.wesn[XLO]) < fabs (lon - GMT->common.R.wesn[XHI])) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI]);
1554 }
1555 
1556 /*! . */
gmtmap_lat_to_corner(struct GMT_CTRL * GMT,double lat)1557 GMT_LOCAL double gmtmap_lat_to_corner (struct GMT_CTRL *GMT, double lat) {
1558 	return ( (fabs (lat - GMT->common.R.wesn[YLO]) < fabs (lat - GMT->common.R.wesn[YHI])) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI]);
1559 }
1560 
1561 /*! . */
gmtmap_move_to_wesn(struct GMT_CTRL * GMT,double * x_edge,double * y_edge,double lon,double lat,double lon_old,double lat_old,uint64_t j,uint64_t nx)1562 GMT_LOCAL int gmtmap_move_to_wesn (struct GMT_CTRL *GMT, double *x_edge, double *y_edge, double lon, double lat, double lon_old, double lat_old, uint64_t j, uint64_t nx) {
1563 	int n = 0, key;
1564 	double xtmp, ytmp, lon_p, lat_p;
1565 
1566 	/* May add 0, 1, or 2 points to path */
1567 
1568 	if (!nx && j > 0 && GMT->current.map.this_x_status != GMT->current.map.prev_x_status && GMT->current.map.this_y_status != GMT->current.map.prev_y_status) {	/* Need corner */
1569 		xtmp = x_edge[j];	ytmp = y_edge[j];
1570 		if ((GMT->current.map.this_x_status * GMT->current.map.prev_x_status) == -4 || (GMT->current.map.this_y_status * GMT->current.map.prev_y_status) == -4) {	/* the two points outside on opposite sides */
1571 			lon_p = (GMT->current.map.prev_x_status < 0) ? GMT->common.R.wesn[XLO] : ((GMT->current.map.prev_x_status > 0) ? GMT->common.R.wesn[XHI] : gmtmap_lon_to_corner (GMT, lon_old));
1572 			lat_p = (GMT->current.map.prev_y_status < 0) ? GMT->common.R.wesn[YLO] : ((GMT->current.map.prev_y_status > 0) ? GMT->common.R.wesn[YHI] : gmtmap_lat_to_corner (GMT, lat_old));
1573 			gmt_geo_to_xy (GMT, lon_p, lat_p, &x_edge[j], &y_edge[j]);
1574 			j++;
1575 			lon_p = (GMT->current.map.this_x_status < 0) ? GMT->common.R.wesn[XLO] : ((GMT->current.map.this_x_status > 0) ? GMT->common.R.wesn[XHI] : gmtmap_lon_to_corner (GMT, lon));
1576 			lat_p = (GMT->current.map.this_y_status < 0) ? GMT->common.R.wesn[YLO] : ((GMT->current.map.this_y_status > 0) ? GMT->common.R.wesn[YHI] : gmtmap_lat_to_corner (GMT, lat));
1577 			gmt_geo_to_xy (GMT, lon_p, lat_p, &x_edge[j], &y_edge[j]);
1578 			j++;
1579 		}
1580 		else {
1581 			key = MIN (GMT->current.map.this_x_status, GMT->current.map.prev_x_status);
1582 			lon_p = (key < 0) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
1583 			key = MIN (GMT->current.map.this_y_status, GMT->current.map.prev_y_status);
1584 			lat_p = (key < 0) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
1585 			gmt_geo_to_xy (GMT, lon_p, lat_p, &x_edge[j], &y_edge[j]);
1586 			j++;
1587 		}
1588 		x_edge[j] = xtmp;	y_edge[j] = ytmp;
1589 		n = 1;
1590 	}
1591 	if (GMT->current.map.this_x_status != 0) lon = (GMT->current.map.this_x_status < 0) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
1592 	if (GMT->current.map.this_y_status != 0) lat = (GMT->current.map.this_y_status < 0) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
1593 	gmt_geo_to_xy (GMT, lon, lat, &x_edge[j], &y_edge[j]);
1594 	return (n + 1);
1595 }
1596 
1597 /*! . */
gmtmap_wesn_clip_old(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n,double ** x,double ** y,uint64_t * total_nx)1598 GMT_LOCAL uint64_t gmtmap_wesn_clip_old (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n, double **x, double **y, uint64_t *total_nx) {
1599 	uint64_t i, j = 0, nx, k;
1600 	unsigned int sides[4];
1601 	double xlon[4], xlat[4], xc[4], yc[4];
1602 
1603 	*total_nx = 0;	/* Keep track of total of crossings */
1604 
1605 	if (n == 0) return (0);
1606 
1607 	gmt_prep_tmp_arrays (GMT, GMT_NOTSET, 1, 2);	/* Init or reallocate tmp vectors */
1608 
1609 	(void) gmt_map_outside (GMT, lon[0], lat[0]);
1610 	j = gmtmap_move_to_wesn (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], lon[0], lat[0], 0.0, 0.0, 0, 0);	/* Add one point */
1611 
1612 	for (i = 1; i < n; i++) {
1613 		(void) gmt_map_outside (GMT, lon[i], lat[i]);
1614 		nx = gmtmap_crossing (GMT, lon[i-1], lat[i-1], lon[i], lat[i], xlon, xlat, xc, yc, sides);
1615 		for (k = 0; k < nx; k++) {
1616 			gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j, 2);	/* Init or reallocate tmp vectors */
1617 			GMT->hidden.mem_coord[GMT_X][j]   = xc[k];
1618 			GMT->hidden.mem_coord[GMT_Y][j++] = yc[k];
1619 			(*total_nx) ++;
1620 		}
1621 		gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j+2, 2);	/* Init or reallocate tmp vectors */
1622 		j += gmtmap_move_to_wesn (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], lon[i], lat[i], lon[i-1], lat[i-1], j, nx);	/* May add 2 points, which explains the j+2 stuff */
1623 	}
1624 
1625 	*x = gmtlib_assign_vector (GMT, j, GMT_X);
1626 	*y = gmtlib_assign_vector (GMT, j, GMT_Y);
1627 
1628 #ifdef CRAP
1629 {
1630 	FILE *fp = NULL;
1631 	double out[2];
1632 	fp = fopen ("crap.d", "a");
1633 	fprintf (fp, "> N = %d\n", (int)j);
1634 	for (i = 0; i < j; i++) {
1635 		out[GMT_X] = *x[i];
1636 		out[GMT_Y] = *y[i];
1637 		GMT->current.io.output (GMT, fp, 2, out);
1638 	}
1639 	fclose (fp);
1640 }
1641 #endif
1642 	return (j);
1643 }
1644 
1645 /*! . */
map_wesn_clip(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n_orig,double ** x,double ** y,uint64_t * total_nx)1646 uint64_t map_wesn_clip (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n_orig, double **x, double **y, uint64_t *total_nx) {
1647 	char *x_type = NULL, *name = "SENW";
1648 	size_t n_alloc = 0, n_x_alloc = 0, n_t_alloc = 0;
1649 	uint64_t new_n, i, n_get, n, m, n_cross = 0, *x_index = NULL;
1650 	unsigned int j, np, side, in = 1, out = 0;
1651 	int cross = 0;
1652 	bool curved, jump = false, polygon, periodic = false;
1653 	double *xtmp[2] = {NULL, NULL}, *ytmp[2] = {NULL, NULL}, xx[2], yy[2], border[4];
1654 	double x1, x2, y1, y2;
1655 	unsigned int (*clipper[4]) (double, double, double, double, double *, double *, double, bool (*inside) (double, double), bool (*outside) (double, double), int *);
1656 	bool (*inside[4]) (double, double);
1657 	bool (*outside[4]) (double, double);
1658 
1659 #ifdef DEBUG
1660 	FILE *fp = NULL;
1661 	bool dump = false;
1662 #endif
1663 
1664 	if ((n = n_orig) == 0) return (0);
1665 
1666 	/* If there are jumps etc call the old clipper, else we try the new clipper */
1667 
1668 	gmt_geo_to_xy (GMT, lon[0], lat[0], &x1, &y1);
1669 	for (i = 1; !jump && i < n; i++) {
1670 		gmt_geo_to_xy (GMT, lon[i], lat[i], &x2, &y2);
1671 		jump = gmtmap_jump_x (GMT, x2, y2, x1, y1);
1672 		x1 = x2;	y1 = y2;
1673 	}
1674 
1675 	if (jump) return (gmtmap_wesn_clip_old (GMT, lon, lat, n, x, y, total_nx));	/* Must do the old way for now */
1676 	periodic = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);	/* No point clipping against W and E if periodic map */
1677 
1678 	/* Here we can try the Sutherland/Hodgman algorithm */
1679 
1680 	polygon = (n > 2 && !gmt_polygon_is_open (GMT, lon, lat, n));	/* true if input segment is a closed polygon */
1681 
1682 	*total_nx = 1;	/* So that calling program will not discard the clipped polygon */
1683 
1684 	/* Set up function pointers.  This could be done once in gmt_begin at some point */
1685 
1686 	clipper[GMT_BOTTOM] = gmtmap_clip_sn;	clipper[GMT_RIGHT] = gmtmap_clip_we; clipper[GMT_TOP] = gmtmap_clip_sn;	clipper[GMT_LEFT] = gmtmap_clip_we;
1687 	inside[GMT_RIGHT] = inside[GMT_TOP] = gmtmap_inside_upper_boundary;	outside[GMT_RIGHT] = outside[GMT_TOP] = gmtmap_outside_upper_boundary;
1688 	inside[GMT_BOTTOM] = inside[GMT_LEFT] = gmtmap_inside_lower_boundary;	outside[GMT_BOTTOM] = outside[GMT_LEFT] = gmtmap_outside_lower_boundary;
1689 	border[GMT_BOTTOM] = GMT->common.R.wesn[YLO]; border[GMT_LEFT] = GMT->common.R.wesn[XLO];	border[GMT_RIGHT] = GMT->common.R.wesn[XHI];	border[GMT_TOP] = GMT->common.R.wesn[YHI];
1690 
1691 /* This is new approach to get rid of those crossing lines for filled polygons,
1692  * i.e., issue # 949.  Also see comments further down.
1693  * P. Wessel, Dec 1 2016 */
1694 	if (GMT->current.map.coastline && periodic) {	/* Make data longitudes have no jumps [This is for pscoast] */
1695 		for (i = 0; i < n; i++) {
1696 			if (lon[i] < border[GMT_LEFT] && (lon[i] + 360.0) <= border[GMT_RIGHT])
1697 				lon[i] += 360.0;
1698 			else if (lon[i] > border[GMT_RIGHT] && (lon[i] - 360.0) >= border[GMT_LEFT])
1699 				lon[i] -= 360.0;
1700 		}
1701 	}
1702 	else {	/* Don't let points be > 180 from center longitude */
1703 		double mid_lon = 0.5 * (border[GMT_LEFT] + border[GMT_RIGHT]), diff;
1704 		for (i = 0; i < n; i++) {
1705 			diff = lon[i] - mid_lon;
1706 			if (diff > 180.0) lon[i] -= 360.0;
1707 			else if (diff < -180.0) lon[i] += 360.0;
1708 		}
1709 	}
1710 	if (!GMT->current.map.coastline) {	/* Not do if pscoast since it has its own oddness */
1711 		double xmin, xmax;
1712 		gmtlib_get_lon_minmax (GMT, lon, n, &xmin, &xmax);
1713 		xmin -= 360.0;	xmax -= 360.0;
1714 		while (xmax < border[GMT_LEFT]) {xmin += 360.0;	xmax += 360.0;}
1715 		if (xmin > border[GMT_RIGHT]) return 0;	/* Outside */
1716 	}
1717 
1718 	n_get = lrint (1.05*n+5);	/* Anticipate just a few crossings (5%)+5, allocate more later if needed */
1719 	/* Create a pair of arrays for holding input and output */
1720 	gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], n_get, &n_alloc, double);
1721 
1722 	/* Make copy of lon/lat coordinates */
1723 
1724 	gmt_M_memcpy (xtmp[0], lon, n, double);	gmt_M_memcpy (ytmp[0], lat, n, double);
1725 	m = n;
1726 
1727 	/* Preallocate space for crossing information */
1728 
1729 	x_index = gmt_M_malloc (GMT, NULL, GMT_TINY_CHUNK, &n_x_alloc, uint64_t);
1730 	x_type = gmt_M_malloc (GMT, NULL,  GMT_TINY_CHUNK, &n_t_alloc, char);
1731 
1732 #ifdef DEBUG
1733 	if (dump) {
1734 		fp = fopen ("input.d", "w");
1735 		for (i = 0; i < n; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1736 		fclose (fp);
1737 	}
1738 #endif
1739 	for (side = 0; side < 4; side++) {	/* Must clip polygon against a single border, one border at a time */
1740 		n = m;		/* Current size of polygon */
1741 		m = 0;		/* Start with nuthin' */
1742 		n_cross = 0;	/* No crossings so far */
1743 
1744 		curved = !((side%2) ? GMT->current.map.meridian_straight : GMT->current.map.parallel_straight);	/* Is this border straight or curved when projected */
1745 		gmt_M_uint_swap (in, out);	/* Swap what is input and output for clipping against this border */
1746 		if (side%2 && periodic) {	/* No clipping can take place on w or e border; just copy all and go to next side */
1747 			m = n;
1748 			if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1749 			gmt_M_memcpy (xtmp[out], xtmp[in], m, double);
1750 			gmt_M_memcpy (ytmp[out], ytmp[in], m, double);
1751 			continue;
1752 		}
1753 #if 0
1754 /* This caused lots of issues with various map types so replaced by the check about
1755  * that uses the mid-longitude as center and does a +/- 180 test from that.
1756  * P. Wessel, Dec 1 2016 */
1757 		if (!GMT->current.map.coastline && side % 2) {	/* Either left or right border */
1758 			/* For non-periodic maps we have to be careful to position the polygon so it does
1759 			 * not have longitude jumps at the current border.  This does not apply to pscoast
1760 			 * which has special handling and hence bypasses this test.
1761 			 * Note that we may be using the map longitude range if this exceeds 180
1762 			 * as it is tricky to make those plots with large but < 360 longitudes. */
1763 			//double map_lon_range = MAX (180.0, border[GMT_RIGHT] - border[GMT_LEFT]);
1764 			double map_lon_range = 1800.0, diff;
1765 			for (i = 0; i < n; i++) {	/* If points is > map_lon_range degrees from border, flip side */
1766 				diff = xtmp[in][i] - border[side];
1767 				if (diff > map_lon_range) xtmp[in][i] -= 360.0;
1768 				else if (diff < -map_lon_range) xtmp[in][i] += 360.0;
1769 			}
1770 		}
1771 #endif
1772 		if (n) {
1773 			/* Must ensure we copy the very first point if it is inside the clip rectangle */
1774 			if (inside[side] ((side%2) ? xtmp[in][0] : ytmp[in][0], border[side])) {xtmp[out][0] = xtmp[in][0]; ytmp[out][0] = ytmp[in][0]; m = 1;}	/* First point is inside; add it */
1775 		}
1776 		for (i = 1; i < n; i++) {	/* For each line segment */
1777 			np = clipper[side] (xtmp[in][i-1], ytmp[in][i-1], xtmp[in][i], ytmp[in][i], xx, yy, border[side], inside[side], outside[side], &cross);	/* Returns 0, 1, or 2 points */
1778 			if (polygon && cross && curved) {	/* When crossing in/out of a curved boundary we must eventually sample along the curve between crossings */
1779 				x_index[n_cross] = m;		/* Index of intersection point (which will be copied from xx[0], yy[0] below) */
1780 				x_type[n_cross] = (char)cross;	/* -1 going out, +1 going in */
1781 				if (++n_cross == n_x_alloc) {
1782 					x_index = gmt_M_malloc (GMT, x_index, n_cross, &n_x_alloc, uint64_t);
1783 					x_type = gmt_M_malloc (GMT, x_type,  n_cross, &n_t_alloc, char);
1784 				}
1785 			}
1786 			for (j = 0; j < np; j++) {	/* Add the np returned points to the new clipped polygon path */
1787 				if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1788 				xtmp[out][m] = xx[j]; ytmp[out][m] = yy[j]; m++;
1789 			}
1790 		}
1791 		if (polygon && gmt_polygon_is_open (GMT, xtmp[out], ytmp[out], m)) {	/* Do we need to explicitly close this clipped polygon? */
1792 			if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1793 			xtmp[out][m] = xtmp[out][0];	ytmp[out][m] = ytmp[out][0];	m++;	/* Yes. */
1794 		}
1795 		if (polygon && curved && n_cross) {	/* Must resample between crossing points */
1796 			double *x_add = NULL, *y_add = NULL, *x_cpy = NULL, *y_cpy = NULL;
1797 			size_t np = 0;
1798 			uint64_t add, last_index = 0, p, p_next;
1799 
1800 			if (n_cross%2 == 1) {	/* Should not happen with a polygon */
1801 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in map_wesn_clip, side %c: odd number of crossings?\n", name[side]);
1802 			}
1803 
1804 			/* First copy the current polygon */
1805 
1806 			gmt_M_malloc2 (GMT, x_cpy, y_cpy, m, &np, double);
1807 			gmt_M_memcpy (x_cpy, xtmp[out], m, double);
1808 			gmt_M_memcpy (y_cpy, ytmp[out], m, double);
1809 
1810 			for (p = np = 0; p < n_cross; p++) {	/* Process each crossing point */
1811 				if (last_index < x_index[p]) {	/* Copy over segment from were we left off to this crossing point */
1812 					add = x_index[p] - last_index;
1813 					if ((new_n = (np+add)) >= n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], new_n, &n_alloc, double);
1814 					gmt_M_memcpy (&xtmp[out][np], &x_cpy[last_index], add, double);
1815 					gmt_M_memcpy (&ytmp[out][np], &y_cpy[last_index], add, double);
1816 					np += add;
1817 					last_index = x_index[p];
1818 				}
1819 				if (x_type[p] == -1) {	/* Must add path from this exit to the next entry */
1820 					double start_lon, stop_lon;
1821 					p_next = (p == (n_cross-1)) ? 0 : p + 1;	/* index of the next crossing */
1822 					start_lon = x_cpy[x_index[p]];	stop_lon = x_cpy[x_index[p_next]];
1823 					if (side%2 == 0 && periodic) {	/* Make sure we select the shortest longitude arc */
1824 						if ((x_cpy[x_index[p_next]] - x_cpy[x_index[p]]) < -180.0)
1825 							stop_lon += 360.0;
1826 						else if ((x_cpy[x_index[p_next]] - x_cpy[x_index[p]]) > +180.0)
1827 							stop_lon -= 360.0;
1828 					}
1829 					add = gmtlib_map_path (GMT, start_lon, y_cpy[x_index[p]], stop_lon, y_cpy[x_index[p_next]], &x_add, &y_add);
1830 					if ((new_n = (np+add)) >= n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], new_n, &n_alloc, double);
1831 					gmt_M_memcpy (&xtmp[out][np], x_add, add, double);
1832 					gmt_M_memcpy (&ytmp[out][np], y_add, add, double);
1833 					if (add) { gmt_M_free (GMT, x_add);	gmt_M_free (GMT, y_add); }
1834 					np += add;
1835 					last_index = x_index[p_next];
1836 				}
1837 			}
1838 			if (x_index[0] > 0) {	/* First point was clean inside, must add last connection */
1839 				add = m - last_index;
1840 				if ((new_n = (np+add)) >= n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], new_n, &n_alloc, double);
1841 				gmt_M_memcpy (&xtmp[out][np], &x_cpy[last_index], add, double);
1842 				gmt_M_memcpy (&ytmp[out][np], &y_cpy[last_index], add, double);
1843 				np += add;
1844 			}
1845 			m = np;	/* New total of points */
1846 			gmt_M_free (GMT, x_cpy);	gmt_M_free (GMT, y_cpy);
1847 		}
1848 	}
1849 
1850 	gmt_M_free (GMT, xtmp[1]);	/* Free the pairs of arrays that holds the last input array */
1851 	gmt_M_free (GMT, ytmp[1]);
1852 	gmt_M_free (GMT, x_index);	/* Free the pairs of arrays that holds the crossing info */
1853 	gmt_M_free (GMT, x_type);
1854 
1855 	if (m) {	/* Reallocate and return the array with the final clipped polygon */
1856 		n_alloc = m;
1857 		gmt_M_malloc2 (GMT, xtmp[0], ytmp[0], 0U, &n_alloc, double);
1858 		/* Convert to map coordinates */
1859 		for (i = 0; i < m; i++) gmt_geo_to_xy (GMT, xtmp[0][i], ytmp[0][i], &xtmp[0][i], &ytmp[0][i]);
1860 
1861 		*x = xtmp[0];
1862 		*y = ytmp[0];
1863 #ifdef DEBUG
1864 		if (dump) {
1865 			fp = fopen ("output.d", "w");
1866 			for (i = 0; i < m; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1867 			fclose (fp);
1868 		}
1869 #endif
1870 	}
1871 	else {	/* Nothing survived the clipping - free the output arrays */
1872 		gmt_M_free (GMT, xtmp[0]);
1873 		gmt_M_free (GMT, ytmp[0]);
1874 	}
1875 
1876 	return (m);
1877 }
1878 
1879 /*! . */
gmtmap_radial_boundary_arc(struct GMT_CTRL * GMT,int this_way,double end_x[],double end_y[],double ** xarc,double ** yarc)1880 GMT_LOCAL uint64_t gmtmap_radial_boundary_arc (struct GMT_CTRL *GMT, int this_way, double end_x[], double end_y[], double **xarc, double **yarc) {
1881 	uint64_t n_arc, k, pt;
1882 	double az1, az2, d_az, da, xr, yr, da_try, *xx = NULL, *yy = NULL;
1883 
1884 	/* When a polygon crosses out then in again into the circle we need to add a boundary arc
1885 	 * to the polygon where it is clipped.  We simply sample the circle as finely as the arc
1886 	 * length and the current line_step demands */
1887 
1888 	da_try = (GMT->current.setting.map_line_step * 360.0) / (TWO_PI * GMT->current.proj.r);	/* Angular step in degrees */
1889 	az1 = d_atan2d (end_y[0], end_x[0]);	/* azimuth from map center to 1st crossing */
1890 	az2 = d_atan2d (end_y[1], end_x[1]);	/* azimuth from map center to 2nd crossing */
1891 	gmt_M_set_delta_lon (az1, az2, d_az);	/* Insist we take the short arc for now */
1892 	n_arc = lrint (ceil (fabs (d_az) / da_try));	/* Get number of integer increments of da_try degree... */
1893 	if (n_arc < 2) n_arc = 2;	/* ...but minimum 2 */
1894 	da = d_az / (n_arc - 1);	/* Reset da to get exact steps */
1895 	if (n_arc <= 2) return (0);	/* Arc is too short to have intermediate points */
1896 	n_arc -= 2;	/* We do not include the end points since these are the crossing points handled in the calling function */
1897 	gmt_M_malloc2 (GMT, xx, yy, n_arc, NULL, double);
1898 	for (k = 1; k <= n_arc; k++) {	/* Create points along arc from first to second crossing point (k-loop excludes the end points) */
1899 		sincosd (az1 + k * da, &yr, &xr);
1900 		pt = (this_way) ? n_arc - k : k - 1;	/* The order we add the arc depends if we exited or entered the inside area */
1901 		xx[pt] = GMT->current.proj.r * (1.0 + xr);
1902 		yy[pt] = GMT->current.proj.r * (1.0 + yr);
1903 	}
1904 
1905 	*xarc = xx;
1906 	*yarc = yy;
1907 	return (n_arc);
1908 }
1909 
1910 #ifdef DEBUG
1911 /* If we need to dump out clipped polygon then set clip_dump = 1 during execution */
1912 static int clip_dump = 0, clip_id = 0;
gmtmap_dumppol(uint64_t n,double * x,double * y,int * id)1913 GMT_LOCAL void gmtmap_dumppol (uint64_t n, double *x, double *y, int *id) {
1914 	uint64_t i;
1915 	FILE *fp = NULL;
1916 	char line[GMT_LEN64];
1917 	snprintf (line, GMT_LEN64, "dump_%d.d", *id);
1918 	fp = fopen (line, "w");
1919 	for (i = 0; i < n; i++) fprintf (fp, "%g\t%g\n", x[i], y[i]);
1920 	fclose (fp);
1921 	(*id)++;
1922 }
1923 #endif
1924 
1925 /*! . */
gmtmap_radial_clip(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t np,double ** x,double ** y,uint64_t * total_nx)1926 GMT_LOCAL uint64_t gmtmap_radial_clip (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t np, double **x, double **y, uint64_t *total_nx) {
1927 	size_t n_alloc = 0;
1928 	uint64_t n = 0, n_arc;
1929 	unsigned int i, nx;
1930 	unsigned int sides[4];
1931 	bool this_side = false, add_boundary = false;
1932 	double xlon[4], xlat[4], xc[4], yc[4], end_x[3], end_y[3], xr, yr;
1933 	double *xx = NULL, *yy = NULL, *xarc = NULL, *yarc = NULL;
1934 
1935 	*total_nx = 0;	/* Keep track of total of crossings */
1936 
1937 	if (np == 0) return (0);
1938 
1939 	if (!gmt_map_outside (GMT, lon[0], lat[0])) {
1940 		gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1941 		gmt_geo_to_xy (GMT, lon[0], lat[0], &xx[0], &yy[0]);
1942 		n++;
1943 	}
1944 	nx = 0;
1945 	for (i = 1; i < np; i++) {
1946 		this_side = gmt_map_outside (GMT, lon[i], lat[i]);
1947 		if (gmtmap_crossing (GMT, lon[i-1], lat[i-1], lon[i], lat[i], xlon, xlat, xc, yc, sides)) {
1948 			if (this_side) {	/* Crossing boundary and leaving circle: Add exit point to the path */
1949 				if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1950 				xx[n] = xc[0];	yy[n] = yc[0];	n++;
1951 			}
1952 			end_x[nx] = xc[0] - GMT->current.proj.r;	end_y[nx] = yc[0] - GMT->current.proj.r;
1953 			nx++;
1954 			(*total_nx) ++;
1955 			if (nx >= 2) {	/* Got a pair of entry+exit points */
1956 				add_boundary = !this_side;	/* We only add boundary arcs if we first exited and now entered the circle again */
1957 			}
1958 			if (add_boundary) {	/* Crossed twice.  Now add arc between the two crossing points */
1959 				/* PW: Currently, we make the assumption that the shortest arc is the one we want.  However,
1960 				 * extremely large polygons could cut the boundary so that it is the longest arc we want.
1961 				 * The way to improve this algorithm in the future is to find the two opposite points on
1962 				 * the circle boundary that lies on the bisector of az1,az2, and see which point lies
1963 				 * inside the polygon.  This would require that gmt_inonout_sphpol be called.
1964 				 */
1965 				if ((n_arc = gmtmap_radial_boundary_arc (GMT, this_side, &end_x[nx-2], &end_y[nx-2], &xarc, &yarc)) > 0) {
1966 					if ((n + n_arc) >= n_alloc) gmt_M_malloc2 (GMT, xx, yy, n + n_arc, &n_alloc, double);
1967 					gmt_M_memcpy (&xx[n], xarc, n_arc, double);	/* Copy longitudes of arc */
1968 					gmt_M_memcpy (&yy[n], yarc, n_arc, double);	/* Copy latitudes of arc */
1969 					n += n_arc;	/* Number of arc points added (end points are done separately) */
1970 					gmt_M_free (GMT, xarc);	gmt_M_free (GMT,  yarc);
1971 				}
1972 				add_boundary = false;
1973 				nx -= 2;	/* Done with those two crossings */
1974 			}
1975 			if (!this_side) {	/* Crossing boundary and entering circle: Add entry point to the path */
1976 				if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1977 				xx[n] = xc[0];	yy[n] = yc[0];	n++;
1978 			}
1979 		}
1980 		gmt_geo_to_xy (GMT, lon[i], lat[i], &xr, &yr);
1981 		if (!this_side) {	/* Only add points actually inside the map to the path */
1982 			if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1983 			xx[n] = xr;	yy[n] = yr;	n++;
1984 		}
1985 	}
1986 
1987 	if (nx == 2) {	/* Must close polygon by adding boundary arc */
1988 		if ((n_arc = gmtmap_radial_boundary_arc (GMT, this_side, end_x, end_y, &xarc, &yarc)) > 0) {
1989 			if ((n + n_arc) >= n_alloc) gmt_M_malloc2 (GMT, xx, yy, n + n_arc, &n_alloc, double);
1990 			gmt_M_memcpy (&xx[n], xarc, n_arc, double);	/* Copy longitudes of arc */
1991 			gmt_M_memcpy (&yy[n], yarc, n_arc, double);	/* Copy latitudes of arc */
1992 			n += n_arc;	/* Number of arc points added (end points are done separately) */
1993 			gmt_M_free (GMT, xarc);	gmt_M_free (GMT,  yarc);
1994 		}
1995 		if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1996 		xx[n] = xx[0];	yy[n] = yy[0];	n++;	/* Close the polygon */
1997 	}
1998 	n_alloc = n;
1999 	gmt_M_malloc2 (GMT, xx, yy, 0U, &n_alloc, double);
2000 	*x = xx;
2001 	*y = yy;
2002 #ifdef DEBUG
2003 	if (clip_dump) gmtmap_dumppol (n, xx, yy, &clip_id);
2004 #endif
2005 
2006 	return (n);
2007 }
2008 
2009 /*! . */
gmtmap_cartesian_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2010 GMT_LOCAL bool gmtmap_cartesian_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2011 	/* Return true if the projection of either (lon0,lat0) and (lon1,lat1) is inside (not on) the rectangular map boundary */
2012 	/* Here, lon,lat etc are Cartesian and not geographic coordinates, otherwise gmtmap_rect_overlap is used */
2013 	double x0, y0, x1, y1;
2014 
2015 	gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
2016 	gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
2017 
2018 	if (x0 > x1) gmt_M_double_swap (x0, x1);
2019 	if (y0 > y1) gmt_M_double_swap (y0, y1);
2020 
2021 	if (x1 - GMT->current.proj.rect[XLO] < -GMT_CONV8_LIMIT || x0 - GMT->current.proj.rect[XHI] > GMT_CONV8_LIMIT) return (false);
2022 	if (y1 - GMT->current.proj.rect[YLO] < -GMT_CONV8_LIMIT || y0 - GMT->current.proj.rect[YHI] > GMT_CONV8_LIMIT) return (false);
2023 	return (true);
2024 }
2025 
2026 /*! . */
gmtmap_rect_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2027 GMT_LOCAL bool gmtmap_rect_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2028 	/* Return true if the projection of either (lon0,lat0) and (lon1,lat1) is inside (not on) the rectangular map boundary */
2029 	double x0, y0, x1, y1;
2030 
2031 	gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
2032 	gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
2033 
2034 	if (x0 > x1) gmt_M_double_swap (x0, x1);
2035 	if (y0 > y1) gmt_M_double_swap (y0, y1);
2036 
2037 	if (x1 - GMT->current.proj.rect[XLO] < -GMT_CONV8_LIMIT || x0 - GMT->current.proj.rect[XHI] > GMT_CONV8_LIMIT) return (false);
2038 	if (y1 - GMT->current.proj.rect[YLO] < -GMT_CONV8_LIMIT || y0 - GMT->current.proj.rect[YHI] > GMT_CONV8_LIMIT) return (false);
2039 	if (x0 < GMT->current.proj.rect[XLO] && x1 > GMT->current.proj.rect[XHI]) {	/* Possibly a map jump but is it reasonable? */
2040 		/* What can happen for a small non-360 map is that points that approach being +180 degrees in longitude away and the
2041 		 * next point that is +181 will be seen as -179 degrees away and suddenly the x-coordinate jumps from very positive
2042 		 * to very negative (or the other way).  Before this attempt, the function would return true and give crossing lines
2043 		 * across the map.  Here we check if the change in x is a large (> 10x) multiple of the map width. This is likely
2044 		 * not to be fool-proof.  Works with current test scripts so we will give it a go.  Paul Wessel, 7/31/2014 */
2045 		if ((x1 - x0)/(GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]) > 10.0) return (false);
2046 	}
2047 	return (true);
2048 }
2049 
2050 /*! . */
gmtmap_wesn_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2051 GMT_LOCAL bool gmtmap_wesn_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2052 	/* Return true if either of the points (lon0,lat0) and (lon1,lat1) is inside (not on) the rectangular lon/lat boundaries */
2053 	if (lon0 > lon1) gmt_M_double_swap (lon0, lon1);
2054 	if (lat0 > lat1) gmt_M_double_swap (lat0, lat1);
2055 	if (lon1 - GMT->common.R.wesn[XLO] < -GMT_CONV8_LIMIT) {
2056 		lon0 += 360.0;
2057 		lon1 += 360.0;
2058 	}
2059 	else if (lon0 - GMT->common.R.wesn[XHI] > GMT_CONV8_LIMIT) {
2060 		lon0 -= 360.0;
2061 		lon1 -= 360.0;
2062 	}
2063 
2064 	if (lon1 - GMT->common.R.wesn[XLO] < -GMT_CONV8_LIMIT || lon0 - GMT->common.R.wesn[XHI] > GMT_CONV8_LIMIT) return (false);
2065 	if (lat1 - GMT->common.R.wesn[YLO] < -GMT_CONV8_LIMIT || lat0 - GMT->common.R.wesn[YHI] > GMT_CONV8_LIMIT) return (false);
2066 	return (true);
2067 }
2068 
2069 /*! . */
gmtmap_radial_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2070 GMT_LOCAL bool gmtmap_radial_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2071 	/* Dummy routine */
2072 	gmt_M_unused(GMT); gmt_M_unused(lon0); gmt_M_unused(lat0); gmt_M_unused(lon1); gmt_M_unused(lat1);
2073 	return (true);
2074 }
2075 
2076 /*! . */
gmtmap_genperg_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2077 GMT_LOCAL bool gmtmap_genperg_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2078 	/* Dummy routine */
2079 	gmt_M_unused(lon0); gmt_M_unused(lat0); gmt_M_unused(lon1); gmt_M_unused(lat1);
2080 	if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper_overlap: overlap called\n");
2081 	return (true);
2082 }
2083 
2084 /*! . */
gmtmap_genperw_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2085 GMT_LOCAL bool gmtmap_genperw_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2086 	bool out0, out1;
2087 	gmt_M_unused(lon0); gmt_M_unused(lat0); gmt_M_unused(lon1); gmt_M_unused(lat1);
2088 	/* Return true if either of the points (lon0,lat0) and (lon1,lat1) is inside (not on) the windowed genper boundary */
2089 	/* Check if point 1 is beyond horizon: */
2090 	out0 = gmtmap_radial_outside (GMT, lon0, lat0);		/* true if point 0 is beyond the horizon */
2091 	if (!out0) out0 = gmtmap_rect_outside (GMT, lon0, lat0);	/* true if point 0 is beyond the map box */
2092 	out1 = gmtmap_radial_outside (GMT, lon1, lat1);		/* true if point 1 is beyond the horizon */
2093 	if (!out1) out1 = gmtmap_rect_outside (GMT, lon1, lat1);	/* true if point 1 is beyond the map box */
2094 	return (out0 != out1);
2095 }
2096 
2097 #define GMTMAP_N_STEPS 500	/* To avoid repeating 500 in many places */
2098 
2099 /*! . */
gmtmap_xy_search(struct GMT_CTRL * GMT,double * x0,double * x1,double * y0,double * y1,double w0,double e0,double s0,double n0)2100 GMT_LOCAL void gmtmap_xy_search (struct GMT_CTRL *GMT, double *x0, double *x1, double *y0, double *y1, double w0, double e0, double s0, double n0) {
2101 	unsigned int i, j;
2102 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, w, s, x = 0.0, y = 0.0, dlon, dlat;
2103 
2104 	/* Find min/max forward values */
2105 
2106 	xmax = ymax = -DBL_MAX;
2107 	xmin = ymin = DBL_MAX;
2108 	dlon = fabs (e0 - w0) / GMTMAP_N_STEPS;
2109 	dlat = fabs (n0 - s0) / GMTMAP_N_STEPS;
2110 
2111 	for (i = 0; i <= GMTMAP_N_STEPS; i++) {
2112 		w = w0 + i * dlon;
2113 		(*GMT->current.proj.fwd) (GMT, w, s0, &x, &y);
2114 		if (x < xmin) xmin = x;
2115 		if (y < ymin) ymin = y;
2116 		if (x > xmax) xmax = x;
2117 		if (y > ymax) ymax = y;
2118 		(*GMT->current.proj.fwd) (GMT, w, n0, &x, &y);
2119 		if (x < xmin) xmin = x;
2120 		if (y < ymin) ymin = y;
2121 		if (x > xmax) xmax = x;
2122 		if (y > ymax) ymax = y;
2123 	}
2124 	for (j = 0; j <= GMTMAP_N_STEPS; j++) {
2125 		s = s0 + j * dlat;
2126 		(*GMT->current.proj.fwd) (GMT, w0, s, &x, &y);
2127 		if (x < xmin) xmin = x;
2128 		if (y < ymin) ymin = y;
2129 		if (x > xmax) xmax = x;
2130 		if (y > ymax) ymax = y;
2131 		(*GMT->current.proj.fwd) (GMT, e0, s, &x, &y);
2132 		if (x < xmin) xmin = x;
2133 		if (y < ymin) ymin = y;
2134 		if (x > xmax) xmax = x;
2135 		if (y > ymax) ymax = y;
2136 	}
2137 
2138 	*x0 = xmin;	*x1 = xmax;	*y0 = ymin;	*y1 = ymax;
2139 }
2140 
2141 /*! . */
gmtmap_adjust_panel_for_gaps(struct GMT_CTRL * GMT,struct GMT_SUBPLOT * P)2142 GMT_LOCAL void gmtmap_adjust_panel_for_gaps (struct GMT_CTRL *GMT, struct GMT_SUBPLOT *P) {
2143 	/* Checks the caps array and makes adjustment to w/h and adjusts the x/y origin */
2144 	gmt_M_unused (GMT);
2145 
2146 	/* Shrink the available panel dimensions based on the gaps */
2147 	P->w -= (P->gap[XLO] + P->gap[XHI]);
2148 	P->h -= (P->gap[YLO] + P->gap[YHI]);
2149 }
2150 
2151 /*! . */
gmtmap_setxy(struct GMT_CTRL * GMT,double xmin,double xmax,double ymin,double ymax)2152 GMT_LOCAL void gmtmap_setxy (struct GMT_CTRL *GMT, double xmin, double xmax, double ymin, double ymax) {
2153 	/* Set x/y parameters */
2154 	struct GMT_SUBPLOT *P = &(GMT->current.plot.panel);	/* P->active == 1 if a subplot */
2155 	struct GMT_INSET *I = &(GMT->current.plot.inset);	/* I->active == 1 if an inset */
2156 	unsigned int no_scaling = P->no_scaling;
2157 	bool update_parameters = false;
2158 	double fw, fh, fx, fy, w, h;
2159 
2160 	/* Set up the original min/max values, the rectangular map dimensionsm and the projection offset */
2161 	GMT->current.proj.rect_m[XLO] = xmin;	GMT->current.proj.rect_m[XHI] = xmax;	/* This is in original meters */
2162 	GMT->current.proj.rect_m[YLO] = ymin;	GMT->current.proj.rect_m[YHI] = ymax;
2163 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Projected values in meters: %g %g %g %g\n", xmin, xmax, ymin, ymax);
2164 	GMT->current.proj.rect[XHI] = (xmax - xmin) * GMT->current.proj.scale[GMT_X];
2165 	GMT->current.proj.rect[YHI] = (ymax - ymin) * GMT->current.proj.scale[GMT_Y];
2166 	GMT->current.proj.origin[GMT_X] = -xmin * GMT->current.proj.scale[GMT_X];
2167 	GMT->current.proj.origin[GMT_Y] = -ymin * GMT->current.proj.scale[GMT_Y];
2168 	if (GMT->current.proj.obl_flip) {
2169 		GMT->current.proj.origin[GMT_Y] = ymax * GMT->current.proj.scale[GMT_Y];
2170 		GMT->current.proj.scale[GMT_Y] = -GMT->current.proj.scale[GMT_Y];
2171 	}
2172 
2173 	if (!strncmp (GMT->init.module_name, "inset", 5U))
2174 		no_scaling = 1;	/* Don't scale yet if we are calling inset begin (inset end would come here too but not affected since no mapping done by that module) */
2175 
2176 	w = GMT->current.proj.rect[XHI];	h = GMT->current.proj.rect[YHI];
2177 
2178 	/* Check inset first since an inset may be inside a subplot but there are no subplots inside an inset */
2179 	if (I->active && no_scaling == 0) {	/* Must rescale to fit inside the inset dimensions and set dx,dy for centering */
2180 		fw = w / I->w;	fh = h / I->h;
2181 		if (gmt_M_is_geographic (GMT, GMT_IN) || GMT->current.proj.projection == GMT_POLAR || GMT->current.proj.gave_map_width == 0) {	/* Giving -Jx will end up here with map projections */
2182 			if (fw > fh) {	/* Wider than taller given inset dims; adjust width to fit exactly and set dy for centering */
2183 				fx = fy = 1.0 / fw;	I->dx = 0.0;	I->dy = 0.5 * (I->h - h * fy);
2184 			}
2185 			else {	/* Taller than wider given inset dims; adjust height to fit exactly and set dx for centering */
2186 				fx = fy = 1.0 / fh;	I->dy = 0.0;	I->dx = 0.5 * (I->w - w * fx);
2187 			}
2188 		}
2189 		else {	/* Cartesian is scaled independently to fit the inset */
2190 			fx = 1.0 / fw;	fy = 1.0 / fh;	I->dx = I->dy = 0.0;
2191 		}
2192 		update_parameters = true;
2193 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Rescaling map for inset by factors fx = %g fy = %g dx = %g dy = %g\n", fx, fy, I->dx, I->dy);
2194 	}
2195 	else if (P->active && no_scaling == 0)	{	/* Must rescale to fit inside subplot dimensions and set dx,dy for centering */
2196 		gmtmap_adjust_panel_for_gaps (GMT, P);	/* Deal with any gaps requested via subplot -C: shrink w/h and adjust origin */
2197 		fw = w / P->w;	fh = h / P->h;
2198 		if (gmt_M_is_geographic (GMT, GMT_IN) || GMT->current.proj.projection == GMT_POLAR || GMT->current.proj.gave_map_width == 0) {	/* Giving -Jx will end up here with map projections */
2199 			if (fw > fh) {	/* Wider than taller given panel dims; adjust width to fit exactly */
2200 				fx = fy = 1.0 / fw;	P->dx = 0.0;	P->dy = 0.5 * (P->h - h * fy);
2201 			}
2202 			else {	/* Taller than wider given panel dims; adjust height to fit exactly and set dx for centering */
2203 				fx = fy = 1.0 / fh;	P->dy = 0.0;	P->dx = 0.5 * (P->w - w * fx);
2204 			}
2205 		}
2206 		else {	/* Cartesian is scaled independently to fit the subplot fully */
2207 			fx = 1.0 / fw;	fy = 1.0 / fh;	P->dx = P->dy = 0.0;
2208 		}
2209 		update_parameters = true;
2210 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Rescaling map for subplot by factors fx = %g fy = %g dx = %g dy = %g\n", fx, fy, P->dx, P->dy);
2211 		if (gmt_M_is_rect_graticule (GMT) && P->parallel) {
2212 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Force parallel latitude annotations\n");
2213 			strcpy (GMT->current.setting.map_annot_ortho, "");	/* All annotations will be parallel to axes */
2214 			GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_LAT_PARALLEL;	/* Plot latitude parallel to frame for geo maps */
2215 		}
2216 	}
2217 	if (update_parameters) {	/* Scale the parameters due to inset or subplot adjustments */
2218 		/* Update all projection parameters given the reduction factors fx, fy */
2219 		GMT->current.proj.scale[GMT_X] *= fx;
2220 		GMT->current.proj.scale[GMT_Y] *= fy;
2221 		GMT->current.proj.w_r *= fx;	/* Only matter for geographic where fx = fy anyway */
2222 		GMT->current.proj.rect[XHI] = (xmax - xmin) * GMT->current.proj.scale[GMT_X];
2223 		GMT->current.proj.rect[YHI] = (ymax - ymin) * GMT->current.proj.scale[GMT_Y];
2224 		GMT->current.proj.origin[GMT_X] = -xmin * GMT->current.proj.scale[GMT_X];
2225 		GMT->current.proj.origin[GMT_Y] = -ymin * GMT->current.proj.scale[GMT_Y];
2226 	}
2227 }
2228 
2229 /*! . */
gmtmap_setinfo(struct GMT_CTRL * GMT,double xmin,double xmax,double ymin,double ymax,double scl)2230 GMT_LOCAL void gmtmap_setinfo (struct GMT_CTRL *GMT, double xmin, double xmax, double ymin, double ymax, double scl) {
2231 	/* Set [and rescale] parameters */
2232 	double factor = 1.0, w, h;
2233 
2234 	if (GMT->current.map.is_world && doubleAlmostEqualZero (xmax, xmin)) {	/* Safety valve for cases when w & e both project to the same side due to round-off */
2235 		xmax = MAX (fabs (xmin), fabs (xmax));
2236 		xmin =-xmax;
2237 	}
2238 	w = (xmax - xmin) * GMT->current.proj.scale[GMT_X];
2239 	h = (ymax - ymin) * GMT->current.proj.scale[GMT_Y];
2240 
2241 	if (GMT->current.proj.gave_map_width == 1)	/* Must rescale to given width */
2242 		factor = scl / w;
2243 	else if (GMT->current.proj.gave_map_width == 2)	/* Must rescale to given height */
2244 		factor = scl / h;
2245 	else if (GMT->current.proj.gave_map_width == 3)	/* Must rescale to max dimension */
2246 		factor = scl / MAX (w, h);
2247 	else if (GMT->current.proj.gave_map_width == 4)	/* Must rescale to min dimension */
2248 		factor = scl / MIN (w, h);
2249 	GMT->current.proj.scale[GMT_X] *= factor;
2250 	GMT->current.proj.scale[GMT_Y] *= factor;
2251 	GMT->current.proj.w_r *= factor;
2252 
2253 	if (GMT->current.proj.g_debug > 1) {
2254 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "xmin %7.3f xmax %7.3f ymin %7.4f ymax %7.3f scale %6.3f\n", xmin/1000, xmax/1000, ymin/1000, ymax/1000, scl);
2255 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gave_map_width %d w %9.4e h %9.4e factor %9.4e\n", GMT->current.proj.gave_map_width, w, h, factor);
2256 	}
2257 
2258 	gmtmap_setxy (GMT, xmin, xmax, ymin, ymax);
2259 }
2260 
2261 /*! . */
gmtmap_mean_radius(struct GMT_CTRL * GMT,double a,double f)2262 GMT_LOCAL double gmtmap_mean_radius (struct GMT_CTRL *GMT, double a, double f) {
2263 	double r = 0, b = a * (1 - f);
2264 
2265 	if (f == 0.0) return a;	/* Not that hard */
2266 
2267 	switch (GMT->current.setting.proj_mean_radius) {
2268 		case GMT_RADIUS_MEAN:
2269 			r = a * (1.0 - f / 3.0);
2270 			break;
2271 		case GMT_RADIUS_AUTHALIC:
2272 			r = sqrt (0.5 * a * a + 0.5 * b * b * atanh (GMT->current.proj.ECC) / GMT->current.proj.ECC);
2273 			break;
2274 		case GMT_RADIUS_VOLUMETRIC:
2275 			r = pow (a*a*b, 1.0/3.0);
2276 			break;
2277 		case GMT_RADIUS_MERIDIONAL:
2278 			r = pow (0.5 * (pow (a, 1.5) + pow (b, 1.5)), 2.0/3.0);
2279 			break;
2280 		case GMT_RADIUS_QUADRATIC:
2281 			r = 0.5 * sqrt (3.0 * a * a + b * b);
2282 			break;
2283 		default:	/* Cannot get here! Safety valve */
2284 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT mean radius type not recognized - defaulting to mean radius\n");
2285 			r = a * (1.0 - f / 3.0);
2286 			break;
2287 	}
2288 
2289 	return (r);
2290 }
2291 
2292 /*! . */
gmtmap_set_spherical(struct GMT_CTRL * GMT,bool notify)2293 GMT_LOCAL void gmtmap_set_spherical (struct GMT_CTRL *GMT, bool notify) {
2294 	/* Set up ellipsoid parameters using spherical approximation */
2295 
2296 	GMT->current.setting.ref_ellipsoid[GMT_N_ELLIPSOIDS - 1].eq_radius =
2297 		gmtmap_mean_radius (GMT, GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius, GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening);
2298 	GMT->current.setting.proj_ellipsoid = GMT_N_ELLIPSOIDS - 1;	/* Custom ellipsoid */
2299 	GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening = 0.0;
2300 	if (notify) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Spherical approximation used\n");
2301 	GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_NONE;	/* No lat swapping for spherical */
2302 
2303 	gmtlib_init_ellipsoid (GMT);
2304 }
2305 
2306 /*! . */
gmtmap_left_conic(struct GMT_CTRL * GMT,double y)2307 GMT_LOCAL double gmtmap_left_conic (struct GMT_CTRL *GMT, double y) {
2308 	double x_ws, y_ws, x_wn, y_wn, dy;
2309 
2310 	gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &x_ws, &y_ws);
2311 	gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &x_wn, &y_wn);
2312 	dy = y_wn - y_ws;
2313 	if (doubleAlmostEqualZero (y_wn, y_ws))
2314 		return (0.0);
2315 	return (x_ws + ((x_wn - x_ws) * (y - y_ws) / dy));
2316 }
2317 
2318 /*! . */
gmtmap_right_conic(struct GMT_CTRL * GMT,double y)2319 GMT_LOCAL double gmtmap_right_conic (struct GMT_CTRL *GMT, double y) {
2320 	double x_es, y_es, x_en, y_en, dy;
2321 
2322 	gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], &x_es, &y_es);
2323 	gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &x_en, &y_en);
2324 	dy = y_en - y_es;
2325 	if (doubleAlmostEqualZero (y_en, y_es))
2326 		return (GMT->current.map.width);
2327 	return (x_es - ((x_es - x_en) * (y - y_es) / dy));
2328 }
2329 
2330 /*! . */
gmtmap_left_rect(struct GMT_CTRL * GMT,double y)2331 GMT_LOCAL double gmtmap_left_rect (struct GMT_CTRL *GMT, double y) {
2332 	gmt_M_unused(GMT); gmt_M_unused(y);
2333 	return (0.0);
2334 }
2335 
2336 /*! . */
gmtmap_right_rect(struct GMT_CTRL * GMT,double y)2337 GMT_LOCAL double gmtmap_right_rect (struct GMT_CTRL *GMT, double y) {
2338 	gmt_M_unused(y);
2339 	return (GMT->current.map.width);
2340 }
2341 
2342 /*! . */
gmtmap_left_circle(struct GMT_CTRL * GMT,double y)2343 GMT_LOCAL double gmtmap_left_circle (struct GMT_CTRL *GMT, double y) {
2344 	y -= GMT->current.proj.origin[GMT_Y];
2345 	return (GMT->current.map.half_width - d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y));
2346 }
2347 
2348 /*! . */
gmtmap_right_circle(struct GMT_CTRL * GMT,double y)2349 GMT_LOCAL double gmtmap_right_circle (struct GMT_CTRL *GMT, double y) {
2350 	/* y -= GMT->current.proj.r; */
2351 	y -= GMT->current.proj.origin[GMT_Y];
2352 	return (GMT->current.map.half_width + d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y));
2353 }
2354 
2355 /*! . */
gmtmap_left_ellipse(struct GMT_CTRL * GMT,double y)2356 GMT_LOCAL double gmtmap_left_ellipse (struct GMT_CTRL *GMT, double y) {
2357 	/* Applies to Hammer and Mollweide only, where major axis = 2 * minor axis */
2358 
2359 	y = (y - GMT->current.proj.origin[GMT_Y]) / GMT->current.proj.w_r;	/* Fraction, relative to Equator */
2360 	return (GMT->current.map.half_width - 2.0 * GMT->current.proj.w_r * d_sqrt (1.0 - y * y));
2361 }
2362 
2363 /*! . */
gmtmap_right_ellipse(struct GMT_CTRL * GMT,double y)2364 GMT_LOCAL double gmtmap_right_ellipse (struct GMT_CTRL *GMT, double y) {
2365 	/* Applies to Hammer and Mollweide only, where major axis = 2 * minor axis */
2366 
2367 	y = (y - GMT->current.proj.origin[GMT_Y]) / GMT->current.proj.w_r;	/* Fraction, relative to Equator */
2368 	return (GMT->current.map.half_width + 2.0 * GMT->current.proj.w_r * d_sqrt (1.0 - y * y));
2369 }
2370 
2371 /*! . */
gmtmap_az_backaz_cartesian(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2372 GMT_LOCAL double gmtmap_az_backaz_cartesian (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2373 	/* Calculate azimuths or backazimuths.  Cartesian case.
2374 	 * First point is considered "Event" and second "Station".
2375 	 * Azimuth is direction from Station to Event.
2376 	 * BackAzimuth is direction from Event to Station */
2377 
2378 	double az, dx, dy;
2379 
2380 	if (baz) {	/* exchange point one and two */
2381 		gmt_M_double_swap (lonS, lonE);
2382 		gmt_M_double_swap (latS, latE);
2383 	}
2384 	dx = lonS - lonE;
2385 	dy = latS - latE;
2386 	az = (dx == 0.0 && dy == 0.0) ? GMT->session.d_NaN : 90.0 - atan2d (dy, dx);
2387 	if (az < 0.0) az += 360.0;
2388 	return (az);
2389 }
2390 
2391 /*! . */
gmtmap_az_backaz_cartesian_proj(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2392 GMT_LOCAL double gmtmap_az_backaz_cartesian_proj (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2393 	/* Calculate azimuths or backazimuths.  Cartesian case.
2394 	 * First point is considered "Event" and second "Station".
2395 	 * Azimuth is direction from Station to Event.
2396 	 * BackAzimuth is direction from Event to Station */
2397 
2398 	double az, dx, dy, xE, yE, xS, yS;
2399 
2400 	if (baz) {	/* exchange point one and two */
2401 		gmt_M_double_swap (lonS, lonE);
2402 		gmt_M_double_swap (latS, latE);
2403 	}
2404 	gmt_geo_to_xy (GMT, lonE, latE, &xE, &yE);
2405 	gmt_geo_to_xy (GMT, lonS, latS, &xS, &yS);
2406 	dx = xS - xE;
2407 	dy = yS - yE;
2408 	az = (dx == 0.0 && dy == 0.0) ? GMT->session.d_NaN : 90.0 - atan2d (dy, dx);
2409 	if (az < 0.0) az += 360.0;
2410 	return (az);
2411 }
2412 
2413 /*! . */
gmtmap_az_backaz_flatearth(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2414 GMT_LOCAL double gmtmap_az_backaz_flatearth (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2415 	/* Calculate azimuths or backazimuths.  Flat earth code.
2416 	 * First point is considered "Event" and second "Station".
2417 	 * Azimuth is direction from Station to Event.
2418 	 * BackAzimuth is direction from Event to Station */
2419 
2420 	double az, dx, dy, dlon;
2421 
2422 	if (baz) {	/* exchange point one and two */
2423 		gmt_M_double_swap (lonS, lonE);
2424 		gmt_M_double_swap (latS, latE);
2425 	}
2426 	gmt_M_set_delta_lon (lonE, lonS, dlon);
2427 	dx = dlon * cosd (0.5 * (latS + latE));
2428 	dy = latS - latE;
2429 	az = (dx == 0.0 && dy == 0.0) ? GMT->session.d_NaN : 90.0 - atan2d (dy, dx);
2430 	if (az < 0.0) az += 360.0;
2431 	return (az);
2432 }
2433 
2434 /*! . */
gmtmap_az_backaz_sphere(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2435 GMT_LOCAL double gmtmap_az_backaz_sphere (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2436 	/* Calculate azimuths or backazimuths.  Spherical code.
2437 	 * First point is considered "Event" and second "Station".
2438 	 * Azimuth is direction from Station to Event.
2439 	 * BackAzimuth is direction from Event to Station */
2440 
2441 	double az, sin_yS, cos_yS, sin_yE, cos_yE, sin_dlon, cos_dlon;
2442 	gmt_M_unused(GMT);
2443 
2444 	if (baz) {	/* exchange point one and two */
2445 		gmt_M_double_swap (lonS, lonE);
2446 		gmt_M_double_swap (latS, latE);
2447 	}
2448 	sincosd (latS, &sin_yS, &cos_yS);
2449 	sincosd (latE, &sin_yE, &cos_yE);
2450 	sincosd (lonS - lonE, &sin_dlon, &cos_dlon);
2451 	az = atan2d (cos_yS * sin_dlon, cos_yE * sin_yS - sin_yE * cos_yS * cos_dlon);
2452 	if (az < 0.0) az += 360.0;
2453 	return (az);
2454 }
2455 
2456 #define VINCENTY_EPS		5e-14
2457 #define VINCENTY_MAX_ITER	50
2458 /*! . */
gmtmap_az_backaz_vincenty(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool back_az)2459 GMT_LOCAL double gmtmap_az_backaz_vincenty (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool back_az) {
2460 	/* Translation of NGS FORTRAN code for determination of true distance
2461 	** and respective forward and back azimuths between two points on the
2462 	** ellipsoid.  Good for any pair of points that are not antipodal.
2463 	**
2464 	**      INPUT
2465 	**	latS, lonS -- latitude and longitude of first point in radians.
2466 	**	latE, lonE -- latitude and longitude of second point in radians.
2467 	**
2468 	**	OUTPUT
2469 	**  	faz -- azimuth from first point to second in radians clockwise from North.
2470 	**	baz -- azimuth from second point back to first point.
2471 	**	bool back_az controls which is returned
2472 	** Modified by P.W. from: http://article.gmane.org/gmane.comp.gis.proj-4.devel/3478
2473 	*/
2474 	int n_iter = 0;
2475 	double az, c, d, e, r, f, d_lon, dx, x, y, sa, cx, cy, cz, sx, sy, c2a, cu1, cu2, su1, tu1, tu2, ts, baz, faz;
2476 
2477 	f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
2478 	r = 1.0 - f;
2479 	tu1 = r * tand (latS);
2480 	tu2 = r * tand (latE);
2481 	cu1 = 1.0 / sqrt (tu1 * tu1 + 1.0);
2482 	su1 = cu1 * tu1;
2483 	cu2 = 1.0 / sqrt (tu2 * tu2 + 1.0);
2484 	ts  = cu1 * cu2;
2485 	baz = ts * tu2;
2486 	faz = baz * tu1;
2487 	gmt_M_set_delta_lon (lonS, lonE, d_lon);
2488 	if (gmt_M_is_zero (d_lon) && doubleAlmostEqualZero (latS, latE)) return GMT->session.d_NaN;
2489 	x = dx = D2R * d_lon;
2490 	do {
2491 		n_iter++;
2492 		sincos (x, &sx, &cx);
2493 		tu1 = cu2 * sx;
2494 		tu2 = baz - su1 * cu2 * cx;
2495 		sy = sqrt (tu1 * tu1 + tu2 * tu2);
2496 		cy = ts * cx + faz;
2497 		y = atan2 (sy, cy);
2498 		sa = ts * sx / sy;
2499 		c2a = -sa * sa + 1.0;
2500 		cz = faz + faz;
2501 		if (c2a > 0.0) cz = -cz / c2a + cy;
2502 		e = cz * cz * 2.0 - 1.0;
2503 		c = ((c2a * -3.0 + 4.0) * f + 4.0) * c2a * f / 16.0;
2504 		d = x;
2505 		x = ((e * cy * c + cz) * sy * c + y) * sa;
2506 		x = (1.0 - c) * x * f + dx;
2507 	} while (fabs (d - x) > VINCENTY_EPS && n_iter <= VINCENTY_MAX_ITER);
2508 	if (n_iter > VINCENTY_MAX_ITER) {
2509 		GMT->current.proj.n_geodesic_approx++;	/* Count inaccurate results */
2510 		if (GMT->current.proj.n_geodesic_approx == 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "At least one near- or actual antipodal points encountered. Precision may be reduced slightly.\n");
2511 	}
2512 	GMT->current.proj.n_geodesic_calls++;
2513 	/* To give the same sense of results as all other codes, we must basically swap baz and faz; here done in the ? test */
2514 	az = (back_az) ? atan2 (tu1, tu2) : atan2 (cu1 * sx, baz * cx - su1 * cu2) + M_PI;
2515 	return (R2D * az);
2516 }
2517 
2518 /*! . */
gmtmap_az_backaz_rudoe(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2519 GMT_LOCAL double gmtmap_az_backaz_rudoe (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2520 	/* Calculate azimuths or backazimuths for geodesics using geocentric latitudes.
2521 	 * First point is considered "Event" and second "Station".
2522 	 * Azimuth is direction from Station to Event.
2523 	 * BackAzimuth is direction from Event to Station */
2524 
2525 	double az, a, b, c, d, e, f, g, h, a1, b1, c1, d1, e1, f1, g1, h1, thg, ss, sc;
2526 
2527 	/* Equations are unstable for latitudes of exactly 0 degrees. */
2528 	if (latE == 0.0) latE = 1.0e-08;
2529 	if (latS == 0.0) latS = 1.0e-08;
2530 
2531 	/* Must convert from geographic to geocentric coordinates in order
2532 	 * to use the spherical trig equations.  This requires a latitude
2533 	 * correction given by: 1-ECC2=1-2*f + f*f = GMT->current.proj.one_m_ECC2
2534 	 */
2535 
2536 	thg = atan (GMT->current.proj.one_m_ECC2 * tand (latE));
2537 	sincos (thg, &c, &f);		f = -f;
2538 	sincosd (lonE, &d, &e);		e = -e;
2539 	a = f * e;
2540 	b = -f * d;
2541 	g = -c * e;
2542 	h = c * d;
2543 
2544 	/* Calculating some trig constants. */
2545 
2546 	thg = atan (GMT->current.proj.one_m_ECC2 * tand (latS));
2547 	sincos (thg, &c1, &f1);		f1 = -f1;
2548 	sincosd (lonS, &d1, &e1);	e1 = -e1;
2549 	a1 = f1 * e1;
2550 	b1 = -f1 * d1;
2551 	g1 = -c1 * e1;
2552 	h1 = c1 * d1;
2553 
2554 	/* Spherical trig relationships used to compute angles. */
2555 
2556 	if (baz) {	/* Get Backazimuth */
2557 		ss = pow(a-d1,2.0) + pow(b-e1,2.0) + c * c - 2.0;
2558 		sc = pow(a-g1,2.0) + pow(b-h1,2.0) + pow(c-f1,2.0) - 2.0;
2559 	}
2560 	else {		/* Get Azimuth */
2561 		ss = pow(a1-d, 2.0) + pow(b1-e, 2.0) + c1 * c1 - 2.0;
2562 		sc = pow(a1-g, 2.0) + pow(b1-h, 2.0) + pow(c1-f, 2.0) - 2.0;
2563 	}
2564 	az = atan2d (ss,sc);
2565 	if (az < 0.0) az += 360.0;
2566 	return (az);
2567 }
2568 
2569 /*
2570  *	TRANSFORMATION ROUTINES FOR THE LINEAR PROJECTION (GMT_LINEAR)
2571  */
2572 
2573 /*! . */
gmtmap_linearxy(struct GMT_CTRL * GMT,double x,double y,double * x_i,double * y_i)2574 GMT_LOCAL void gmtmap_linearxy (struct GMT_CTRL *GMT, double x, double y, double *x_i, double *y_i) {
2575 	/* Transform both x and y linearly */
2576 	(*GMT->current.proj.fwd_x) (GMT, x, x_i);
2577 	(*GMT->current.proj.fwd_y) (GMT, y, y_i);
2578 }
2579 
2580 /*! . */
gmtmap_ilinearxy(struct GMT_CTRL * GMT,double * x,double * y,double x_i,double y_i)2581 GMT_LOCAL void gmtmap_ilinearxy (struct GMT_CTRL *GMT, double *x, double *y, double x_i, double y_i) {
2582 	/* Inversely transform both x and y linearly */
2583 	(*GMT->current.proj.inv_x) (GMT, x, x_i);
2584 	(*GMT->current.proj.inv_y) (GMT, y, y_i);
2585 }
2586 
2587 GMT_LOCAL unsigned int gmtmap_wrap_around_check_tm (struct GMT_CTRL *GMT, double *angle, double last_x, double last_y, double this_x, double this_y, double *xx, double *yy, unsigned int *sides);
2588 
2589 /*! . */
gmtmap_init_linear(struct GMT_CTRL * GMT,bool * search)2590 GMT_LOCAL int gmtmap_init_linear (struct GMT_CTRL *GMT, bool *search) {
2591 	bool positive;
2592 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2593 
2594 	*search = false;
2595 	GMT->current.map.left_edge  = &gmtmap_left_rect;
2596 	GMT->current.map.right_edge = &gmtmap_right_rect;
2597 	GMT->current.proj.fwd = &gmtmap_linearxy;
2598 	GMT->current.proj.inv = &gmtmap_ilinearxy;
2599 	if (gmt_M_x_is_lon (GMT, GMT_IN)) {	/* x is longitude */
2600 		GMT->current.proj.central_meridian = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
2601 		GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2602 	}
2603 	else if (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_ABSTIME && GMT->current.io.cycle_col == GMT_X) {
2604 		GMT->current.map.lon_wrap_range = GMT->current.io.cycle_range;
2605 		GMT->current.map.is_world = doubleAlmostEqual (fabs(GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]), GMT->current.io.cycle_range);
2606 		GMT->current.map.lat_wrap = false;
2607 	}
2608 	else if (gmt_M_type (GMT, GMT_IN, GMT_Y) == GMT_IS_ABSTIME && GMT->current.io.cycle_col == GMT_Y) {
2609 		GMT->current.map.lat_wrap_range = GMT->current.io.cycle_range;
2610 		GMT->current.map.is_world = doubleAlmostEqual (fabs(GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]), GMT->current.io.cycle_range);
2611 		GMT->current.map.wrap_around_check = &gmtmap_wrap_around_check_tm;
2612 		GMT->current.map.lon_wrap = false;
2613 	}
2614 	else
2615 		GMT->current.map.lon_wrap = GMT->current.map.lat_wrap = false;
2616 	if (gmt_M_y_is_lon (GMT, GMT_IN)) {	/* y is longitude */
2617 		GMT->current.proj.central_meridian = 0.5 * (GMT->common.R.wesn[YLO] + GMT->common.R.wesn[YHI]);
2618 	}
2619 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.pars[0];
2620 	GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
2621 	GMT->current.proj.xyz_pos[GMT_X] = (GMT->current.proj.scale[GMT_X] >= 0.0);	/* False if user wants x to increase left */
2622 	GMT->current.proj.xyz_pos[GMT_Y] = (GMT->current.proj.scale[GMT_Y] >= 0.0);	/* False if user wants y to increase down */
2623 	switch ( (GMT->current.proj.xyz_projection[GMT_X]%3)) {	/* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2624 		case GMT_LINEAR:	/* Regular scaling */
2625 			if (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_ABSTIME && GMT->current.proj.xyz_projection[GMT_X] != GMT_TIME && !GMT->current.io.cycle_operator) {
2626 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Option -JX|x: Your x-column contains absolute time but -JX|x...T was not specified!\n");
2627 				GMT->current.proj.xyz_projection[GMT_X] = GMT_TIME;
2628 			}
2629 			GMT->current.proj.fwd_x = ((gmt_M_x_is_lon (GMT, GMT_IN)) ? &gmtproj_translind  : &gmtlib_translin);
2630 			GMT->current.proj.inv_x = ((gmt_M_x_is_lon (GMT, GMT_IN)) ? &gmtproj_itranslind : &gmtlib_itranslin);
2631 			if (GMT->current.proj.xyz_pos[GMT_X]) {
2632 				(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmin);
2633 				(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmax);
2634 			}
2635 			else {
2636 				(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmin);
2637 				(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmax);
2638 			}
2639 			break;
2640 		case GMT_LOG10:	/* Log10 transformation */
2641 			if (GMT->common.R.wesn[XLO] <= 0.0 || GMT->common.R.wesn[XHI] <= 0.0) {
2642 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -JX|x:  Limits must be positive for log10 option\n");
2643 				return GMT_PROJECTION_ERROR;
2644 			}
2645 			xmin = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XLO]) : d_log10 (GMT, GMT->common.R.wesn[XHI]);
2646 			xmax = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XHI]) : d_log10 (GMT, GMT->common.R.wesn[XLO]);
2647 			GMT->current.proj.fwd_x = &gmtproj_translog10;
2648 			GMT->current.proj.inv_x = &gmtproj_itranslog10;
2649 			break;
2650 		case GMT_POW:	/* x^y transformation */
2651 			GMT->current.proj.xyz_pow[GMT_X] = GMT->current.proj.pars[2];
2652 			GMT->current.proj.xyz_ipow[GMT_X] = 1.0 / GMT->current.proj.pars[2];
2653 			positive = !((GMT->current.proj.xyz_pos[GMT_X] + (GMT->current.proj.xyz_pow[GMT_X] > 0.0)) % 2);
2654 			xmin = (positive) ? pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]);
2655 			xmax = (positive) ? pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]);
2656 			GMT->current.proj.fwd_x = &gmtproj_transpowx;
2657 			GMT->current.proj.inv_x = &gmtproj_itranspowx;
2658 			break;
2659 	}
2660 	switch (GMT->current.proj.xyz_projection[GMT_Y]%3) {	/* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2661 		case GMT_LINEAR:	/* Regular scaling */
2662 			if (gmt_M_type (GMT, GMT_IN, GMT_Y) == GMT_IS_ABSTIME && GMT->current.proj.xyz_projection[GMT_Y] != GMT_TIME && !GMT->current.io.cycle_operator) {
2663 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Option -JX|x:  Your y-column contains absolute time but -JX|x...T was not specified!\n");
2664 				GMT->current.proj.xyz_projection[GMT_Y] = GMT_TIME;
2665 			}
2666 			GMT->current.proj.fwd_y = ((gmt_M_y_is_lon (GMT, GMT_IN)) ? &gmtproj_translind  : &gmtlib_translin);
2667 			GMT->current.proj.inv_y = ((gmt_M_y_is_lon (GMT, GMT_IN)) ? &gmtproj_itranslind : &gmtlib_itranslin);
2668 			if (GMT->current.proj.xyz_pos[GMT_Y]) {
2669 				(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymin);
2670 				(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymax);
2671 			}
2672 			else {
2673 				(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymin);
2674 				(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymax);
2675 			}
2676 			break;
2677 		case GMT_LOG10:	/* Log10 transformation */
2678 			if (GMT->common.R.wesn[YLO] <= 0.0 || GMT->common.R.wesn[YHI] <= 0.0) {
2679 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -JX|x:  Limits must be positive for log10 option\n");
2680 				return GMT_PROJECTION_ERROR;
2681 			}
2682 			ymin = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YLO]) : d_log10 (GMT, GMT->common.R.wesn[YHI]);
2683 			ymax = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YHI]) : d_log10 (GMT, GMT->common.R.wesn[YLO]);
2684 			GMT->current.proj.fwd_y = &gmtproj_translog10;
2685 			GMT->current.proj.inv_y = &gmtproj_itranslog10;
2686 			break;
2687 		case GMT_POW:	/* x^y transformation */
2688 			GMT->current.proj.xyz_pow[GMT_Y] = GMT->current.proj.pars[3];
2689 			GMT->current.proj.xyz_ipow[GMT_Y] = 1.0 / GMT->current.proj.pars[3];
2690 			positive = !((GMT->current.proj.xyz_pos[GMT_Y] + (GMT->current.proj.xyz_pow[GMT_Y] > 0.0)) % 2);
2691 			ymin = (positive) ? pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]);
2692 			ymax = (positive) ? pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]);
2693 			GMT->current.proj.fwd_y = &gmtproj_transpowy;
2694 			GMT->current.proj.inv_y = &gmtproj_itranspowy;
2695 	}
2696 
2697 	/* Was given axes length instead of scale? */
2698 
2699 	if (GMT->current.proj.compute_scale[GMT_X]) GMT->current.proj.scale[GMT_X] /= fabs (xmin - xmax);
2700 	if (GMT->current.proj.compute_scale[GMT_Y]) GMT->current.proj.scale[GMT_Y] /= fabs (ymin - ymax);
2701 
2702 	/* If either is zero, adjust width or height to the other */
2703 
2704 	if (GMT->current.proj.scale[GMT_X] == 0) {	/* Must redo x-scaling by using y-scale */
2705 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.autoscl[GMT_X] * GMT->current.proj.scale[GMT_Y];
2706 		if (GMT->current.proj.autoscl[GMT_X] == -1) GMT->current.proj.xyz_pos[GMT_X] = !GMT->current.proj.xyz_pos[GMT_Y];
2707 		switch ( (GMT->current.proj.xyz_projection[GMT_X]%3)) {	/* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2708 			case GMT_LINEAR:	/* Regular scaling */
2709 				if (GMT->current.proj.xyz_pos[GMT_X]) {
2710 					(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmin);
2711 					(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmax);
2712 				}
2713 				else {
2714 					(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmin);
2715 					(*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmax);
2716 				}
2717 				break;
2718 			case GMT_LOG10:	/* Log10 transformation */
2719 				xmin = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XLO]) : d_log10 (GMT, GMT->common.R.wesn[XHI]);
2720 				xmax = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XHI]) : d_log10 (GMT, GMT->common.R.wesn[XLO]);
2721 				break;
2722 			case GMT_POW:	/* x^y transformation */
2723 				positive = !((GMT->current.proj.xyz_pos[GMT_X] + (GMT->current.proj.xyz_pow[GMT_X] > 0.0)) % 2);
2724 				xmin = (positive) ? pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]);
2725 				xmax = (positive) ? pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]);
2726 				break;
2727 		}
2728 		GMT->current.proj.pars[0] = GMT->current.proj.scale[GMT_X] * fabs (xmin - xmax);
2729 	}
2730 	else if (GMT->current.proj.scale[GMT_Y] == 0) {	/* Must redo y-scaling by using x-scale */
2731 		GMT->current.proj.scale[GMT_Y] = GMT->current.proj.autoscl[GMT_Y] * GMT->current.proj.scale[GMT_X];
2732 		if (GMT->current.proj.autoscl[GMT_Y] == -1) GMT->current.proj.xyz_pos[GMT_Y] = !GMT->current.proj.xyz_pos[GMT_X];
2733 		switch (GMT->current.proj.xyz_projection[GMT_Y]%3) {	/* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2734 			case GMT_LINEAR:	/* Regular scaling */
2735 				if (GMT->current.proj.xyz_pos[GMT_Y]) {
2736 					(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymin);
2737 					(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymax);
2738 				}
2739 				else {
2740 					(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymin);
2741 					(*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymax);
2742 				}
2743 				break;
2744 			case GMT_LOG10:	/* Log10 transformation */
2745 				ymin = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YLO]) : d_log10 (GMT, GMT->common.R.wesn[YHI]);
2746 				ymax = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YHI]) : d_log10 (GMT, GMT->common.R.wesn[YLO]);
2747 				break;
2748 			case GMT_POW:	/* x^y transformation */
2749 				positive = !((GMT->current.proj.xyz_pos[GMT_Y] + (GMT->current.proj.xyz_pow[GMT_Y] > 0.0)) % 2);
2750 				ymin = (positive) ? pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]);
2751 				ymax = (positive) ? pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]);
2752 		}
2753 		GMT->current.proj.pars[1] = GMT->current.proj.scale[GMT_Y] * fabs (ymin - ymax);
2754 	}
2755 
2756 	/* This override ensures that when using -J[x|X]...d degrees work as meters */
2757 
2758 	GMT->current.proj.M_PR_DEG = 1.0;
2759 	GMT->current.proj.KM_PR_DEG = GMT->current.proj.M_PR_DEG / METERS_IN_A_KM;
2760 
2761 	gmtmap_setxy (GMT, xmin, xmax, ymin, ymax);
2762 	if (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_LON) {	/* Using linear projection with longitudes */
2763 		GMT->current.map.outside = &gmtmap_wesn_outside;
2764 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
2765 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
2766 		GMT->current.map.clip = &map_wesn_clip;
2767 	}
2768 	else {
2769 		GMT->current.proj.VE = GMT->current.proj.scale[GMT_Y] / GMT->current.proj.scale[GMT_X];
2770 		if (GMT->current.proj.VE < 1.0)
2771 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Linear projection implies x-axis distance exaggeration relative to the y-axis by a factor of %g\n", GMT->current.proj.VE);
2772 		else if (GMT->current.proj.VE > 1.0)
2773 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Linear projection implies y-axis distance exaggeration relative to the x-axis by a factor of %g\n", GMT->current.proj.VE);
2774 
2775 		GMT->current.map.outside = &gmtmap_rect_outside;
2776 		GMT->current.map.crossing = &gmtmap_rect_crossing;
2777 		GMT->current.map.overlap = &gmtmap_cartesian_overlap;
2778 		GMT->current.map.clip = &gmtmap_rect_clip;
2779 	}
2780 	GMT->current.map.n_lat_nodes = 2;
2781 	GMT->current.map.n_lon_nodes = 3;	/* > 2 to avoid map-jumps */
2782 	GMT->current.map.frame.check_side = true;
2783 	GMT->current.map.frame.horizontal = 1;
2784 	GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2785 
2786 	return (GMT_NOERROR);
2787 }
2788 
2789 /*!
2790  *	TRANSFORMATION ROUTINES FOR POLAR (theta,r) PROJECTION (GMT_POLAR)
2791  */
gmtmap_init_polar(struct GMT_CTRL * GMT,bool * search)2792 GMT_LOCAL int gmtmap_init_polar (struct GMT_CTRL *GMT, bool *search) {
2793 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2794 
2795 	*search = false;
2796 	gmtproj_vpolar (GMT, GMT->current.proj.pars[1]);
2797 	if (GMT->current.proj.angle_kind == GMT_IS_LAT && (GMT->common.R.wesn[XLO] < -90.0 || GMT->common.R.wesn[XHI] > 90.0)) {
2798 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R:  Your +ky modifier for -Jp|P implies angles must be contained in the -90/+90 range (latitudes)\n");
2799 		return GMT_PROJECTION_ERROR;
2800 	}
2801 	if (GMT->current.proj.flip) {	/* Check restrictions */
2802 		if (GMT->common.R.wesn[YLO] < 0.0 || GMT->common.R.wesn[YHI] > GMT->current.proj.flip_radius) {
2803 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "-JP...+f requires s >= 0 and n <= %g!\n", GMT->current.proj.flip_radius);
2804 			return GMT_PROJECTION_ERROR;
2805 		}
2806 		if (doubleAlmostEqual (GMT->common.R.wesn[YHI], GMT->current.proj.flip_radius) && gmt_M_is_zero (GMT->current.proj.radial_offset))
2807 			GMT->current.proj.edge[2] = false;
2808 	}
2809 	else {
2810 		if (gmt_M_is_zero (GMT->common.R.wesn[YLO]) && gmt_M_is_zero (GMT->current.proj.radial_offset))
2811 			GMT->current.proj.edge[0] = false;
2812 	}
2813 	if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
2814 	GMT->current.map.left_edge = &gmtmap_left_circle;
2815 	GMT->current.map.right_edge = &gmtmap_right_circle;
2816 	GMT->current.proj.fwd = &gmtproj_polar;
2817 	GMT->current.proj.inv = &gmtproj_ipolar;
2818 	GMT->current.map.is_world = false;	/* There is no wrapping around here */
2819 	gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
2820 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[0];
2821 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[0]);
2822 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
2823 	/* GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI]; */
2824 	GMT->current.proj.r = GMT->current.proj.scale[GMT_Y] * GMT->common.R.wesn[YHI];
2825 	GMT->current.map.outside = &gmtmap_polar_outside;
2826 	GMT->current.map.crossing = &gmtmap_wesn_crossing;
2827 	GMT->current.map.overlap = &gmtmap_wesn_overlap;
2828 	GMT->current.map.clip = &map_wesn_clip;
2829 	GMT->current.map.frame.horizontal = 1;
2830 	if (!GMT->current.proj.got_elevations) GMT->current.plot.r_theta_annot = true;	/* Special labeling case (see gmtlib_get_annot_label) */
2831 	GMT->current.map.n_lat_nodes = 2;
2832 	GMT->current.map.meridian_straight = 1;
2833 
2834 	return (GMT_NOERROR);
2835 }
2836 
2837 /*!
2838  *	TRANSFORMATION ROUTINES FOR THE MERCATOR PROJECTION (GMT_MERCATOR)
2839  */
gmtmap_init_merc(struct GMT_CTRL * GMT,bool * search)2840 GMT_LOCAL int gmtmap_init_merc (struct GMT_CTRL *GMT, bool *search) {
2841 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, D = 1.0;
2842 
2843 	*search = false;	/* No need to search for wesn */
2844 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
2845 	if (GMT->current.proj.GMT_convert_latitudes) {	/* Set fudge factor */
2846 		gmtlib_scale_eqrad (GMT);
2847 		D = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius / GMT->current.proj.lat_swap_vals.rm;
2848 	}
2849 	if (doubleAlmostEqual (GMT->common.R.wesn[YLO], -90.0) || doubleAlmostEqual (GMT->common.R.wesn[YHI], 90.0)) {
2850 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R:  Cannot include south/north poles with Mercator projection!\n");
2851 		return GMT_PROJECTION_ERROR;
2852 	}
2853 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2854 	gmtmap_cyl_validate_clon (GMT, 0);	/* Make sure the central longitude is valid */
2855 	gmtproj_vmerc (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
2856 	GMT->current.proj.j_x *= D;
2857 	GMT->current.proj.j_yc *= D;
2858 	GMT->current.proj.j_ix /= D;
2859 	GMT->current.proj.fwd = &gmtproj_merc_sph;
2860 	GMT->current.proj.inv = &gmtproj_imerc_sph;
2861 	(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2862 	(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2863 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= (D * GMT->current.proj.M_PR_DEG);
2864 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
2865 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
2866 	GMT->current.map.n_lat_nodes = 2;
2867 	GMT->current.map.n_lon_nodes = 3;	/* > 2 to avoid map-jumps */
2868 	GMT->current.map.outside = &gmtmap_wesn_outside;
2869 	GMT->current.map.crossing = &gmtmap_wesn_crossing;
2870 	GMT->current.map.overlap = &gmtmap_wesn_overlap;
2871 	GMT->current.map.clip = &map_wesn_clip;
2872 	GMT->current.map.left_edge = &gmtmap_left_rect;
2873 	GMT->current.map.right_edge = &gmtmap_right_rect;
2874 	GMT->current.map.frame.horizontal = 1;
2875 	GMT->current.map.frame.check_side = true;
2876 	GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2877 
2878 	return (GMT_NOERROR);
2879 }
2880 
2881 /*!
2882  *	TRANSFORMATION ROUTINES FOR CYLINDRICAL EQUAL-AREA PROJECTIONS (GMT_CYL_EQ)
2883  */
gmtmap_init_cyleq(struct GMT_CTRL * GMT,bool * search)2884 GMT_LOCAL int gmtmap_init_cyleq (struct GMT_CTRL *GMT, bool *search) {
2885 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2886 
2887 	*search = false;	/* No need to search for wesn */
2888 	GMT->current.proj.Dx = GMT->current.proj.Dy = 0.0;
2889 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
2890 	if (GMT->current.proj.GMT_convert_latitudes) {
2891 		double D, k0, qp, slat, e, e2;
2892 		gmtlib_scale_eqrad (GMT);
2893 		slat = GMT->current.proj.pars[1];
2894 		GMT->current.proj.pars[1] = gmt_M_latg_to_lata (GMT, GMT->current.proj.pars[1]);
2895 		e = GMT->current.proj.ECC;
2896 		e2 = GMT->current.proj.ECC2;
2897 		qp = 1.0 - 0.5 * (1.0 - e2) * log ((1.0 - e) / (1.0 + e)) / e;
2898 		k0 = cosd (slat) / d_sqrt (1.0 - e2 * sind (GMT->current.proj.pars[1]) * sind (GMT->current.proj.pars[1]));
2899 		D = k0 / cosd (GMT->current.proj.pars[1]);
2900 		GMT->current.proj.Dx = D;
2901 		GMT->current.proj.Dy = 0.5 * qp / D;
2902 	}
2903 	GMT->current.proj.iDx = 1.0 / GMT->current.proj.Dx;
2904 	GMT->current.proj.iDy = 1.0 / GMT->current.proj.Dy;
2905 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2906 	gmtmap_cyl_validate_clon (GMT, 1);	/* Make sure the central longitude is valid */
2907 	gmtproj_vcyleq (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
2908 	gmtproj_cyleq (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2909 	gmtproj_cyleq (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2910 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
2911 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
2912 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
2913 	GMT->current.map.n_lat_nodes = 2;
2914 	GMT->current.map.n_lon_nodes = 3;	/* > 2 to avoid map-jumps */
2915 	GMT->current.proj.fwd = &gmtproj_cyleq;
2916 	GMT->current.proj.inv = &gmtproj_icyleq;
2917 	GMT->current.map.outside = &gmtmap_wesn_outside;
2918 	GMT->current.map.crossing = &gmtmap_wesn_crossing;
2919 	GMT->current.map.overlap = &gmtmap_wesn_overlap;
2920 	GMT->current.map.clip = &map_wesn_clip;
2921 	GMT->current.map.left_edge = &gmtmap_left_rect;
2922 	GMT->current.map.right_edge = &gmtmap_right_rect;
2923 	GMT->current.map.frame.horizontal = 1;
2924 	GMT->current.map.frame.check_side = true;
2925 	GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2926 
2927 	return (GMT_NOERROR);
2928 }
2929 
2930 /*!
2931  *	TRANSFORMATION ROUTINES FOR CYLINDRICAL EQUIDISTANT PROJECTION (GMT_CYL_EQDIST)
2932  */
2933 
gmtmap_init_cyleqdist(struct GMT_CTRL * GMT,bool * search)2934 GMT_LOCAL bool gmtmap_init_cyleqdist (struct GMT_CTRL *GMT, bool *search) {
2935 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2936 
2937 	*search = false;	/* No need to search for wesn */
2938 
2939 	gmtmap_set_spherical (GMT, true);	/* Force spherical for now */
2940 
2941 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2942 	gmtmap_cyl_validate_clon (GMT, 1);	/* Make sure the central longitude is valid */
2943 	gmtproj_vcyleqdist (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
2944 	gmtproj_cyleqdist (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2945 	gmtproj_cyleqdist (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2946 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
2947 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
2948 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
2949 	GMT->current.map.n_lat_nodes = 2;
2950 	GMT->current.map.n_lon_nodes = 3;	/* > 2 to avoid map-jumps */
2951 	GMT->current.proj.fwd = &gmtproj_cyleqdist;
2952 	GMT->current.proj.inv = &gmtproj_icyleqdist;
2953 	GMT->current.map.outside = &gmtmap_wesn_outside;
2954 	GMT->current.map.crossing = &gmtmap_wesn_crossing;
2955 	GMT->current.map.overlap = &gmtmap_wesn_overlap;
2956 	GMT->current.map.clip = &map_wesn_clip;
2957 	GMT->current.map.left_edge = &gmtmap_left_rect;
2958 	GMT->current.map.right_edge = &gmtmap_right_rect;
2959 	GMT->current.map.frame.horizontal = 1;
2960 	GMT->current.map.frame.check_side = true;
2961 	GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2962 
2963 
2964 	return (GMT_NOERROR);
2965 }
2966 
2967 /*!
2968  *	TRANSFORMATION ROUTINES FOR MILLER CYLINDRICAL PROJECTION (GMT_MILLER)
2969  */
gmtmap_init_miller(struct GMT_CTRL * GMT,bool * search)2970 GMT_LOCAL int gmtmap_init_miller (struct GMT_CTRL *GMT, bool *search) {
2971 	double xmin, xmax, ymin, ymax;
2972 
2973 	*search = false;	/* No need to search for wesn */
2974 
2975 	gmtmap_set_spherical (GMT, true);	/* Force spherical for now */
2976 
2977 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2978 	gmtmap_cyl_validate_clon (GMT, 1);	/* Make sure the central longitude is valid */
2979 	gmtproj_vmiller (GMT, GMT->current.proj.pars[0]);
2980 	gmtproj_miller (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2981 #ifdef CHRISTMAS
2982 	if (GMT->common.R.wesn[YLO] > 0.0) {
2983 		gmtproj_miller (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2984 		gmtproj_miller (GMT, 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]), GMT->common.R.wesn[YHI], &xmax, &ymax);
2985 	}
2986 	else {
2987 		gmtproj_miller (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmin, &ymin);
2988 		gmtproj_miller (GMT, 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]), GMT->common.R.wesn[YLO], &xmax, &ymax);
2989 	}
2990 #else
2991 	gmtproj_miller (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2992 	gmtproj_miller (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2993 #endif
2994 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
2995 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
2996 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
2997 	GMT->current.map.n_lat_nodes = 2;
2998 	GMT->current.map.n_lon_nodes = 3;	/* > 2 to avoid map-jumps */
2999 	GMT->current.proj.fwd = &gmtproj_miller;
3000 	GMT->current.proj.inv = &gmtproj_imiller;
3001 	GMT->current.map.outside = &gmtmap_wesn_outside;
3002 	GMT->current.map.crossing = &gmtmap_wesn_crossing;
3003 	GMT->current.map.overlap = &gmtmap_wesn_overlap;
3004 	GMT->current.map.clip = &map_wesn_clip;
3005 	GMT->current.map.left_edge = &gmtmap_left_rect;
3006 	GMT->current.map.right_edge = &gmtmap_right_rect;
3007 	GMT->current.map.frame.horizontal = 1;
3008 	GMT->current.map.frame.check_side = true;
3009 	GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
3010 
3011 	return (GMT_NOERROR);
3012 }
3013 
3014 /*!
3015  *	TRANSFORMATION ROUTINES FOR CYLINDRICAL STEREOGRAPHIC PROJECTIONS (GMT_CYL_STEREO)
3016  */
gmtmap_init_cylstereo(struct GMT_CTRL * GMT,bool * search)3017 GMT_LOCAL int gmtmap_init_cylstereo (struct GMT_CTRL *GMT, bool *search) {
3018 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
3019 
3020 	*search = false;	/* No need to search for wesn */
3021 	gmtmap_set_spherical (GMT, true);	/* Force spherical for now */
3022 
3023 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
3024 	gmtmap_cyl_validate_clon (GMT, 1);	/* Make sure the central longitude is valid */
3025 	gmtproj_vcylstereo (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
3026 	gmtproj_cylstereo (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3027 	gmtproj_cylstereo (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3028 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
3029 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
3030 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
3031 	GMT->current.map.n_lat_nodes = 2;
3032 	GMT->current.map.n_lon_nodes = 3;	/* > 2 to avoid map-jumps */
3033 	GMT->current.proj.fwd = &gmtproj_cylstereo;
3034 	GMT->current.proj.inv = &gmtproj_icylstereo;
3035 	GMT->current.map.outside = &gmtmap_wesn_outside;
3036 	GMT->current.map.crossing = &gmtmap_wesn_crossing;
3037 	GMT->current.map.overlap = &gmtmap_wesn_overlap;
3038 	GMT->current.map.clip = &map_wesn_clip;
3039 	GMT->current.map.left_edge = &gmtmap_left_rect;
3040 	GMT->current.map.right_edge = &gmtmap_right_rect;
3041 	GMT->current.map.frame.horizontal = 1;
3042 	GMT->current.map.frame.check_side = true;
3043 	GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
3044 
3045 	return (GMT_NOERROR);
3046 }
3047 
3048 
3049 /*!
3050  *	TRANSFORMATION ROUTINES FOR THE POLAR STEREOGRAPHIC PROJECTION (GMT_STEREO)
3051  */
3052 
gmtmap_init_stereo(struct GMT_CTRL * GMT,bool * search)3053 GMT_LOCAL int gmtmap_init_stereo (struct GMT_CTRL *GMT, bool *search) {
3054 	unsigned int i;
3055 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0, latg, D = 1.0;
3056 
3057 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
3058 	latg = GMT->current.proj.pars[1];
3059 
3060 	gmtmap_set_polar (GMT);
3061 
3062 	if (GMT->current.setting.proj_scale_factor == -1.0) GMT->current.setting.proj_scale_factor = 0.9996;	/* Select default map scale for Stereographic */
3063 	if (GMT->current.proj.polar && (lrint (GMT->current.proj.pars[5]) == 1)) GMT->current.setting.proj_scale_factor = 1.0;	/* Gave true scale at given parallel set below */
3064 	/* Equatorial view has a problem with infinite loops.  Until I find a cure
3065 	  we set projection center latitude to 0.001 so equatorial works for now */
3066 
3067 	if (fabs (GMT->current.proj.pars[1]) < GMT_CONV4_LIMIT) GMT->current.proj.pars[1] = 0.001;
3068 
3069 	gmtproj_vstereo (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3070 
3071 	if (GMT->current.proj.GMT_convert_latitudes) {	/* Set fudge factors when conformal latitudes are used */
3072 		double e1p, e1m, s, c;
3073 
3074 		D = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius / GMT->current.proj.lat_swap_vals.rm;
3075 		if (GMT->current.proj.polar) {
3076 			e1p = 1.0 + GMT->current.proj.ECC;	e1m = 1.0 - GMT->current.proj.ECC;
3077 			D /= d_sqrt (pow (e1p, e1p) * pow (e1m, e1m));
3078 			if (lrint (GMT->current.proj.pars[5]) == 1) {	/* Gave true scale at given parallel */
3079 				double k_p, m_c, t_c, es;
3080 
3081 				sincosd (fabs (GMT->current.proj.pars[4]), &s, &c);
3082 				es = GMT->current.proj.ECC * s;
3083 				m_c = c / d_sqrt (1.0 - GMT->current.proj.ECC2 * s * s);
3084 				t_c = d_sqrt (((1.0 - s) / (1.0 + s)) * pow ((1.0 + es) / (1.0 - es), GMT->current.proj.ECC));
3085 				k_p = 0.5 * m_c * d_sqrt (pow (e1p, e1p) * pow (e1m, e1m)) / t_c;
3086 				D *= k_p;
3087 			}
3088 		}
3089 		else {
3090 			sincosd (latg, &s, &c);	/* Need original geographic pole coordinates */
3091 			D *= (c / (GMT->current.proj.cosp * d_sqrt (1.0 - GMT->current.proj.ECC2 * s * s)));
3092 		}
3093 	}
3094 	GMT->current.proj.Dx = GMT->current.proj.Dy = D;
3095 
3096 	GMT->current.proj.iDx = 1.0 / GMT->current.proj.Dx;
3097 	GMT->current.proj.iDy = 1.0 / GMT->current.proj.Dy;
3098 
3099 	if (GMT->current.proj.polar) {	/* Polar aspect */
3100 		GMT->current.proj.fwd = &gmtproj_plrs_sph;
3101 		GMT->current.proj.inv = &gmtproj_iplrs_sph;
3102 		if (GMT->current.proj.units_pr_degree) {
3103 			(*GMT->current.proj.fwd) (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[4], &dummy, &radius);
3104 			GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3105 		}
3106 		else
3107 			GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3108 		GMT->current.map.meridian_straight = 1;
3109 	}
3110 	else {
3111 		GMT->current.proj.fwd = (gmt_M_is_zero (GMT->current.proj.pole)) ? &gmtproj_stereo2_sph : &gmtproj_stereo1_sph;
3112 		GMT->current.proj.inv = &gmtproj_istereo_sph;
3113 		if (GMT->current.proj.units_pr_degree) {
3114 			gmtproj_vstereo (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
3115 			(*GMT->current.proj.fwd) (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
3116 			GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3117 		}
3118 		else
3119 			GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3120 
3121 		gmtproj_vstereo (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3122 	}
3123 
3124 
3125 	if (GMT->common.R.oblique) {	/* Rectangular box given */
3126 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3127 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3128 
3129 		GMT->current.map.outside = &gmtmap_rect_outside2;
3130 		GMT->current.map.crossing = &gmtmap_rect_crossing;
3131 		GMT->current.map.overlap = &gmtmap_rect_overlap;
3132 		GMT->current.map.clip = &gmtmap_rect_clip;
3133 		GMT->current.map.left_edge = &gmtmap_left_rect;
3134 		GMT->current.map.right_edge = &gmtmap_right_rect;
3135 		GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3136 		GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
3137 	}
3138 	else {
3139 		if (GMT->current.proj.polar) {	/* Polar aspect */
3140 			if (GMT->current.proj.north_pole) {
3141 				double hor_lat = 90.0 - GMT->current.proj.f_horizon;
3142 				if (GMT->common.R.wesn[YLO] < hor_lat) {
3143 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "South boundary cannot be beyond the horizon for north polar stereographic projection (reset to horizon at %lg)\n", hor_lat);
3144 					GMT->common.R.wesn[YLO] = hor_lat;
3145 				}
3146 				if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
3147 			}
3148 			else {
3149 				double hor_lat = GMT->current.proj.f_horizon - 90.0;
3150 				if (GMT->common.R.wesn[YHI] > hor_lat) {
3151 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "North boundary cannot be beyond the horizon for south polar stereographic projection (reset to horizon at %lg)\n", hor_lat);
3152 					GMT->common.R.wesn[YHI] = hor_lat;
3153 				}
3154 				if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
3155 			}
3156 			if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
3157 					|| doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
3158 				GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
3159 			GMT->current.map.outside = &gmtmap_polar_outside;
3160 			GMT->current.map.crossing = &gmtmap_wesn_crossing;
3161 			GMT->current.map.overlap = &gmtmap_wesn_overlap;
3162 			GMT->current.map.clip = &map_wesn_clip;
3163 			GMT->current.map.frame.horizontal = 1;
3164 			GMT->current.map.n_lat_nodes = 2;
3165 			gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3166 		}
3167 		else {	/* Global view only */
3168 			/* No annotations or tickmarks in global mode */
3169 			for (i = 0; i < GMT_GRID_UPPER; i++) {
3170 				GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
3171 				GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
3172 			}
3173 			GMT->common.R.wesn[XLO] = 0.0;
3174 			GMT->common.R.wesn[XHI] = 360.0;
3175 			GMT->common.R.wesn[YLO] = -90.0;
3176 			GMT->common.R.wesn[YHI] = 90.0;
3177 			xmax = ymax = GMT->current.proj.rho_max;
3178 			xmin = ymin = -xmax;
3179 			GMT->current.map.outside = &gmtmap_radial_outside;
3180 			GMT->current.map.crossing = &gmtmap_radial_crossing;
3181 			GMT->current.map.overlap = &gmtmap_radial_overlap;
3182 			GMT->current.map.clip = &gmtmap_radial_clip;
3183 			if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3184 		}
3185 		GMT->current.map.left_edge = &gmtmap_left_circle;
3186 		GMT->current.map.right_edge = &gmtmap_right_circle;
3187 	}
3188 
3189 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
3190 	GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
3191 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
3192 
3193 	*search = GMT->common.R.oblique;
3194 	return (GMT_NOERROR);
3195 }
3196 
3197 /*!
3198  *	TRANSFORMATION ROUTINES FOR THE LAMBERT CONFORMAL CONIC PROJECTION (GMT_LAMBERT)
3199  */
3200 
gmtmap_init_lambert(struct GMT_CTRL * GMT,bool * search)3201 GMT_LOCAL int gmtmap_init_lambert (struct GMT_CTRL *GMT, bool *search) {
3202 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
3203 
3204 	GMT->current.proj.GMT_convert_latitudes = gmtmap_quickconic (GMT);
3205 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3206 	gmtproj_vlamb (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
3207 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG;
3208 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
3209 	if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) {	/* Spherical code w/wo conformal latitudes */
3210 		GMT->current.proj.fwd = &gmtproj_lamb_sph;
3211 		GMT->current.proj.inv = &gmtproj_ilamb_sph;
3212 	}
3213 	else {
3214 		GMT->current.proj.fwd = &gmtproj_lamb;
3215 		GMT->current.proj.inv = &gmtproj_ilamb;
3216 	}
3217 
3218 	if (GMT->common.R.oblique) {	/* Rectangular box given*/
3219 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3220 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3221 
3222 		GMT->current.map.outside = &gmtmap_rect_outside;
3223 		GMT->current.map.crossing = &gmtmap_rect_crossing;
3224 		GMT->current.map.overlap = &gmtmap_rect_overlap;
3225 		GMT->current.map.clip = &gmtmap_rect_clip;
3226 		GMT->current.map.left_edge = &gmtmap_left_rect;
3227 		GMT->current.map.right_edge = &gmtmap_right_rect;
3228 		GMT->current.map.frame.check_side = true;
3229 	}
3230 	else {
3231 		gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3232 		GMT->current.map.outside = &gmtmap_wesn_outside;
3233 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
3234 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
3235 		GMT->current.map.clip = &map_wesn_clip;
3236 		GMT->current.map.left_edge = &gmtmap_left_conic;
3237 		GMT->current.map.right_edge = &gmtmap_right_conic;
3238 	}
3239 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
3240 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
3241 	GMT->current.map.n_lat_nodes = 2;
3242 	GMT->current.map.frame.horizontal = 1;
3243 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
3244 	GMT->current.map.meridian_straight = 1;
3245 
3246 	*search = GMT->common.R.oblique;
3247 	return (GMT_NOERROR);
3248 }
3249 
3250 /*!
3251  *	TRANSFORMATION ROUTINES FOR THE OBLIQUE MERCATOR PROJECTION (GMT_OBLIQUE_MERC)
3252  */
3253 
gmtmap_translate_point_spherical(struct GMT_CTRL * GMT,double lon,double lat,double azimuth,double distance,double * tlon,double * tlat,double * back_azimuth)3254 GMT_LOCAL void gmtmap_translate_point_spherical (struct GMT_CTRL *GMT, double lon, double lat, double azimuth, double distance, double *tlon, double *tlat, double *back_azimuth) {
3255 	/* compute new point dist degrees from input point along azimuth */
3256 	double sa, ca, sd, cd, sy, cy;
3257 	gmt_M_unused (GMT);
3258 	sincosd (lat,  &sy, &cy);
3259 	sincosd (azimuth,  &sa, &ca);
3260 	sincosd (distance, &sd, &cd);
3261 	*tlon = lon + atand (sd * sa / (cy * cd - sy * sd * ca));
3262 	*tlat = d_asind (sy * cd + cy * sd * ca);
3263 	if (back_azimuth)
3264 		*back_azimuth = gmtmap_az_backaz_sphere (GMT, lon, lat, *tlon, *tlat, true);
3265 }
3266 
gmt_translate_point(struct GMT_CTRL * GMT,double lon,double lat,double azimuth,double distance,double * tlon,double * tlat,double * back_azimuth)3267 void gmt_translate_point (struct GMT_CTRL *GMT, double lon, double lat, double azimuth, double distance, double *tlon, double *tlat, double *back_azimuth) {
3268 	/* compute new point dist degrees from input point along azimuth */
3269 	gmtmap_translate_point_spherical (GMT, lon, lat, azimuth, distance, tlon, tlat, back_azimuth);
3270 }
3271 
gmtmap_translate_point_geodesic(struct GMT_CTRL * GMT,double lon1,double lat1,double azimuth,double distance_m,double * lon2,double * lat2,double * back_azimuth)3272 GMT_LOCAL void gmtmap_translate_point_geodesic (struct GMT_CTRL *GMT, double lon1, double lat1, double azimuth, double distance_m, double *lon2, double *lat2, double *back_azimuth) {
3273 	/* Use Vincenty (1975) solution to the direct geodesic problem.  Unstable for near antipodal points */
3274 	double a = GMT->current.proj.EQ_RAD, f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening, f1 = 1.0 - f;
3275 	double b = a * f1, s = distance_m, alpha1 = azimuth * D2R, s_alpha1, c_alpha1, tan_U1, c_U1, s_U1, sigma1, s_alpha, cosSqAlpha, cos2SigmaM, tmp;
3276 	double uSq, A, B, sigma, sigmaP, deltaSigma, sinSigma, cosSigma, lambda, C, L;
3277 
3278 	sincos (alpha1, &s_alpha1, &c_alpha1);
3279 	tan_U1 = f1 * tand (lat1);
3280 	c_U1 = 1.0 / sqrt ((1.0 + tan_U1 * tan_U1));
3281 	s_U1 = tan_U1 * c_U1,
3282 	sigma1 = atan2 (tan_U1, c_alpha1);
3283 	s_alpha = c_U1 * s_alpha1;
3284 	cosSqAlpha = 1.0 - s_alpha * s_alpha;
3285 	uSq = cosSqAlpha * (a * a - b * b) / (b * b);
3286 	A = 1.0 + uSq / 16384.0 * (4096.0 + uSq * (-768.0 + uSq * (320.0 - 175.0 * uSq)));
3287 	B = uSq / 1024.0 * (256.0 + uSq * (-128.0 + uSq * (74.0 - 47.0 * uSq)));
3288 	sigma = s / (b * A);
3289 	sigmaP = 2 * M_PI;
3290 	while (fabs (sigma - sigmaP) > 1e-12) {
3291 		cos2SigmaM = cos (2.0 * sigma1 + sigma);
3292 		sincos (sigma, &sinSigma, &cosSigma);
3293 		deltaSigma = B * sinSigma * (cos2SigmaM + B / 4.0 * (cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM) - B / 6.0 * cos2SigmaM * (-3.0 + 4.0 * sinSigma * sinSigma) * (-3.0 + 4.0 * cos2SigmaM * cos2SigmaM)));
3294 		sigmaP = sigma;
3295 		sigma = s / (b * A) + deltaSigma;
3296 	}
3297 	tmp = s_U1 * sinSigma - c_U1 * cosSigma * c_alpha1;
3298 	*lat2 = R2D * atan2 (s_U1 * cosSigma + c_U1 * sinSigma * c_alpha1, f1 * sqrt (s_alpha * s_alpha + tmp * tmp));
3299 	lambda = atan2 (sinSigma * s_alpha1, c_U1 * cosSigma - s_U1 * sinSigma * c_alpha1);
3300 	C = f / 16.0 * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha));
3301 	L = lambda - (1.0 - C) * f * s_alpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM)));
3302 	*lon2 = lon1 + L * R2D;
3303 	if (back_azimuth) *back_azimuth = R2D * atan2 (s_alpha, -tmp); /* final back azimuth */
3304 }
3305 
gmtmap_pole_rotate_forward(struct GMT_CTRL * GMT,double lon,double lat,double * tlon,double * tlat)3306 GMT_LOCAL void gmtmap_pole_rotate_forward (struct GMT_CTRL *GMT, double lon, double lat, double *tlon, double *tlat) {
3307 	/* Given the pole position in GMT->current.proj, geographical coordinates
3308 	 * are computed from oblique coordinates assuming a spherical earth.
3309 	 * Latitutes and longitudes are in degrees.
3310 	 */
3311 
3312 	double sin_lat, cos_lat, cos_lon, sin_lon, cc;
3313 
3314 	sincosd (lat, &sin_lat, &cos_lat);
3315 	sincosd (lon - GMT->current.proj.o_pole_lon, &sin_lon, &cos_lon);
3316 	cc = cos_lat * cos_lon;
3317 	*tlat = d_asind (GMT->current.proj.o_sin_pole_lat * sin_lat + GMT->current.proj.o_cos_pole_lat * cc);
3318 	*tlon = GMT->current.proj.o_beta + d_atan2d (cos_lat * sin_lon, GMT->current.proj.o_sin_pole_lat * cc - GMT->current.proj.o_cos_pole_lat * sin_lat);
3319 }
3320 
3321 #if 0
3322 /* Not currently used in GMT */
3323 GMT_LOCAL void gmtmap_pole_rotate_inverse (struct GMT_CTRL *GMT, double *lon, double *lat, double tlon, double tlat) {
3324 	/* Given the pole position in GMT->current.proj, geographical coordinates
3325 	 * are computed from oblique coordinates assuming a spherical earth.
3326 	 * Latitutes and longitudes are in degrees.
3327 	 */
3328 
3329 	double sin_tlat, cos_tlat, cos_tlon, sin_tlon, cc;
3330 
3331 	sincosd (tlat, &sin_tlat, &cos_tlat);
3332 	sincosd (tlon - GMT->current.proj.o_beta, &sin_tlon, &cos_tlon);
3333 	cc = cos_tlat * cos_tlon;
3334 	*lat = d_asind (GMT->current.proj.o_sin_pole_lat * sin_tlat - GMT->current.proj.o_cos_pole_lat * cc);
3335 	*lon = GMT->current.proj.o_pole_lon + d_atan2d (cos_tlat * sin_tlon, GMT->current.proj.o_sin_pole_lat * cc + GMT->current.proj.o_cos_pole_lat * sin_tlat);
3336 }
3337 #endif
3338 
3339 
3340 /*! . */
gmtmap_get_origin(struct GMT_CTRL * GMT,double lon1,double lat1,double lon_p,double lat_p,double * lon2,double * lat2)3341 GMT_LOCAL void gmtmap_get_origin (struct GMT_CTRL *GMT, double lon1, double lat1, double lon_p, double lat_p, double *lon2, double *lat2) {
3342 	double beta = 0.0, dummy = 0.0, d, az, c;
3343 
3344 	/* Now find origin that is 90 degrees from pole, let oblique lon=0 go through lon1/lat1 */
3345 	c = cosd (lat_p) * cosd (lat1) * cosd (lon1-lon_p);
3346 	c = d_acosd (sind (lat_p) * sind (lat1) + c);
3347 
3348 	if (c != 90.0) {	/* Not true origin */
3349 		d = fabs (90.0 - c);
3350 		az = d_asind (sind (lon_p-lon1) * cosd (lat_p) / sind (c));
3351 		if (c < 90.0) az += 180.0;
3352 		*lat2 = d_asind (sind (lat1) * cosd (d) + cosd (lat1) * sind (d) * cosd (az));
3353 		*lon2 = lon1 + d_atan2d (sind (d) * sind (az), cosd (lat1) * cosd (d) - sind (lat1) * sind (d) * cosd (az));
3354 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Correct projection origin = %g/%g\n", *lon2, *lat2);
3355 	}
3356 	else {
3357 		*lon2 = lon1;
3358 		*lat2 = lat1;
3359 	}
3360 
3361 	gmtmap_pole_rotate_forward (GMT, *lon2, *lat2, &beta, &dummy);
3362 
3363 	GMT->current.proj.o_beta = -beta;
3364 }
3365 
3366 /*! . */
gmtmap_get_rotate_pole(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)3367 GMT_LOCAL void gmtmap_get_rotate_pole (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
3368 	double plon, plat, beta, dummy;
3369 	double A[3], B[3], P[3];
3370 	/* Given A = (lon1, lat1) and B = (lon2, lat2), get P = A x B */
3371 	gmt_geo_to_cart (GMT, lat1, lon1, A, true);	/* Cartesian vector for origin */
3372 	gmt_geo_to_cart (GMT, lat2, lon2, B, true);	/* Cartesian vector for 2nd point along oblique Equator */
3373 	gmt_cross3v (GMT, A, B, P);			/* Get pole position of plane through A and B (and center of Earth O) */
3374 	gmt_normalize3v (GMT, P);			/* Make sure P has unit length */
3375 	gmt_cart_to_geo (GMT, &plat, &plon, P, true);	/* Recover lon,lat of pole */
3376 
3377 	if (plat < 0.0 && GMT->hidden.func_level == 0 && !strncmp (GMT->init.module_name, "mapproject", 10U))	/* Only allowed in mapproject at top level */
3378 		GMT->current.proj.o_spole = true;
3379 	if (GMT->current.proj.N_hemi && plat < 0.0) {	/* Insist on a Northern hemisphere pole */
3380 		plat = -plat;
3381 		plon += 180.0;
3382 		if (plon >= 360.0) plon -= 360.0;
3383 	}
3384 	GMT->current.proj.o_pole_lon = plon;
3385 	GMT->current.proj.o_pole_lat = plat;
3386 	sincosd (plat, &GMT->current.proj.o_sin_pole_lat, &GMT->current.proj.o_cos_pole_lat);
3387 	gmtmap_pole_rotate_forward (GMT, lon1, lat1, &beta, &dummy);
3388 	GMT->current.proj.o_beta = -beta;
3389 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Oblique Mercator pole is %.12g %.12g, with beta = %.12g\n", plon, plat, -beta);
3390 }
3391 
3392 /*! . */
gmtmap_init_oblique(struct GMT_CTRL * GMT,bool * search)3393 GMT_LOCAL int gmtmap_init_oblique (struct GMT_CTRL *GMT, bool *search) {
3394 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0;
3395 	double o_x = 0.0, o_y = 0.0, p_x = 0.0, p_y = 0.0, az, b_x = 0.0, b_y = 0.0, w, e, s, n;
3396 
3397 	*search = true;
3398 	gmtmap_set_spherical (GMT, true);	/* PW: Force spherical for now */
3399 
3400 	if (strncmp (GMT->init.module_name, "mapproject", 10U))
3401 		GMT->current.proj.o_spole = false;	/* Only used in mapproject */
3402 
3403 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG;	/* To get plot-units / m */
3404 
3405 	o_x = GMT->current.proj.pars[0];	o_y = GMT->current.proj.pars[1];
3406 
3407 	if (lrint (GMT->current.proj.pars[6]) == 1) {	/* Must get correct origin, then get second point */
3408 		double distance = 10.0;	/* Default spherical length for gmt_translate_point */
3409 		p_x = GMT->current.proj.pars[2];	p_y = GMT->current.proj.pars[3];
3410 
3411 		GMT->current.proj.o_pole_lon = p_x;
3412 		GMT->current.proj.o_pole_lat = p_y;
3413 		GMT->current.proj.o_sin_pole_lat = sind (p_y);
3414 		GMT->current.proj.o_cos_pole_lat = cosd (p_y);
3415 
3416 		/* Find azimuth to pole, add 90, and compute second point 10 degrees away */
3417 
3418 		gmtmap_get_origin (GMT, o_x, o_y, p_x, p_y, &o_x, &o_y);
3419 		az = atand (cosd (p_y) * sind (p_x - o_x) / (cosd (o_y) * sind (p_y) - sind (o_y) * cosd (p_y) * cosd (p_x - o_x))) + 90.0;
3420 		if ((90.0 - fabs (o_y)) < distance)
3421 			distance = 90.0 - fabs (o_y) - GMT_CONV4_LIMIT;
3422 		/* compute point <distance> degrees from origin along azimuth */
3423 		gmt_translate_point (GMT, o_x, o_y, az, distance, &b_x, &b_y, NULL);
3424 
3425 		GMT->current.proj.pars[0] = o_x;	GMT->current.proj.pars[1] = o_y;
3426 		GMT->current.proj.pars[2] = b_x;	GMT->current.proj.pars[3] = b_y;
3427 	}
3428 	else {	/* Just find pole */
3429 		b_x = GMT->current.proj.pars[2];	b_y = GMT->current.proj.pars[3];
3430 		gmtmap_get_rotate_pole (GMT, o_x, o_y, b_x, b_y);
3431 	}
3432 
3433 	/* Here we have pole and origin */
3434 
3435 	gmtlib_set_oblique_pole_and_origin (GMT, GMT->current.proj.o_pole_lon, GMT->current.proj.o_pole_lat, o_x, o_y);
3436 	gmtproj_vmerc (GMT, 0.0, 0.0);
3437 	/* Internally, the origin is at the intersection between the meridian through the pole and the selected
3438 	 * origin and the oblique equator, but we need to shift that up to the selected oblique latitude through
3439 	 * the user's origin. */
3440 	gmtproj_oblmrc (GMT, GMT->current.proj.lon0, GMT->current.proj.lat0, &dummy, &GMT->current.proj.o_shift);
3441 	if (GMT->current.proj.o_spole) GMT->current.proj.o_shift = -GMT->current.proj.o_shift;
3442 
3443 	if (GMT->common.R.oblique) {	/* wesn is lower left and upper right corners in normal lon/lats */
3444 		gmtproj_oblmrc (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3445 		gmtproj_oblmrc (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3446 	}
3447 	else {	/* Gave oblique degrees */
3448 		if (doubleAlmostEqual (GMT->common.R.wesn[YLO], -90.0) || doubleAlmostEqual (GMT->common.R.wesn[YHI], 90.0)) {
3449 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R:  Cannot include south/north oblique poles with Oblique Mercator projection!\n");
3450 			return GMT_PROJECTION_ERROR;
3451 		}
3452 		/* Convert oblique wesn in degrees to meters using regular Mercator */
3453 		if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) {
3454 			GMT->common.R.wesn[XLO] = -180.0;
3455 			GMT->common.R.wesn[XHI] = +180.0;
3456 		}
3457 		gmtproj_merc_sph (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3458 		gmtproj_merc_sph (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3459 		GMT->common.R.oblique = true;	/* Since wesn was oblique, not geographical wesn */
3460 	}
3461 
3462 	gmtproj_imerc_sph (GMT, &w, &s, xmin, ymin);	/* Get oblique wesn boundaries */
3463 	gmtproj_imerc_sph (GMT, &e, &n, xmax, ymax);
3464 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
3465 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
3466 	GMT->current.proj.fwd = &gmtproj_oblmrc;
3467 	GMT->current.proj.inv = &gmtproj_ioblmrc;
3468 	GMT->current.map.outside = &gmtmap_rect_outside;
3469 	GMT->current.map.crossing = &gmtmap_rect_crossing;
3470 	GMT->current.map.overlap = &gmtmap_rect_overlap;
3471 	GMT->current.map.clip = &gmtmap_rect_clip;
3472 	GMT->current.map.left_edge = &gmtmap_left_rect;
3473 	GMT->current.map.right_edge = &gmtmap_right_rect;
3474 
3475 	GMT->current.map.is_world = gmt_M_360_range (w, e);
3476 #if 0
3477 	if (GMT->current.map.is_world == false) {	/* Check if one of the poles are inside the area */
3478 		if (!gmt_map_outside (GMT, 0.0, -90.0))	/* South pole is inside map */
3479 			GMT->current.map.is_world = true;
3480 		else if (!gmt_map_outside (GMT, 0.0, +90.0))	/* North pole is inside map */
3481 			GMT->current.map.is_world = true;
3482 	}
3483 #endif
3484 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3485 	GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3486 	return (GMT_NOERROR);
3487 }
3488 
3489 /*
3490  *	TRANSFORMATION ROUTINES FOR THE TRANSVERSE MERCATOR PROJECTION (GMT_TM)
3491  */
3492 
3493 /* For global TM maps */
3494 
3495 /*! . */
gmtmap_wrap_around_check_tm(struct GMT_CTRL * GMT,double * angle,double last_x,double last_y,double this_x,double this_y,double * xx,double * yy,unsigned int * sides)3496 GMT_LOCAL unsigned int gmtmap_wrap_around_check_tm (struct GMT_CTRL *GMT, double *angle, double last_x, double last_y, double this_x, double this_y, double *xx, double *yy, unsigned int *sides) {
3497 	double dx, dy, width, jump;
3498 
3499 	jump = this_y - last_y;
3500 	width = 0.5 * GMT->current.map.height;
3501 
3502 	if (fabs (jump) - width <= GMT_CONV4_LIMIT || fabs (jump) <= GMT_CONV4_LIMIT) return (0);
3503 	dx = this_x - last_x;
3504 
3505 	if (jump < -width) {	/* Crossed top boundary */
3506 		dy = GMT->current.map.height + jump;
3507 		xx[0] = xx[1] = last_x + (GMT->current.map.height - last_y) * dx / dy;
3508 		if (xx[0] < 0.0 || xx[0] > GMT->current.proj.rect[XHI]) return (0);
3509 		yy[0] = GMT->current.map.height;	yy[1] = 0.0;
3510 		sides[0] = 2;
3511 		angle[0] = d_atan2d (dy, dx);
3512 	}
3513 	else {	/* Crossed bottom boundary */
3514 		dy = GMT->current.map.height - jump;
3515 		xx[0] = xx[1] = last_x + last_y * dx / dy;
3516 		if (xx[0] < 0.0 || xx[0] > GMT->current.proj.rect[XHI]) return (0);
3517 		yy[0] = 0.0;	yy[1] = GMT->current.map.height;
3518 		sides[0] = 0;
3519 		angle[0] = d_atan2d (dy, -dx);
3520 	}
3521 	angle[1] = angle[0] + 180.0;
3522 	sides[1] = 2 - sides[0];
3523 
3524 	return (2);
3525 }
3526 
3527 /*! . */
gmtmap_this_point_wraps_tm(struct GMT_CTRL * GMT,double y0,double y1)3528 GMT_LOCAL bool gmtmap_this_point_wraps_tm (struct GMT_CTRL *GMT, double y0, double y1) {
3529 	/* Returns true if the 2 y-points implies a jump at this x-level of the TM map */
3530 
3531 	double dy;
3532 
3533 	return ((dy = fabs (y1 - y0)) > GMT->current.map.half_height);
3534 }
3535 
3536 /*! . */
gmtmap_will_it_wrap_tm(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t * start)3537 GMT_LOCAL bool gmtmap_will_it_wrap_tm (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t *start) {
3538 	/* Determines if a polygon will wrap at edges for TM global projection */
3539 	uint64_t i;
3540 	bool wrap;
3541 	gmt_M_unused(x);
3542 
3543 	if (!GMT->current.map.is_world) return (false);
3544 
3545 	for (i = 1, wrap = false; !wrap && i < n; i++) {
3546 		wrap = gmtmap_this_point_wraps_tm (GMT, y[i-1], y[i]);
3547 	}
3548 	*start = i - 1;
3549 	return (wrap);
3550 }
3551 
3552 /*! . */
gmtmap_get_crossings_tm(struct GMT_CTRL * GMT,double * xc,double * yc,double x0,double y0,double x1,double y1)3553 GMT_LOCAL void gmtmap_get_crossings_tm (struct GMT_CTRL *GMT, double *xc, double *yc, double x0, double y0, double x1, double y1) {
3554 	/* Finds crossings for wrap-arounds for global TM maps */
3555 	double xa, xb, ya, yb, dy, c;
3556 
3557 	xa = x0;	xb = x1;
3558 	ya = y0;	yb = y1;
3559 	if (ya > yb) {	/* Make A the minimum y point */
3560 		gmt_M_double_swap (xa, xb);
3561 		gmt_M_double_swap (ya, yb);
3562 	}
3563 
3564 	yb -= GMT->current.map.height;
3565 
3566 	dy = ya - yb;
3567 	c = (doubleAlmostEqualZero (ya, yb)) ? 0.0 : (xa - xb) / dy;
3568 	xc[0] = xc[1] = xb - yb * c;
3569 	if (y0 > y1) {	/* First cut top */
3570 		yc[0] = GMT->current.map.height;
3571 		yc[1] = 0.0;
3572 	}
3573 	else {
3574 		yc[0] = 0.0;
3575 		yc[1] = GMT->current.map.height;
3576 	}
3577 }
3578 
3579 /*! . */
gmtmap_jump_tm(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)3580 GMT_LOCAL int gmtmap_jump_tm (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
3581 	/* true if y-distance between points exceeds 1/2 map height at this x value */
3582 	/* Only used for TM world maps */
3583 
3584 	double dy;
3585 	gmt_M_unused(x0); gmt_M_unused(x1);
3586 
3587 	dy = y1 - y0;
3588 	if (dy > GMT->current.map.half_height) return (-1);	/* Cross bottom/south boundary */
3589 	if (dy < (-GMT->current.map.half_height)) return (1);	/* Cross top/north boundary */
3590 	return (0);
3591 }
3592 
3593 /*! . */
gmtmap_init_tm(struct GMT_CTRL * GMT,bool * search)3594 GMT_LOCAL int gmtmap_init_tm (struct GMT_CTRL *GMT, bool *search) {
3595 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
3596 
3597 	*search = GMT->common.R.oblique;
3598 
3599 	/* Wrap and truncations are in y, not x for TM */
3600 
3601 	GMT->current.map.wrap_around_check = &gmtmap_wrap_around_check_tm;
3602 	GMT->current.map.jump = &gmtmap_jump_tm;
3603 	GMT->current.map.will_it_wrap = &gmtmap_will_it_wrap_tm;
3604 #if 0
3605 	GMT->current.map.this_point_wraps = &gmtmap_this_point_wraps_tm;
3606 #endif
3607 	GMT->current.map.get_crossings = &gmtmap_get_crossings_tm;
3608 
3609 	if (GMT->current.setting.proj_scale_factor == -1.0) GMT->current.setting.proj_scale_factor = 1.0;	/* Select default map scale for TM */
3610 	GMT->current.proj.GMT_convert_latitudes = gmtmap_quicktm (GMT, GMT->current.proj.pars[0], 10.0);
3611 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3612 	gmtproj_vtm (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
3613 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
3614 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
3615 	if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) {	/* Spherical code w/wo conformal latitudes */
3616 		GMT->current.proj.fwd = &gmtproj_tm_sph;
3617 		GMT->current.proj.inv = &gmtproj_itm_sph;
3618 	}
3619 	else {
3620 		GMT->current.proj.fwd = &gmtproj_tm;
3621 		GMT->current.proj.inv = &gmtproj_itm;
3622 	}
3623 
3624 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
3625 	if (GMT->current.map.is_world) {	/* Gave oblique degrees */
3626 		double w, e, dummy = 0.0;
3627 		w = GMT->current.proj.central_meridian + GMT->common.R.wesn[YLO];
3628 		e = GMT->current.proj.central_meridian + GMT->common.R.wesn[YHI];
3629 		GMT->common.R.wesn[YLO] = -90;
3630 		GMT->common.R.wesn[YHI] = 90;
3631 		GMT->common.R.wesn[XHI] = e;
3632 		GMT->common.R.wesn[XLO] = w;
3633 		gmtproj_vtm (GMT, GMT->current.proj.pars[0], 0.0);
3634 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], 0.0, &xmin, &dummy);
3635 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], 0.0, &xmax, &dummy);
3636 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &dummy, &ymin);
3637 		ymax = ymin + (TWO_PI * GMT->current.proj.EQ_RAD * GMT->current.setting.proj_scale_factor);
3638 		gmtproj_vtm (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
3639 		GMT->current.map.outside = &gmtmap_rect_outside;
3640 		GMT->current.map.crossing = &gmtmap_rect_crossing;
3641 		GMT->current.map.overlap = &gmtmap_rect_overlap;
3642 		GMT->current.map.clip = &gmtmap_rect_clip;
3643 		GMT->current.map.left_edge = &gmtmap_left_rect;
3644 		GMT->current.map.right_edge = &gmtmap_right_rect;
3645 		GMT->current.map.frame.check_side = true;
3646 		GMT->current.map.is_world_tm = true;
3647 		GMT->common.R.oblique = true;	/* Since wesn was oblique, not geographical wesn */
3648 		GMT->common.R.wesn[XHI] = GMT->current.proj.central_meridian + 180.0;
3649 		GMT->common.R.wesn[XLO] = GMT->current.proj.central_meridian - 180.0;
3650 	}
3651 	else if (!GMT->common.R.oblique) {
3652 		gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3653 		GMT->current.map.outside = &gmtmap_wesn_outside;
3654 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
3655 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
3656 		GMT->current.map.clip = &map_wesn_clip;
3657 		GMT->current.map.left_edge = &gmtmap_left_rect;
3658 		GMT->current.map.right_edge = &gmtmap_right_rect;
3659 		GMT->current.map.is_world_tm = doubleAlmostEqualZero (GMT->common.R.wesn[YHI], GMT->common.R.wesn[YLO]);
3660 		GMT->current.map.is_world = false;
3661 	}
3662 	else { /* Find min values */
3663 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3664 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3665 		GMT->current.map.outside = &gmtmap_rect_outside;
3666 		GMT->current.map.crossing = &gmtmap_rect_crossing;
3667 		GMT->current.map.overlap = &gmtmap_rect_overlap;
3668 		GMT->current.map.clip = &gmtmap_rect_clip;
3669 		GMT->current.map.left_edge = &gmtmap_left_rect;
3670 		GMT->current.map.right_edge = &gmtmap_right_rect;
3671 		GMT->current.map.frame.check_side = true;
3672 		GMT->current.map.is_world_tm = false;
3673 		GMT->current.map.is_world = (fabs (GMT->common.R.wesn[YLO] - GMT->common.R.wesn[YHI]) < GMT_CONV4_LIMIT);
3674 	}
3675 
3676 	GMT->current.map.frame.horizontal = 1;
3677 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
3678 
3679 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3680 
3681 	return (GMT_NOERROR);
3682 }
3683 
3684 /*
3685  *	TRANSFORMATION ROUTINES FOR THE UNIVERSAL TRANSVERSE MERCATOR PROJECTION (GMT_UTM)
3686  */
3687 
3688 /*! . */
gmtmap_set_utmzone(struct GMT_CTRL * GMT)3689 GMT_LOCAL void gmtmap_set_utmzone (struct GMT_CTRL *GMT) {
3690 	/* When no UTM zone is given we determine the best one from midpoint of -R.
3691 	 * We pass back via GMT->current.proj.pars[0] the zone number.
3692 	 * Note that despite the funky non-standard zones around Norway
3693 	 * the central meridian still follows the simple 6-degree stepping.
3694 	 */
3695 	int kx, ky;
3696 	double clon, clat;
3697 	char zone[4] = {""};
3698 
3699 	clon = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
3700 	if (clon > 180.0) clon -= 360.0;
3701 	else if (clon < -180.0) clon += 360.0;
3702 	kx = irint (floor ((clon + 180.0) / 6.0)) + 1;	/* Best UTM zone */
3703 	GMT->current.proj.lon0 = -180.0 + kx * 6.0 - 3.0;	/* Best centered longitude */
3704 	if (GMT->common.R.wesn[YLO] >= 0.0)		/* By doing >= we are implicitly saying that when no info we default to northern hemisphere */
3705 		GMT->current.proj.utm_hemisphere = +1;
3706 	else
3707 		GMT->current.proj.utm_hemisphere = -1;
3708 	GMT->current.proj.utm_zoney = 0;
3709 	clat = 0.5 * (GMT->common.R.wesn[YLO] + GMT->common.R.wesn[YHI]);
3710 	if (clat < -80.0) {	/* A or B */
3711 		zone[0] = (GMT->current.proj.lon0 < 0.0) ? 'A' : 'B';
3712 	}
3713 	else if (clat > 84.0) {	/* Y or Z */
3714 		zone[0] = (GMT->current.proj.lon0 < 0.0) ? 'Y' : 'Z';
3715 	}
3716 	else {	/* In the general latitude range of 1-60 zones */
3717 		ky = irint (floor ((clat + 80.0) / 8.0));
3718 		if (ky == 20) ky = 19;	/* Since band X is 12 degrees tall there is no 20 */
3719 		if (ky >= 11) ky += 4;		/* zone = (P-X) */
3720 		else if (ky >= 6) ky += 3;	/* zone = (J-N) */
3721 		else ky += 2;			/* zone = (C-H) */
3722 		GMT->current.proj.utm_zoney = 'A' + (char)ky;
3723 		if (GMT->current.proj.utm_zoney == 'X') {	/* Deal with funky zone X */
3724 			if (clon >= 0.0 && clon < 9.0)
3725 				kx = 31;
3726 			else if (clon >= 9.0  && clon < 21.0)
3727 				kx = 33;
3728 			else if (clon >= 21.0 && clon < 33.0)
3729 				kx = 35;
3730 			else if (clon >= 33.0 && clon < 42.0)
3731 				kx = 37;
3732 		}
3733 		else if (GMT->current.proj.utm_zoney == 'V') {	/* Deal with funky zone V */
3734 			if (clon >= 3.0 && clon < 12.0)
3735 				kx = 32;
3736 		}
3737 		snprintf (zone, 4, "%d%c", kx, GMT->current.proj.utm_zoney);
3738 	}
3739 	GMT->current.proj.pars[0] = (double)kx;
3740 	GMT->current.proj.lat0 = 0.0;
3741 	GMT_Report (GMT->parent, GMT_MSG_WARNING, "No UTM zone given; zone %s selected\n", zone);
3742 }
3743 
3744 /*! . */
gmtmap_init_utm(struct GMT_CTRL * GMT,bool * search)3745 GMT_LOCAL int gmtmap_init_utm (struct GMT_CTRL *GMT, bool *search) {
3746 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, lon0;
3747 
3748 	*search = GMT->common.R.oblique;
3749 
3750 	if (GMT->current.setting.proj_scale_factor == -1.0) GMT->current.setting.proj_scale_factor = 0.9996;	/* Select default map scale for UTM */
3751 	if (GMT->current.proj.pars[0] < 0.0) gmtmap_set_utmzone (GMT);	/* Determine UTM zone from -R */
3752 	lon0 = 180.0 + 6.0 * GMT->current.proj.pars[0] - 3.0;	/* Central meridian for this UTM zone */
3753 	if (lon0 >= 360.0) lon0 -= 360.0;
3754 	GMT->current.proj.GMT_convert_latitudes = gmtmap_quicktm (GMT, lon0, 10.0);
3755 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3756 	gmtproj_vtm (GMT, lon0, 0.0);	/* Central meridian for this zone */
3757 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
3758 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
3759 	switch (GMT->current.proj.utm_hemisphere) {	/* Set hemisphere */
3760 		case -1:
3761 			GMT->current.proj.north_pole = false;
3762 			break;
3763 		case +1:
3764 			GMT->current.proj.north_pole = true;
3765 			break;
3766 		default:
3767 			GMT->current.proj.north_pole = (GMT->common.R.wesn[YLO] >= 0.0);
3768 			break;
3769 	}
3770 	if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) {	/* Spherical code w/wo conformal latitudes */
3771 		GMT->current.proj.fwd = &gmtproj_utm_sph;
3772 		GMT->current.proj.inv = &gmtproj_iutm_sph;
3773 	}
3774 	else {
3775 		GMT->current.proj.fwd = &gmtproj_utm;
3776 		GMT->current.proj.inv = &gmtproj_iutm;
3777 	}
3778 
3779 	if (fabs (GMT->common.R.wesn[XLO] - GMT->common.R.wesn[XHI]) > 360.0) {	/* -R in UTM meters */
3780 		(*GMT->current.proj.inv) (GMT, &GMT->common.R.wesn[XLO], &GMT->common.R.wesn[YLO], GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO]);
3781 		(*GMT->current.proj.inv) (GMT, &GMT->common.R.wesn[XHI], &GMT->common.R.wesn[YHI], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI]);
3782 		GMT->common.R.oblique = true;
3783 	}
3784 	if (GMT->common.R.oblique) {
3785 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3786 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3787 		GMT->current.map.outside = &gmtmap_rect_outside;
3788 		GMT->current.map.crossing = &gmtmap_rect_crossing;
3789 		GMT->current.map.overlap = &gmtmap_rect_overlap;
3790 		GMT->current.map.clip = &gmtmap_rect_clip;
3791 		GMT->current.map.left_edge = &gmtmap_left_rect;
3792 		GMT->current.map.right_edge = &gmtmap_right_rect;
3793 		GMT->current.map.frame.check_side = true;
3794 	}
3795 	else {
3796 		gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3797 		GMT->current.map.outside = &gmtmap_wesn_outside;
3798 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
3799 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
3800 		GMT->current.map.clip = &map_wesn_clip;
3801 		GMT->current.map.left_edge = &gmtmap_left_rect;
3802 		GMT->current.map.right_edge = &gmtmap_right_rect;
3803 	}
3804 
3805 	GMT->current.map.frame.horizontal = 1;
3806 
3807 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
3808 
3809 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3810 
3811 	return (GMT_NOERROR);
3812 }
3813 
3814 #if 0
3815 GMT_LOCAL double gmtmap_UTMzone_to_clon (struct GMT_CTRL *GMT, unsigned int zone_x, char zone_y) {
3816 	/* Return the central longitude of this UTM zone */
3817 	double clon = 180.0 + 6.0 * zone_x - 3.0;	/* This is valid for most zones */
3818 
3819 	if (zone_y == 0) return (clon);	/* Latitude zone is not specified so we are done */
3820 	if ((zone_y < 'A' || zone_y > 'Z') || zone_y < 'I' || zone_y < 'O') return (GMT->session.d_NaN);	/* Bad latitude zone so return NaN*/
3821 	if (zone_y <= 'B') return (-90.0 + 180.0 * (zone_y - 'A'));	/* Either -90 or +90 */
3822 	if (zone_y == 'V' && zone_x == 31) return (1.5);	/* Center of 31V */
3823 	if (zone_y == 'V' && zone_x == 32) return (7.5);	/* Center of 32V */
3824 	if (zone_y == 'X') {
3825 		if (zone_x == 31) return (4.5);		/* Center of 31X */
3826 		if (zone_x == 33) return (15.0);	/* Center of 33X */
3827 		if (zone_x == 35) return (27.0);	/* Center of 35X */
3828 		if (zone_x == 37) return (37.5);	/* Center of 37X */
3829 		if (zone_x == 32 || zone_x == 34 || zone_x == 36) return (GMT->session.d_NaN);	/* Bad latitude zone so return NaN*/
3830 		return (clon);
3831 	}
3832 	/* Only Y or Z left */
3833 	return (-90.0 + 180.0 * (zone_y - 'Y'));	/* Either -90 or +90 */
3834 }
3835 #endif
3836 
3837 /*
3838  *	TRANSFORMATION ROUTINES FOR THE LAMBERT AZIMUTHAL EQUAL-AREA PROJECTION (GMT_LAMB_AZ_EQ)
3839  */
3840 
3841 /*! . */
gmtmap_init_lambeq(struct GMT_CTRL * GMT,bool * search)3842 GMT_LOCAL int gmtmap_init_lambeq (struct GMT_CTRL *GMT, bool *search) {
3843 	unsigned int i;
3844 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
3845 
3846 	*search = GMT->common.R.oblique;
3847 	GMT->current.proj.Dx = GMT->current.proj.Dy = 1.0;
3848 
3849 	gmtmap_set_polar (GMT);
3850 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
3851 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3852 	gmtproj_vlambeq (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3853 
3854 	if (GMT->current.proj.GMT_convert_latitudes) {
3855 		double D, s, c;
3856 		sincosd (GMT->current.proj.pars[1], &s, &c);
3857 		D = (GMT->current.proj.polar) ? 1.0 : (GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius / GMT->current.proj.lat_swap_vals.ra) * c / (GMT->current.proj.cosp * d_sqrt (1.0 - GMT->current.proj.ECC2 * s * s));
3858 		GMT->current.proj.Dx = D;
3859 		GMT->current.proj.Dy = 1.0 / D;
3860 	}
3861 	GMT->current.proj.iDx = 1.0 / GMT->current.proj.Dx;
3862 	GMT->current.proj.iDy = 1.0 / GMT->current.proj.Dy;
3863 
3864 	GMT->current.proj.fwd = &gmtproj_lambeq;
3865 	GMT->current.proj.inv = &gmtproj_ilambeq;
3866 	if (GMT->current.proj.units_pr_degree) {
3867 		gmtproj_vlambeq (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
3868 		gmtproj_lambeq (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
3869 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3870 		gmtproj_vlambeq (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3871 	}
3872 	else
3873 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3874 
3875 	if (GMT->common.R.oblique) {	/* Rectangular box given */
3876 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3877 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3878 
3879 		GMT->current.map.outside = &gmtmap_rect_outside2;
3880 		GMT->current.map.crossing = &gmtmap_rect_crossing;
3881 		GMT->current.map.overlap = &gmtmap_rect_overlap;
3882 		GMT->current.map.clip = &gmtmap_rect_clip;
3883 		GMT->current.map.left_edge = &gmtmap_left_rect;
3884 		GMT->current.map.right_edge = &gmtmap_right_rect;
3885 		GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3886 		GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
3887 	}
3888 	else {
3889 		if (GMT->current.proj.polar) {	/* Polar aspect */
3890 			if (GMT->current.proj.north_pole) {
3891 				double hor_lat = 90.0 - GMT->current.proj.f_horizon;
3892 				if (GMT->common.R.wesn[YLO] < hor_lat) {
3893 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "South boundary cannot be beyond the horizon for north polar Lambert azimuthal projection (reset to horizon at %lg)\n", hor_lat);
3894 					GMT->common.R.wesn[YLO] = hor_lat;
3895 				}
3896 				if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
3897 			}
3898 			else {
3899 				double hor_lat = GMT->current.proj.f_horizon - 90.0;
3900 				if (GMT->common.R.wesn[YHI] > hor_lat) {
3901 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "North boundary cannot be beyond the horizon for south polar Lambert azimuthal projection (reset to horizon at %lg)\n", hor_lat);
3902 					GMT->common.R.wesn[YHI] = hor_lat;
3903 				}
3904 				if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
3905 			}
3906 			if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
3907 					|| doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
3908 				GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
3909 			GMT->current.map.outside = &gmtmap_polar_outside;
3910 			GMT->current.map.crossing = &gmtmap_wesn_crossing;
3911 			GMT->current.map.overlap = &gmtmap_wesn_overlap;
3912 			GMT->current.map.clip = &map_wesn_clip;
3913 			GMT->current.map.frame.horizontal = 1;
3914 			GMT->current.map.n_lat_nodes = 2;
3915 			gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3916 		}
3917 		else {	/* Global view only */
3918 			/* No annotations or tickmarks in global mode */
3919 			for (i = 0; i < GMT_GRID_UPPER; i++) {
3920 				GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
3921 				GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
3922 			}
3923 			GMT->common.R.wesn[XLO] = 0.0;
3924 			GMT->common.R.wesn[XHI] = 360.0;
3925 			GMT->common.R.wesn[YLO] = -90.0;
3926 			GMT->common.R.wesn[YHI] = 90.0;
3927 			xmax = ymax = GMT->current.proj.rho_max;
3928 			xmin = ymin = -xmax;
3929 			GMT->current.map.outside = &gmtmap_radial_outside;
3930 			GMT->current.map.crossing = &gmtmap_radial_crossing;
3931 			GMT->current.map.overlap = &gmtmap_radial_overlap;
3932 			GMT->current.map.clip = &gmtmap_radial_clip;
3933 			if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3934 		}
3935 		GMT->current.map.left_edge = &gmtmap_left_circle;
3936 		GMT->current.map.right_edge = &gmtmap_right_circle;
3937 	}
3938 
3939 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
3940 	GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
3941 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
3942 	if (GMT->current.proj.polar) GMT->current.map.meridian_straight = 1;
3943 
3944 	return (GMT_NOERROR);
3945 }
3946 
3947 /*
3948  *	TRANSFORMATION ROUTINES FOR THE ORTHOGRAPHIC PROJECTION (GMT_ORTHO)
3949  */
3950 
3951 /*! . */
gmtmap_init_ortho(struct GMT_CTRL * GMT,bool * search)3952 GMT_LOCAL int gmtmap_init_ortho (struct GMT_CTRL *GMT, bool *search) {
3953 	unsigned int i;
3954 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
3955 
3956 	*search  =GMT->common.R.oblique;
3957 
3958 	gmtmap_set_spherical (GMT, true);	/* PW: Force spherical for now */
3959 
3960 	gmtmap_set_polar (GMT);
3961 
3962 	if (GMT->current.proj.units_pr_degree) {
3963 		gmtproj_vortho (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
3964 		gmtproj_ortho (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
3965 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3966 	}
3967 	else
3968 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3969 
3970 	gmtproj_vortho (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3971 	GMT->current.proj.fwd = &gmtproj_ortho;
3972 	GMT->current.proj.inv = &gmtproj_iortho;
3973 
3974 	if (GMT->common.R.oblique) {	/* Rectangular box given */
3975 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3976 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3977 
3978 		GMT->current.map.outside = &gmtmap_rect_outside2;
3979 		GMT->current.map.crossing = &gmtmap_rect_crossing;
3980 		GMT->current.map.overlap = &gmtmap_rect_overlap;
3981 		GMT->current.map.clip = &gmtmap_rect_clip;
3982 		GMT->current.map.left_edge = &gmtmap_left_rect;
3983 		GMT->current.map.right_edge = &gmtmap_right_rect;
3984 		GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3985 		GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
3986 	}
3987 	else {
3988 		if (GMT->current.proj.polar) {	/* Polar aspect */
3989 			if (GMT->current.proj.north_pole) {
3990 				double hor_lat = 90.0 - GMT->current.proj.f_horizon;
3991 				if (GMT->common.R.wesn[YLO] < hor_lat) {
3992 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "South boundary cannot be beyond the horizon for north polar orthographic projection (reset to horizon at %lg)\n", hor_lat);
3993 					GMT->common.R.wesn[YLO] = hor_lat;
3994 				}
3995 				if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
3996 			}
3997 			else {
3998 				double hor_lat = GMT->current.proj.f_horizon - 90.0;
3999 				if (GMT->common.R.wesn[YHI] > hor_lat) {
4000 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "North boundary cannot be beyond the horizon for south polar orthographic projection (reset to horizon at %lg)\n", hor_lat);
4001 					GMT->common.R.wesn[YHI] = hor_lat;
4002 				}
4003 				if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4004 			}
4005 			if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
4006 					|| doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4007 				GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4008 			GMT->current.map.outside = &gmtmap_polar_outside;
4009 			GMT->current.map.crossing = &gmtmap_wesn_crossing;
4010 			GMT->current.map.overlap = &gmtmap_wesn_overlap;
4011 			GMT->current.map.clip = &map_wesn_clip;
4012 			GMT->current.map.frame.horizontal = 1;
4013 			GMT->current.map.n_lat_nodes = 2;
4014 			gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4015 		}
4016 		else {	/* Global view only */
4017 			/* No annotations or tickmarks in global mode */
4018 			for (i = 0; i < GMT_GRID_UPPER; i++) {
4019 				GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
4020 				GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
4021 			}
4022 			GMT->common.R.wesn[XLO] = 0.0;
4023 			GMT->common.R.wesn[XHI] = 360.0;
4024 			GMT->common.R.wesn[YLO] = -90.0;
4025 			GMT->common.R.wesn[YHI] = 90.0;
4026 			xmax = ymax = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4027 			xmin = ymin = -xmax;
4028 			GMT->current.map.outside = &gmtmap_radial_outside;
4029 			GMT->current.map.crossing = &gmtmap_radial_crossing;
4030 			GMT->current.map.overlap = &gmtmap_radial_overlap;
4031 			GMT->current.map.clip = &gmtmap_radial_clip;
4032 			if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4033 		}
4034 		GMT->current.map.left_edge = &gmtmap_left_circle;
4035 		GMT->current.map.right_edge = &gmtmap_right_circle;
4036 	}
4037 
4038 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
4039 	GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4040 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4041 	if (GMT->current.proj.polar) GMT->current.map.meridian_straight = 1;
4042 
4043 	return (GMT_NOERROR);
4044 }
4045 
4046 /*! . */
gmtmap_left_genper(struct GMT_CTRL * GMT,double y)4047 GMT_LOCAL double gmtmap_left_genper (struct GMT_CTRL *GMT, double y) {
4048 	/* Windowed genper may need to consider the inner of circle and rectangle */
4049 	return (MAX (0.0, gmtmap_left_circle (GMT, y)));
4050 }
4051 
4052 /*! . */
gmtmap_right_genper(struct GMT_CTRL * GMT,double y)4053 GMT_LOCAL double gmtmap_right_genper (struct GMT_CTRL *GMT, double y) {
4054 	return (MIN (GMT->current.map.width, gmtmap_right_circle (GMT, y)));
4055 }
4056 
4057 /*! . */
gmtmap_init_genper(struct GMT_CTRL * GMT,bool * search)4058 GMT_LOCAL int gmtmap_init_genper (struct GMT_CTRL *GMT, bool *search) {
4059 	unsigned int i;
4060 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
4061 	double alt, azimuth, tilt, width, height;
4062 	double twist, scale, units;
4063 
4064 	units = GMT->current.proj.pars[2];
4065 	scale = GMT->current.proj.pars[3];
4066 	alt = GMT->current.proj.pars[4];
4067 	azimuth = GMT->current.proj.pars[5];
4068 	tilt = GMT->current.proj.pars[6];
4069 	twist = GMT->current.proj.pars[7];
4070 	width = GMT->current.proj.pars[8];
4071 	height = GMT->current.proj.pars[9];
4072 
4073 	if (GMT->current.proj.g_sphere) gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
4074 
4075 	gmtmap_set_polar (GMT);
4076 
4077 	if (GMT->current.proj.units_pr_degree) {
4078 		gmtproj_vgenper (GMT, 0.0, 90.0, alt, azimuth, tilt, twist, width, height);
4079 		gmtproj_genper (GMT, 0.0, fabs (GMT->current.proj.pars[3]), &dummy, &radius);
4080 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[2] / radius);
4081 	}
4082 	else
4083 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
4084 
4085 	if (GMT->current.proj.g_debug > 1) {
4086 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: units_pr_degree %d\n", GMT->current.proj.units_pr_degree);
4087 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: radius %f\n", radius);
4088 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: scale %f units %f\n", scale, units);
4089 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: x scale %f y scale %f\n", GMT->current.proj.scale[GMT_X], GMT->current.proj.scale[GMT_Y]);
4090 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gave_map_width %d \n",GMT->current.proj.gave_map_width);
4091 	}
4092 
4093 	gmtproj_vgenper (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], alt, azimuth, tilt, twist, width, height);
4094 	GMT->current.proj.fwd = &gmtproj_genper;
4095 	GMT->current.proj.inv = &gmtproj_igenper;
4096 
4097 	GMT->common.R.wesn[XLO] = 0.0;
4098 	GMT->common.R.wesn[XHI] = 360.0;
4099 	GMT->common.R.wesn[YLO] = -90.0;
4100 	GMT->common.R.wesn[YHI] = 90.0;
4101 
4102 	xmin = GMT->current.proj.g_xmin;
4103 	xmax = GMT->current.proj.g_xmax;
4104 	ymin = GMT->current.proj.g_ymin;
4105 	ymax = GMT->current.proj.g_ymax;
4106 
4107 	if (GMT->current.proj.g_width != 0.0) {
4108 		GMT->common.R.oblique = false;
4109 		GMT->current.proj.windowed = true;
4110 		if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "using windowed region\n");
4111 		GMT->current.map.outside = &gmtmap_rect_outside2;
4112 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4113 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4114 #if 0
4115 		GMT->current.map.crossing = &gmtmap_genper_crossing;
4116 		GMT->current.map.overlap = &gmtmap_genperw_overlap;
4117 		GMT->current.map.left_edge = &gmtmap_left_rect;
4118 		GMT->current.map.right_edge = &gmtmap_right_rect;
4119 #endif
4120 		GMT->current.map.clip = &gmtmap_rect_clip_old;
4121 		GMT->current.map.left_edge = &gmtmap_left_genper;
4122 		GMT->current.map.right_edge = &gmtmap_right_genper;
4123 		GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
4124 		GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
4125 		GMT->current.map.jump = &gmtmap_jump_not;
4126 		*search = true;
4127 	}
4128 	else {
4129 		if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "using global view\n");
4130 		/* No annotations or tickmarks in global mode */
4131 		for (i = 0; i < GMT_GRID_UPPER; i++) {
4132 			GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
4133 			GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
4134 		}
4135 		GMT->current.map.overlap = &gmtmap_genperg_overlap;
4136 		GMT->current.map.crossing = &gmtmap_radial_crossing;
4137 		GMT->current.map.clip = &gmtmap_radial_clip;
4138 		GMT->current.map.outside = &gmtmap_radial_outside;
4139 		GMT->current.map.left_edge = &gmtmap_left_circle;
4140 		GMT->current.map.right_edge = &gmtmap_right_circle;
4141 
4142 		if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4143 
4144 		*search = false;
4145   	}
4146 
4147 	if (GMT->current.proj.polar) {
4148 		if (GMT->current.proj.north_pole) {
4149 			if (GMT->common.R.wesn[YLO] < (90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YLO] = 90.0 - GMT->current.proj.f_horizon;
4150 			if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4151 		} else {
4152 			if (GMT->common.R.wesn[YHI] > -(90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YHI] = -(90.0 - GMT->current.proj.f_horizon);
4153 			if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4154 		}
4155 		if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
4156 				|| doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4157 			GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4158 	}
4159 
4160 	if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "xmin %f xmax %f ymin %f ymax %f\n", xmin/1000, xmax/1000, ymin/1000, ymax/1000);
4161 
4162 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
4163 
4164 	GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4165 
4166 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4167 
4168 	if (GMT->current.proj.g_debug > 0) {
4169 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x scale %e y scale %e\n", GMT->current.proj.scale[GMT_X], GMT->current.proj.scale[GMT_Y]);
4170 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x center %f y center %f\n", GMT->current.proj.c_x0, GMT->current.proj.c_y0);
4171 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x max %f y max %f\n", GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
4172 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x0 %f y0 %f\n\n", GMT->current.proj.origin[GMT_X], GMT->current.proj.origin[GMT_Y]);
4173 		fflush(NULL);
4174 	}
4175 
4176 	return (GMT_NOERROR);
4177 }
4178 
4179 /*
4180  *	TRANSFORMATION ROUTINES FOR THE GNOMONIC PROJECTION (GMT_GNOMONIC)
4181  */
4182 
4183 /*! . */
gmtmap_init_gnomonic(struct GMT_CTRL * GMT,bool * search)4184 GMT_LOCAL int gmtmap_init_gnomonic (struct GMT_CTRL *GMT, bool *search) {
4185 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
4186 
4187 	*search = GMT->common.R.oblique;
4188 
4189 	gmtmap_set_spherical (GMT, true);	/* PW: Force spherical for now */	gmtmap_set_polar (GMT);
4190 
4191 	if (GMT->current.proj.units_pr_degree) {
4192 		gmtproj_vgnomonic (GMT, 0.0, 90.0, 60.0);
4193 		gmtproj_gnomonic (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
4194 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
4195 	}
4196 	else
4197 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
4198 
4199 	gmtproj_vgnomonic (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
4200 	GMT->current.proj.fwd = &gmtproj_gnomonic;
4201 	GMT->current.proj.inv = &gmtproj_ignomonic;
4202 
4203 	if (GMT->common.R.oblique) {	/* Rectangular box given */
4204 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4205 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4206 
4207 		GMT->current.map.outside = &gmtmap_rect_outside2;
4208 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4209 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4210 		GMT->current.map.clip = &gmtmap_rect_clip;
4211 		GMT->current.map.left_edge = &gmtmap_left_rect;
4212 		GMT->current.map.right_edge = &gmtmap_right_rect;
4213 		GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
4214 		GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
4215 	}
4216 	else {
4217 		if (GMT->current.proj.polar) {	/* Polar aspect */
4218 			if (GMT->current.proj.north_pole) {
4219 				if (GMT->common.R.wesn[YLO] < (90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YLO] = 90.0 - GMT->current.proj.f_horizon;
4220 				if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4221 			}
4222 			else {
4223 				if (GMT->common.R.wesn[YHI] > -(90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YHI] = -(90.0 - GMT->current.proj.f_horizon);
4224 				if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4225 			}
4226 			if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
4227 					|| doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4228 				GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4229 			GMT->current.map.outside = &gmtmap_polar_outside;
4230 			GMT->current.map.crossing = &gmtmap_wesn_crossing;
4231 			GMT->current.map.overlap = &gmtmap_wesn_overlap;
4232 			GMT->current.map.clip = &map_wesn_clip;
4233 			GMT->current.map.frame.horizontal = 1;
4234 			GMT->current.map.n_lat_nodes = 2;
4235 			gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4236 		}
4237 		else {	/* Global view only */
4238 			GMT->common.R.wesn[XLO] = 0.0;
4239 			GMT->common.R.wesn[XHI] = 360.0;
4240 			GMT->common.R.wesn[YLO] = -90.0;
4241 			GMT->common.R.wesn[YHI] = 90.0;
4242 			xmax = ymax = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4243 			xmin = ymin = -xmax;
4244 			GMT->current.map.outside = &gmtmap_radial_outside;
4245 			GMT->current.map.crossing = &gmtmap_radial_crossing;
4246 			GMT->current.map.overlap = &gmtmap_radial_overlap;
4247 			GMT->current.map.clip = &gmtmap_radial_clip;
4248 			if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4249 		}
4250 		GMT->current.map.left_edge = &gmtmap_left_circle;
4251 		GMT->current.map.right_edge = &gmtmap_right_circle;
4252 	}
4253 
4254 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
4255 	GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4256 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4257 
4258 	return (GMT_NOERROR);
4259 }
4260 
4261 /*
4262  *	TRANSFORMATION ROUTINES FOR THE AZIMUTHAL EQUIDISTANT PROJECTION (GMT_AZ_EQDIST)
4263  */
4264 
4265 /*! . */
gmtmap_init_azeqdist(struct GMT_CTRL * GMT,bool * search)4266 GMT_LOCAL int gmtmap_init_azeqdist (struct GMT_CTRL *GMT, bool *search) {
4267 	unsigned int i;
4268 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
4269 
4270 	*search = GMT->common.R.oblique;
4271 
4272 	gmtmap_set_spherical (GMT, true);	/* PW: Force spherical for now */
4273 
4274 	gmtmap_set_polar (GMT);
4275 
4276 	if (GMT->current.proj.units_pr_degree) {
4277 		gmtproj_vazeqdist (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
4278 		gmtproj_azeqdist (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
4279 		if (gmt_M_is_zero (radius)) radius = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4280 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
4281 	}
4282 	else
4283 		GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
4284 
4285 	gmtproj_vazeqdist (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
4286 	GMT->current.proj.fwd = &gmtproj_azeqdist;
4287 	GMT->current.proj.inv = &gmtproj_iazeqdist;
4288 
4289 	if (GMT->common.R.oblique) {	/* Rectangular box given */
4290 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4291 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4292 
4293 		GMT->current.map.outside = &gmtmap_rect_outside2;
4294 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4295 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4296 		GMT->current.map.clip = &gmtmap_rect_clip;
4297 		GMT->current.map.left_edge = &gmtmap_left_rect;
4298 		GMT->current.map.right_edge = &gmtmap_right_rect;
4299 		GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
4300 		GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 60.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
4301 	}
4302 	else {
4303 		if (GMT->current.proj.polar && (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 180.0) {	/* Polar aspect */
4304 			if (!GMT->current.proj.north_pole && GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4305 			if (GMT->current.proj.north_pole && GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4306 			GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4307 			if (GMT->current.map.is_world || doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4308 				GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4309 			GMT->current.map.outside = &gmtmap_polar_outside;
4310 			GMT->current.map.crossing = &gmtmap_wesn_crossing;
4311 			GMT->current.map.overlap = &gmtmap_wesn_overlap;
4312 			GMT->current.map.clip = &map_wesn_clip;
4313 			GMT->current.map.frame.horizontal = 1;
4314 			GMT->current.map.n_lat_nodes = 2;
4315 			gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4316 		}
4317 		else {	/* Global view only, force wesn = 0/360/-90/90  */
4318 			/* No annotations or tickmarks in global mode */
4319 			GMT->current.map.is_world = true;
4320 			for (i = 0; i < GMT_GRID_UPPER; i++) {
4321 				GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
4322 				GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
4323 			}
4324 			GMT->common.R.wesn[XLO] = 0.0;
4325 			GMT->common.R.wesn[XHI] = 360.0;
4326 			GMT->common.R.wesn[YLO] = -90.0;
4327 			GMT->common.R.wesn[YHI] = 90.0;
4328 			xmax = ymax = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4329 			xmin = ymin = -xmax;
4330 			GMT->current.map.outside = &gmtmap_radial_outside;
4331 			GMT->current.map.crossing = &gmtmap_radial_crossing;
4332 			GMT->current.map.overlap = &gmtmap_radial_overlap;
4333 			GMT->current.map.clip = &gmtmap_radial_clip;
4334 			if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4335 		}
4336 		GMT->current.map.left_edge = &gmtmap_left_circle;
4337 		GMT->current.map.right_edge = &gmtmap_right_circle;
4338 	}
4339 
4340 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
4341 	GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4342 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4343 	if (GMT->current.proj.polar) GMT->current.map.meridian_straight = 1;
4344 
4345 	return (GMT_NOERROR);
4346 }
4347 
4348 /*
4349  *	TRANSFORMATION ROUTINES FOR THE MOLLWEIDE EQUAL AREA PROJECTION (GMT_MOLLWEIDE)
4350  */
4351 
4352 /*! . */
gmtmap_init_mollweide(struct GMT_CTRL * GMT,bool * search)4353 GMT_LOCAL int gmtmap_init_mollweide (struct GMT_CTRL *GMT, bool *search) {
4354 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, y, dummy;
4355 
4356 	*search = GMT->common.R.oblique;
4357 
4358 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4359 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4360 
4361 	if (gmtmap_central_meridian_not_set (GMT))
4362 		gmtmap_set_default_central_meridian (GMT);
4363 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4364 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4365 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4366 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = M_PI * GMT->current.proj.pars[1] / sqrt (8.0);
4367 	gmtproj_vmollweide (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4368 	if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4369 	if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4370 
4371 	if (GMT->common.R.oblique) {
4372 		gmtproj_mollweide (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4373 		gmtproj_mollweide (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4374 		GMT->current.map.outside = &gmtmap_rect_outside;
4375 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4376 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4377 		GMT->current.map.clip = &gmtmap_rect_clip;
4378 		GMT->current.map.left_edge = &gmtmap_left_rect;
4379 		GMT->current.map.right_edge = &gmtmap_right_rect;
4380 		GMT->current.map.frame.check_side = true;
4381 	}
4382 	else {
4383 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4384 		gmtproj_mollweide (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4385 		gmtproj_mollweide (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4386 		gmtproj_mollweide (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4387 		gmtproj_mollweide (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4388 		GMT->current.map.outside = &gmtmap_wesn_outside;
4389 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4390 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4391 		GMT->current.map.clip = &map_wesn_clip;
4392 		GMT->current.map.left_edge = &gmtmap_left_ellipse;
4393 		GMT->current.map.right_edge = &gmtmap_right_ellipse;
4394 		GMT->current.map.frame.horizontal = 2;
4395 		GMT->current.proj.polar = true;
4396 	}
4397 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4398 	GMT->current.proj.fwd = &gmtproj_mollweide;
4399 	GMT->current.proj.inv = &gmtproj_imollweide;
4400 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4401 	GMT->current.map.parallel_straight = 1;
4402 
4403 	return (GMT_NOERROR);
4404 }
4405 
4406 
4407 /*
4408  *	TRANSFORMATION ROUTINES FOR THE HAMMER-AITOFF EQUAL AREA PROJECTION (GMT_HAMMER)
4409  */
4410 
4411 /*! . */
gmtmap_init_hammer(struct GMT_CTRL * GMT,bool * search)4412 GMT_LOCAL int gmtmap_init_hammer (struct GMT_CTRL *GMT, bool *search) {
4413 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4414 
4415 	*search = GMT->common.R.oblique;
4416 
4417 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4418 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4419 
4420 	if (gmtmap_central_meridian_not_set (GMT))
4421 		gmtmap_set_default_central_meridian (GMT);
4422 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4423 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4424 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4425 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = 0.5 * M_PI * GMT->current.proj.pars[1] / M_SQRT2;
4426 	gmtproj_vhammer (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4427 	if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4428 	if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4429 
4430 	if (GMT->common.R.oblique) {
4431 		gmtproj_hammer (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4432 		gmtproj_hammer (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4433 		GMT->current.map.outside = &gmtmap_rect_outside;
4434 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4435 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4436 		GMT->current.map.clip = &gmtmap_rect_clip;
4437 		GMT->current.map.left_edge = &gmtmap_left_rect;
4438 		GMT->current.map.right_edge = &gmtmap_right_rect;
4439 		GMT->current.map.frame.check_side = true;
4440 	}
4441 	else {
4442 		double x, y, dummy = 0.0;
4443 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4444 		x = (fabs (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) > fabs (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian)) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
4445 		gmtproj_hammer (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4446 		gmtproj_hammer (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4447 		gmtproj_hammer (GMT, x, GMT->common.R.wesn[YLO], &dummy, &ymin);
4448 		gmtproj_hammer (GMT, x, GMT->common.R.wesn[YHI], &dummy, &ymax);
4449 		GMT->current.map.outside = &gmtmap_wesn_outside;
4450 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4451 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4452 		GMT->current.map.clip = &map_wesn_clip;
4453 		GMT->current.map.left_edge = &gmtmap_left_ellipse;
4454 		GMT->current.map.right_edge = &gmtmap_right_ellipse;
4455 		GMT->current.map.frame.horizontal = 2;
4456 		GMT->current.proj.polar = true;
4457 	}
4458 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4459 	GMT->current.proj.fwd = &gmtproj_hammer;
4460 	GMT->current.proj.inv = &gmtproj_ihammer;
4461 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4462 	return (GMT_NOERROR);
4463 }
4464 
4465 /*
4466  *	TRANSFORMATION ROUTINES FOR THE VAN DER GRINTEN PROJECTION (GMT_VANGRINTEN)
4467  */
4468 
4469 /*! . */
gmtmap_init_grinten(struct GMT_CTRL * GMT,bool * search)4470 GMT_LOCAL int gmtmap_init_grinten (struct GMT_CTRL *GMT, bool *search) {
4471 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4472 
4473 	*search = GMT->common.R.oblique;
4474 
4475 	gmtmap_set_spherical (GMT, true);
4476 
4477 	if (gmtmap_central_meridian_not_set (GMT))
4478 		gmtmap_set_default_central_meridian (GMT);
4479 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4480 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4481 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4482 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
4483 	gmtproj_vgrinten (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4484 	if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4485 	if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4486 
4487 	if (GMT->common.R.oblique) {
4488 		gmtproj_grinten (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4489 		gmtproj_grinten (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4490 		GMT->current.map.outside = &gmtmap_rect_outside;
4491 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4492 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4493 		GMT->current.map.clip = &gmtmap_rect_clip;
4494 		GMT->current.map.left_edge = &gmtmap_left_rect;
4495 		GMT->current.map.right_edge = &gmtmap_right_rect;
4496 		GMT->current.map.frame.check_side = true;
4497 	}
4498 	else {
4499 		double x, y, dummy = 0.0;
4500 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4501 		x = (fabs (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) > fabs (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian)) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
4502 		gmtproj_grinten (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4503 		gmtproj_grinten (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4504 		gmtproj_grinten (GMT, x, GMT->common.R.wesn[YLO], &dummy, &ymin);
4505 		gmtproj_grinten (GMT, x, GMT->common.R.wesn[YHI], &dummy, &ymax);
4506 		GMT->current.map.outside = &gmtmap_wesn_outside;
4507 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4508 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4509 		GMT->current.map.clip = &map_wesn_clip;
4510 		GMT->current.map.left_edge = &gmtmap_left_circle;
4511 		GMT->current.map.right_edge = &gmtmap_right_circle;
4512 		GMT->current.map.frame.horizontal = 2;
4513 		GMT->current.proj.polar = true;
4514 	}
4515 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4516 	GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4517 	GMT->current.proj.fwd = &gmtproj_grinten;
4518 	GMT->current.proj.inv = &gmtproj_igrinten;
4519 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4520 	return (GMT_NOERROR);
4521 }
4522 
4523 /*
4524  *	TRANSFORMATION ROUTINES FOR THE WINKEL-TRIPEL MODIFIED AZIMUTHAL PROJECTION (GMT_WINKEL)
4525  */
4526 
4527 /*! . */
gmtmap_init_winkel(struct GMT_CTRL * GMT,bool * search)4528 GMT_LOCAL int gmtmap_init_winkel (struct GMT_CTRL *GMT, bool *search) {
4529 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4530 
4531 	*search = GMT->common.R.oblique;
4532 
4533 	gmtmap_set_spherical (GMT, true);	/* PW: Force spherical for now */
4534 
4535 	if (gmtmap_central_meridian_not_set (GMT))
4536 		gmtmap_set_default_central_meridian (GMT);
4537 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4538 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4539 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4540 	gmtproj_vwinkel (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4541 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = 2.0 * GMT->current.proj.pars[1] / (1.0 + GMT->current.proj.r_cosphi1);
4542 
4543 	if (GMT->common.R.oblique) {
4544 		gmtproj_winkel (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4545 		gmtproj_winkel (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4546 		GMT->current.map.outside = &gmtmap_rect_outside;
4547 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4548 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4549 		GMT->current.map.clip = &gmtmap_rect_clip;
4550 		GMT->current.map.left_edge = &gmtmap_left_rect;
4551 		GMT->current.map.right_edge = &gmtmap_right_rect;
4552 		GMT->current.map.frame.check_side = true;
4553 	}
4554 	else {
4555 		double x, y, dummy = 0.0;
4556 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4557 		x = (fabs (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) > fabs (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian)) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
4558 		gmtproj_winkel (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4559 		gmtproj_winkel (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4560 		gmtproj_winkel (GMT, x, GMT->common.R.wesn[YLO], &dummy, &ymin);
4561 		gmtproj_winkel (GMT, x, GMT->common.R.wesn[YHI], &dummy, &ymax);
4562 		GMT->current.map.outside = &gmtmap_wesn_outside;
4563 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4564 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4565 		GMT->current.map.clip = &map_wesn_clip;
4566 		GMT->current.map.left_edge = &gmtproj_left_winkel;
4567 		GMT->current.map.right_edge = &gmtproj_right_winkel;
4568 		GMT->current.map.frame.horizontal = 2;
4569 		/* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4570 		if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4571 	}
4572 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4573 	GMT->current.proj.fwd = &gmtproj_winkel;
4574 	GMT->current.proj.inv = &gmtproj_iwinkel;
4575 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4576 	return (GMT_NOERROR);
4577 }
4578 
4579 /*
4580  *	TRANSFORMATION ROUTINES FOR THE ECKERT IV PROJECTION (GMT_ECKERT4)
4581  */
4582 
4583 /*! . */
gmtmap_init_eckert4(struct GMT_CTRL * GMT,bool * search)4584 GMT_LOCAL int gmtmap_init_eckert4 (struct GMT_CTRL *GMT, bool *search) {
4585 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4586 
4587 	*search = GMT->common.R.oblique;
4588 
4589 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4590 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4591 
4592 	if (gmtmap_central_meridian_not_set (GMT))
4593 		gmtmap_set_default_central_meridian (GMT);
4594 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4595 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4596 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4597 	gmtproj_veckert4 (GMT, GMT->current.proj.pars[0]);
4598 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
4599 
4600 	if (GMT->common.R.oblique) {
4601 		gmtproj_eckert4 (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4602 		gmtproj_eckert4 (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4603 		GMT->current.map.outside = &gmtmap_rect_outside;
4604 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4605 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4606 		GMT->current.map.clip = &gmtmap_rect_clip;
4607 		GMT->current.map.left_edge = &gmtmap_left_rect;
4608 		GMT->current.map.right_edge = &gmtmap_right_rect;
4609 		GMT->current.map.frame.check_side = true;
4610 	}
4611 	else {
4612 		double y, dummy = 0.0;
4613 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4614 		gmtproj_eckert4 (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4615 		gmtproj_eckert4 (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4616 		gmtproj_eckert4 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4617 		gmtproj_eckert4 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4618 		GMT->current.map.outside = &gmtmap_wesn_outside;
4619 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4620 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4621 		GMT->current.map.clip = &map_wesn_clip;
4622 		GMT->current.map.left_edge = &gmtproj_left_eckert4;
4623 		GMT->current.map.right_edge = &gmtproj_right_eckert4;
4624 		GMT->current.map.frame.horizontal = 2;
4625 		/* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4626 		if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4627 	}
4628 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4629 	GMT->current.proj.fwd = &gmtproj_eckert4;
4630 	GMT->current.proj.inv = &gmtproj_ieckert4;
4631 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4632 	GMT->current.map.parallel_straight = 1;
4633 
4634 	return (GMT_NOERROR);
4635 }
4636 
4637 /*
4638  *	TRANSFORMATION ROUTINES FOR THE ECKERT VI PROJECTION (GMT_ECKERT6)
4639  */
4640 
4641 /*! . */
gmtmap_init_eckert6(struct GMT_CTRL * GMT,bool * search)4642 GMT_LOCAL int gmtmap_init_eckert6 (struct GMT_CTRL *GMT, bool *search) {
4643 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4644 
4645 	*search = GMT->common.R.oblique;
4646 
4647 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4648 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4649 
4650 	if (gmtmap_central_meridian_not_set (GMT))
4651 		gmtmap_set_default_central_meridian (GMT);
4652 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4653 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4654 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4655 	gmtproj_veckert6 (GMT, GMT->current.proj.pars[0]);
4656 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = 0.5 * GMT->current.proj.pars[1] * sqrt (2.0 + M_PI);
4657 
4658 	if (GMT->common.R.oblique) {
4659 		gmtproj_eckert6 (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4660 		gmtproj_eckert6 (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4661 		GMT->current.map.outside = &gmtmap_rect_outside;
4662 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4663 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4664 		GMT->current.map.clip = &gmtmap_rect_clip;
4665 		GMT->current.map.left_edge = &gmtmap_left_rect;
4666 		GMT->current.map.right_edge = &gmtmap_right_rect;
4667 		GMT->current.map.frame.check_side = true;
4668 	}
4669 	else {
4670 		double y, dummy = 0.0;
4671 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4672 		gmtproj_eckert6 (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4673 		gmtproj_eckert6 (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4674 		gmtproj_eckert6 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4675 		gmtproj_eckert6 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4676 		GMT->current.map.outside = &gmtmap_wesn_outside;
4677 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4678 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4679 		GMT->current.map.clip = &map_wesn_clip;
4680 		GMT->current.map.left_edge = &gmtproj_left_eckert6;
4681 		GMT->current.map.right_edge = &gmtproj_right_eckert6;
4682 		GMT->current.map.frame.horizontal = 2;
4683 		/* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4684 		if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4685 	}
4686 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4687 	GMT->current.proj.fwd = &gmtproj_eckert6;
4688 	GMT->current.proj.inv = &gmtproj_ieckert6;
4689 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4690 	GMT->current.map.parallel_straight = 1;
4691 
4692 	return (GMT_NOERROR);
4693 }
4694 
4695 /*
4696  *	TRANSFORMATION ROUTINES FOR THE ROBINSON PSEUDOCYLINDRICAL PROJECTION (GMT_ROBINSON)
4697  */
4698 
4699 /*! . */
gmtmap_init_robinson(struct GMT_CTRL * GMT,bool * search)4700 GMT_LOCAL int gmtmap_init_robinson (struct GMT_CTRL *GMT, bool *search) {
4701 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4702 
4703 	*search = GMT->common.R.oblique;
4704 
4705 	gmtmap_set_spherical (GMT, true);	/* PW: Force spherical for now */
4706 
4707 	if (gmtmap_central_meridian_not_set (GMT))
4708 		gmtmap_set_default_central_meridian (GMT);
4709 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4710 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4711 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4712 	gmtproj_vrobinson (GMT, GMT->current.proj.pars[0]);
4713 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1] / 0.8487;
4714 
4715 	if (GMT->common.R.oblique) {
4716 		gmtproj_robinson (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4717 		gmtproj_robinson (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4718 		GMT->current.map.outside = &gmtmap_rect_outside;
4719 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4720 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4721 		GMT->current.map.clip = &gmtmap_rect_clip;
4722 		GMT->current.map.left_edge = &gmtmap_left_rect;
4723 		GMT->current.map.right_edge = &gmtmap_right_rect;
4724 		GMT->current.map.frame.check_side = true;
4725 	}
4726 	else {
4727 		double y, dummy = 0.0;
4728 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4729 		gmtproj_robinson (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4730 		gmtproj_robinson (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4731 		gmtproj_robinson (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4732 		gmtproj_robinson (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4733 		GMT->current.map.outside = &gmtmap_wesn_outside;
4734 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4735 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4736 		GMT->current.map.clip = &map_wesn_clip;
4737 		GMT->current.map.left_edge = &gmtproj_left_robinson;
4738 		GMT->current.map.right_edge = &gmtproj_right_robinson;
4739 		GMT->current.map.frame.horizontal = 2;
4740 		/* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4741 		if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4742 	}
4743 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4744 	GMT->current.proj.fwd = &gmtproj_robinson;
4745 	GMT->current.proj.inv = &gmtproj_irobinson;
4746 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4747 	GMT->current.map.parallel_straight = 1;
4748 
4749 	return (GMT_NOERROR);
4750 }
4751 
4752 /*
4753  *	TRANSFORMATION ROUTINES FOR THE SINUSOIDAL EQUAL AREA PROJECTION (GMT_SINUSOIDAL)
4754  */
4755 
4756 /*! . */
gmtmap_init_sinusoidal(struct GMT_CTRL * GMT,bool * search)4757 GMT_LOCAL int gmtmap_init_sinusoidal (struct GMT_CTRL *GMT, bool *search) {
4758 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4759 
4760 	*search = GMT->common.R.oblique;
4761 
4762 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4763 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4764 
4765 	if (gmtmap_central_meridian_not_set (GMT))
4766 		gmtmap_set_default_central_meridian (GMT);
4767 	if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4768 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4769 	if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4770 	if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4771 	gmtproj_vsinusoidal (GMT, GMT->current.proj.pars[0]);
4772 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4773 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
4774 	GMT->current.proj.fwd = &gmtproj_sinusoidal;
4775 	GMT->current.proj.inv = &gmtproj_isinusoidal;
4776 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4777 
4778 	if (GMT->common.R.oblique) {
4779 		gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4780 		gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4781 		GMT->current.map.outside = &gmtmap_rect_outside;
4782 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4783 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4784 		GMT->current.map.clip = &gmtmap_rect_clip;
4785 		GMT->current.map.left_edge = &gmtmap_left_rect;
4786 		GMT->current.map.right_edge = &gmtmap_right_rect;
4787 		GMT->current.map.frame.check_side = true;
4788 	}
4789 	else {
4790 		double dummy = 0.0, y;
4791 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4792 		gmtproj_sinusoidal (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4793 		gmtproj_sinusoidal (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4794 		gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4795 		gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4796 		GMT->current.map.outside = &gmtmap_wesn_outside;
4797 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4798 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4799 		GMT->current.map.clip = &map_wesn_clip;
4800 		GMT->current.map.left_edge = &gmtproj_left_sinusoidal;
4801 		GMT->current.map.right_edge = &gmtproj_right_sinusoidal;
4802 		GMT->current.map.frame.horizontal = 2;
4803 		GMT->current.proj.polar = true;
4804 	}
4805 
4806 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4807 	GMT->current.map.parallel_straight = 1;
4808 
4809 	return (GMT_NOERROR);
4810 }
4811 
4812 /*
4813  *	TRANSFORMATION ROUTINES FOR THE CASSINI PROJECTION (GMT_CASSINI)
4814  */
4815 
4816 /*! . */
gmtmap_init_cassini(struct GMT_CTRL * GMT,bool * search)4817 GMT_LOCAL int gmtmap_init_cassini (struct GMT_CTRL *GMT, bool *search) {
4818 	bool too_big;
4819 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, L = 0.0, R = 0.0;
4820 
4821 	*search = GMT->common.R.oblique;
4822 
4823 	if (gmtmap_central_meridian_not_set (GMT))
4824 		gmtmap_set_default_central_meridian (GMT);
4825 	if ((L = (GMT->current.proj.pars[0] - GMT->common.R.wesn[XLO])) > 90.0 || (R = (GMT->common.R.wesn[XHI] - GMT->current.proj.pars[0])) > 90.0) {
4826 		double D = 0.0;
4827 		if (L > 0.0) D = 360.0; else if (R > 0.0) D = -360.0;	/* Try the relevant 360-degree shift */
4828 		GMT->common.R.wesn[XLO] += D;	GMT->common.R.wesn[XHI] += D;	/* Try to shift the range */
4829 		if ((GMT->current.proj.pars[0] - GMT->common.R.wesn[XLO]) > 90.0 || (GMT->common.R.wesn[XHI] - GMT->current.proj.pars[0]) > 90.0) {
4830 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Max longitude extension away from central meridian is limited to +/- 90 degrees\n");
4831 			return GMT_PROJECTION_ERROR;
4832 		}
4833 	}
4834 	too_big = gmtmap_quicktm (GMT, GMT->current.proj.pars[0], 4.0);
4835 	if (too_big) gmtmap_set_spherical (GMT, true);	/* Cannot use ellipsoidal series for this area */
4836 	gmtproj_vcassini (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4837 	if (gmt_M_is_spherical (GMT)) {
4838 		GMT->current.proj.fwd = &gmtproj_cassini_sph;
4839 		GMT->current.proj.inv = &gmtproj_icassini_sph;
4840 	}
4841 	else {
4842 		GMT->current.proj.fwd = &gmtproj_cassini;
4843 		GMT->current.proj.inv = &gmtproj_icassini;
4844 	}
4845 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
4846 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
4847 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4848 
4849 	if (GMT->common.R.oblique) {
4850 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4851 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4852 		GMT->current.map.outside = &gmtmap_rect_outside;
4853 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4854 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4855 		GMT->current.map.clip = &gmtmap_rect_clip;
4856 		GMT->current.map.left_edge = &gmtmap_left_rect;
4857 		GMT->current.map.right_edge = &gmtmap_right_rect;
4858 		GMT->current.map.frame.check_side = true;
4859 	}
4860 	else {
4861 		gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4862 		GMT->current.map.outside = &gmtmap_wesn_outside;
4863 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4864 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4865 		GMT->current.map.clip = &map_wesn_clip;
4866 		GMT->current.map.left_edge = &gmtmap_left_conic;
4867 		GMT->current.map.right_edge = &gmtmap_right_conic;
4868 	}
4869 
4870 	GMT->current.map.frame.horizontal = 1;
4871 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
4872 
4873 	return (GMT_NOERROR);
4874 }
4875 
4876 /*
4877  *	TRANSFORMATION ROUTINES FOR THE ALBERS PROJECTION (GMT_ALBERS)
4878  */
4879 
4880 /*! . */
gmtmap_init_albers(struct GMT_CTRL * GMT,bool * search)4881 GMT_LOCAL int gmtmap_init_albers (struct GMT_CTRL *GMT, bool *search) {
4882 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dy, az, x1, y1;
4883 
4884 	*search = GMT->common.R.oblique;
4885 
4886 	GMT->current.proj.GMT_convert_latitudes = gmtmap_quickconic (GMT);
4887 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4888 	if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) {	/* Spherical code w/wo authalic latitudes */
4889 		gmtproj_valbers_sph (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
4890 		GMT->current.proj.fwd = &gmtproj_albers_sph;
4891 		GMT->current.proj.inv = &gmtproj_ialbers_sph;
4892 	}
4893 	else {
4894 		gmtproj_valbers (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
4895 		GMT->current.proj.fwd = &gmtproj_albers;
4896 		GMT->current.proj.inv = &gmtproj_ialbers;
4897 	}
4898 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG;
4899 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
4900 
4901 	if (GMT->common.R.oblique) {
4902 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4903 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4904 		GMT->current.map.outside = &gmtmap_rect_outside;
4905 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4906 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4907 		GMT->current.map.clip = &gmtmap_rect_clip;
4908 		GMT->current.map.left_edge = &gmtmap_left_rect;
4909 		GMT->current.map.right_edge = &gmtmap_right_rect;
4910 		GMT->current.map.frame.check_side = true;
4911 	}
4912 	else {
4913 		gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4914 		GMT->current.map.outside = &gmtmap_wesn_outside;
4915 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4916 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4917 		GMT->current.map.clip = &map_wesn_clip;
4918 		GMT->current.map.left_edge = &gmtmap_left_conic;
4919 		GMT->current.map.right_edge = &gmtmap_right_conic;
4920 	}
4921 	GMT->current.map.frame.horizontal = 1;
4922 	GMT->current.map.n_lat_nodes = 2;
4923 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
4924 
4925 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4926 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian + 90., GMT->current.proj.pole, &x1, &y1);
4927 	dy = y1 - GMT->current.proj.c_y0;
4928 	az = 2.0 * d_atan2 (dy, x1 - GMT->current.proj.c_x0);
4929 	dy /= (1.0 - cos (az));
4930 	GMT->current.proj.c_y0 += dy;
4931 	GMT->current.map.meridian_straight = 1;
4932 
4933 	return (GMT_NOERROR);
4934 }
4935 
4936 
4937 /*
4938  *	TRANSFORMATION ROUTINES FOR THE EQUIDISTANT CONIC PROJECTION (GMT_ECONIC)
4939  */
4940 
4941 
4942 /*! . */
gmtmap_init_econic(struct GMT_CTRL * GMT,bool * search)4943 GMT_LOCAL int gmtmap_init_econic (struct GMT_CTRL *GMT, bool *search) {
4944 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dy, az, x1, y1;
4945 
4946 	*search = GMT->common.R.oblique;
4947 
4948 	GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4949 	if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4950 	gmtproj_veconic (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
4951 	GMT->current.proj.fwd = &gmtproj_econic;
4952 	GMT->current.proj.inv = &gmtproj_ieconic;
4953 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG;
4954 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
4955 
4956 	if (GMT->common.R.oblique) {
4957 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4958 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4959 		GMT->current.map.outside = &gmtmap_rect_outside;
4960 		GMT->current.map.crossing = &gmtmap_rect_crossing;
4961 		GMT->current.map.overlap = &gmtmap_rect_overlap;
4962 		GMT->current.map.clip = &gmtmap_rect_clip;
4963 		GMT->current.map.left_edge = &gmtmap_left_rect;
4964 		GMT->current.map.right_edge = &gmtmap_right_rect;
4965 	}
4966 	else {
4967 		gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4968 		GMT->current.map.outside = &gmtmap_wesn_outside;
4969 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
4970 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
4971 		GMT->current.map.clip = &map_wesn_clip;
4972 		GMT->current.map.left_edge = &gmtmap_left_conic;
4973 		GMT->current.map.right_edge = &gmtmap_right_conic;
4974 	}
4975 	GMT->current.map.frame.horizontal = 1;
4976 	GMT->current.map.n_lat_nodes = 2;
4977 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
4978 
4979 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4980 	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian + 90., GMT->current.proj.pole, &x1, &y1);
4981 	dy = y1 - GMT->current.proj.c_y0;
4982 	az = 2.0 * d_atan2 (dy, x1 - GMT->current.proj.c_x0);
4983 	dy /= (1.0 - cos (az));
4984 	GMT->current.proj.c_y0 += dy;
4985 	GMT->current.map.meridian_straight = 1;
4986 
4987 	return (GMT_NOERROR);
4988 }
4989 
4990 /*
4991  *	TRANSFORMATION ROUTINES FOR THE POLYCONIC PROJECTION (GMT_POLYCONIC)
4992  */
4993 
4994 /*! . */
gmtmap_init_polyconic(struct GMT_CTRL * GMT,bool * search)4995 GMT_LOCAL int gmtmap_init_polyconic (struct GMT_CTRL *GMT, bool *search) {
4996 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4997 
4998 	*search = GMT->common.R.oblique;
4999 
5000 	gmtmap_set_spherical (GMT, true);	/* PW: Force spherical for now */
5001 
5002 	if (gmtmap_central_meridian_not_set (GMT))
5003 		gmtmap_set_default_central_meridian (GMT);
5004 	GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
5005 	if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
5006 	if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
5007 	gmtproj_vpolyconic (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
5008 	if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
5009 	GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
5010 	GMT->current.proj.fwd = &gmtproj_polyconic;
5011 	GMT->current.proj.inv = &gmtproj_ipolyconic;
5012 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
5013 
5014 	if (GMT->common.R.oblique) {
5015 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
5016 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
5017 		GMT->current.map.outside = &gmtmap_rect_outside;
5018 		GMT->current.map.crossing = &gmtmap_rect_crossing;
5019 		GMT->current.map.overlap = &gmtmap_rect_overlap;
5020 		GMT->current.map.clip = &gmtmap_rect_clip;
5021 		GMT->current.map.left_edge = &gmtmap_left_rect;
5022 		GMT->current.map.right_edge = &gmtmap_right_rect;
5023 		GMT->current.map.frame.check_side = true;
5024 	}
5025 	else {
5026 		double y, dummy = 0.0;
5027 		y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
5028 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
5029 		(*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
5030 		(*GMT->current.proj.fwd) (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
5031 		(*GMT->current.proj.fwd) (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
5032 		GMT->current.map.outside = &gmtmap_wesn_outside;
5033 		GMT->current.map.crossing = &gmtmap_wesn_crossing;
5034 		GMT->current.map.overlap = &gmtmap_wesn_overlap;
5035 		GMT->current.map.clip = &map_wesn_clip;
5036 		GMT->current.map.left_edge = &gmtproj_left_polyconic;
5037 		GMT->current.map.right_edge = &gmtproj_right_polyconic;
5038 		GMT->current.proj.polar = true;
5039 	}
5040 
5041 	GMT->current.map.frame.horizontal = 1;
5042 	gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
5043 
5044 	return (GMT_NOERROR);
5045 }
5046 
5047 #ifdef HAVE_GDAL
5048 /*!
5049  *	TRANSFORMATION ROUTINES FOR PROJ4 TRANSFORMATIONS (GMT_PROJ4_PROJS)
5050  */
map_init_proj4(struct GMT_CTRL * GMT,bool * search)5051  GMT_LOCAL int map_init_proj4 (struct GMT_CTRL *GMT, bool *search) {
5052 	/*
5053 	*  Here we use the trick of letting the previous GMT functions do the necessary initializations
5054 	*  and at the end just replace the pointers to the FWD & INV transform functions to those of GDAL.
5055 	*/
5056 	int error = GMT_NOERROR;
5057 	double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
5058 
5059 	*search = false;
5060 	switch (GMT->current.proj.projection_GMT) {
5061 		case GMT_LINEAR:        error = gmtmap_init_linear (GMT, search); break;      /* Linear transformations */
5062 		case GMT_POLAR:         error = gmtmap_init_polar (GMT, search); break;       /* Both lon/lat are actually theta, radius */
5063 		case GMT_MERCATOR:      error = gmtmap_init_merc (GMT, search); break;        /* Standard Mercator projection */
5064 		case GMT_STEREO:        error = gmtmap_init_stereo (GMT, search); break;      /* Stereographic projection */
5065 		case GMT_LAMBERT:       error = gmtmap_init_lambert (GMT, search); break;     /* Lambert Conformal Conic */
5066 		case GMT_OBLIQUE_MERC:  error = gmtmap_init_oblique (GMT, search); break;     /* Oblique Mercator */
5067 		case GMT_TM:            error = gmtmap_init_tm (GMT, search); break;          /* Transverse Mercator */
5068 		case GMT_UTM:           error = gmtmap_init_utm (GMT, search); break;         /* Universal Transverse Mercator */
5069 		case GMT_LAMB_AZ_EQ:    error = gmtmap_init_lambeq (GMT, search); break;      /* Lambert Azimuthal Equal-Area */
5070 		case GMT_ORTHO:         error = gmtmap_init_ortho (GMT, search); break;       /* Orthographic Projection */
5071 		case GMT_GENPER:        error = gmtmap_init_genper (GMT, search); break;      /* General Perspective Projection */
5072 		case GMT_AZ_EQDIST:     error = gmtmap_init_azeqdist (GMT, search); break;    /* Azimuthal Equal-Distance Projection */
5073 		case GMT_GNOMONIC:      error = gmtmap_init_gnomonic (GMT, search); break;    /* Azimuthal Gnomonic Projection */
5074 		case GMT_MOLLWEIDE:     error = gmtmap_init_mollweide (GMT, search); break;   /* Mollweide Equal-Area */
5075 		case GMT_HAMMER:        error = gmtmap_init_hammer (GMT, search); break;      /* Hammer-Aitoff Equal-Area */
5076 		case GMT_VANGRINTEN:    error = gmtmap_init_grinten (GMT, search); break;     /* Van der Grinten */
5077 		case GMT_WINKEL:        error = gmtmap_init_winkel (GMT, search); break;      /* Winkel Tripel */
5078 		case GMT_ECKERT4:       error = gmtmap_init_eckert4 (GMT, search); break;     /* Eckert IV */
5079 		case GMT_ECKERT6:       error = gmtmap_init_eckert6 (GMT, search); break;     /* Eckert VI */
5080 		case GMT_CYL_EQ:        error = gmtmap_init_cyleq (GMT, search); break;       /* Cylindrical Equal-Area */
5081 		case GMT_CYL_STEREO:    error = gmtmap_init_cylstereo (GMT, search); break;   /* Cylindrical Stereographic */
5082 		case GMT_MILLER:        error = gmtmap_init_miller (GMT, search); break;      /* Miller Cylindrical */
5083 		case GMT_CYL_EQDIST:    error = gmtmap_init_cyleqdist (GMT, search); break;   /* Cylindrical Equidistant */
5084 		case GMT_ROBINSON:      error = gmtmap_init_robinson (GMT, search); break;    /* Robinson */
5085 		case GMT_SINUSOIDAL:    error = gmtmap_init_sinusoidal (GMT, search); break;  /* Sinusoidal Equal-Area */
5086 		case GMT_CASSINI:       error = gmtmap_init_cassini (GMT, search); break;     /* Cassini cylindrical */
5087 		case GMT_ALBERS:        error = gmtmap_init_albers (GMT, search); break;      /* Albers Equal-Area Conic */
5088 		case GMT_ECONIC:        error = gmtmap_init_econic (GMT, search); break;      /* Equidistant Conic */
5089 		case GMT_POLYCONIC:     error = gmtmap_init_polyconic (GMT, search); break;   /* Polyconic */
5090 		default:	/* Non-GMT proj4 projection.  Try to assign functions */
5091 			GMT->current.proj.fwd = &gmt_proj4_fwd;
5092 			GMT->current.proj.inv = &gmt_proj4_inv;
5093 			GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.proj4_scl;
5094 			GMT->current.map.n_lon_nodes = 360;	GMT->current.map.n_lat_nodes = 180;
5095 			if (GMT->common.R.oblique) {
5096 				gmt_proj4_fwd (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
5097 				gmt_proj4_fwd (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
5098 				GMT->current.map.outside = &gmtmap_rect_outside;
5099 				GMT->current.map.crossing = &gmtmap_rect_crossing;
5100 				GMT->current.map.overlap = &gmtmap_rect_overlap;
5101 				GMT->current.map.clip = &gmtmap_rect_clip;
5102 				GMT->current.map.left_edge = &gmtmap_left_rect;
5103 				GMT->current.map.right_edge = &gmtmap_right_rect;
5104 				GMT->current.map.frame.check_side = true;
5105 			}
5106 			else {
5107 				gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
5108 				GMT->current.map.outside = &gmtmap_wesn_outside;
5109 				GMT->current.map.crossing = &gmtmap_wesn_crossing;
5110 				GMT->current.map.overlap = &gmtmap_wesn_overlap;
5111 				GMT->current.map.clip = &map_wesn_clip;
5112 				GMT->current.map.left_edge = &gmtmap_left_rect;
5113 				GMT->current.map.right_edge = &gmtmap_right_rect;
5114 				GMT->current.map.frame.horizontal = 2;
5115 			}
5116 			gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.proj4_scl);
5117 			if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
5118 			break;
5119 	}
5120 	/* Now we only have to replace the pointers to the FWD and INV transform functions */
5121 	GMT->current.proj.fwd = &gmt_proj4_fwd;
5122 	GMT->current.proj.inv = &gmt_proj4_inv;
5123 	return error;
5124 }
5125 #endif
5126 
5127 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5128  *
5129  *	S E C T I O N  1.1 :	S U P P O R T I N G   R O U T I N E S
5130  *
5131  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
5132 
5133 /*! . */
gmt_wesn_search(struct GMT_CTRL * GMT,double xmin,double xmax,double ymin,double ymax,double * west,double * east,double * south,double * north,bool add_pad)5134 void gmt_wesn_search (struct GMT_CTRL *GMT, double xmin, double xmax, double ymin, double ymax, double *west, double *east, double *south, double *north, bool add_pad) {
5135 	double dx, dy, w, e, s, n, x, y, lat, *lon = NULL;
5136 	unsigned int i, j, k;
5137 
5138 	/* Search for extreme lon/lat coordinates by matching along the rectangular boundary */
5139 
5140 	if (!GMT->current.map.n_lon_nodes) GMT->current.map.n_lon_nodes = urint (GMT->current.map.width / GMT->current.setting.map_line_step);
5141 	if (!GMT->current.map.n_lat_nodes) GMT->current.map.n_lat_nodes = urint (GMT->current.map.height / GMT->current.setting.map_line_step);
5142 
5143 	dx = (xmax - xmin) / GMT->current.map.n_lon_nodes;
5144 	dy = (ymax - ymin) / GMT->current.map.n_lat_nodes;
5145 	/* Need temp array to hold all the longitudes we compute */
5146 	lon = gmt_M_memory (GMT, NULL, 2 * (GMT->current.map.n_lon_nodes + GMT->current.map.n_lat_nodes + 2), double);
5147 	w = s = DBL_MAX;	e = n = -DBL_MAX;
5148 	for (i = k = 0; i <= GMT->current.map.n_lon_nodes; i++) {
5149 		x = (i == GMT->current.map.n_lon_nodes) ? xmax : xmin + i * dx;
5150 		gmt_xy_to_geo (GMT, &lon[k++], &lat, x, ymin);
5151 		if (lat < s) s = lat;
5152 		if (lat > n) n = lat;
5153 		gmt_xy_to_geo (GMT, &lon[k++], &lat, x, ymax);
5154 		if (lat < s) s = lat;
5155 		if (lat > n) n = lat;
5156 	}
5157 	for (j = 0; j <= GMT->current.map.n_lat_nodes; j++) {
5158 		y = (j == GMT->current.map.n_lat_nodes) ? ymax : ymin + j * dy;
5159 		gmt_xy_to_geo (GMT, &lon[k++], &lat, xmin, y);
5160 		if (lat < s) s = lat;
5161 		if (lat > n) n = lat;
5162 		gmt_xy_to_geo (GMT, &lon[k++], &lat, xmax, y);
5163 		if (lat < s) s = lat;
5164 		if (lat > n) n = lat;
5165 	}
5166 	gmtlib_get_lon_minmax (GMT, lon, k, &w, &e);	/* Determine lon-range by robust quadrant check */
5167 	gmt_M_free (GMT, lon);
5168 
5169 	/* Then check if one or both poles are inside map; then the above won't be correct */
5170 
5171 	if (GMT->current.proj.pole_in_map[0]) { s = -90.0; w = 0.0; e = 360.0; }
5172 	if (GMT->current.proj.pole_in_map[1]) { n = +90.0; w = 0.0; e = 360.0; }
5173 
5174 	if (add_pad) {	/* Extend by 0.1 degrees, if possible */
5175 		s -= 0.1;	if (s < -90.0) s = -90.0;	/* Make sure point is not inside area, 0.1 is just a small arbitrary number */
5176 		n += 0.1;	if (n > 90.0) n = 90.0;		/* But don't go crazy beyond the pole */
5177 		w -= 0.1;	e += 0.1;	if (fabs (w - e) > 360.0) { w = 0.0; e = 360.0; }	/* Ensure max 360 range */
5178 	}
5179 	*west = w;	*east = e;	*south = s;	*north = n;	/* Pass back our findings */
5180 }
5181 
5182 /*! . */
gmtmap_genper_search(struct GMT_CTRL * GMT,double * west,double * east,double * south,double * north,bool add_pad)5183 GMT_LOCAL void gmtmap_genper_search (struct GMT_CTRL *GMT, double *west, double *east, double *south, double *north, bool add_pad) {
5184 	double w, e, s = 90.0, n = -90.0, *work_x = NULL, *work_y = NULL;
5185 	uint64_t np, k;
5186 	struct GMT_DATASEGMENT *S = gmt_get_segment (GMT, 2);
5187 	/* Because the genper clip path may be a mix of straight borders and a curved horizon, we must determine this
5188 	 * clip path and search along it, getting lon,lat along the way, and find the extreme values to use. Before this
5189 	 * function was added, we ended up in the gmt_wesn_search function which would fail along the horizon in many
5190 	 * cases.  P. Wessel, July 20, 2019. */
5191 
5192 	np = (GMT->current.proj.polar && (GMT->common.R.wesn[YLO] <= -90.0 || GMT->common.R.wesn[YHI] >= 90.0)) ? GMT->current.map.n_lon_nodes + 2: 2 * (GMT->current.map.n_lon_nodes + 1);
5193 	work_x = gmt_M_memory (GMT, NULL, np, double);
5194 	work_y = gmt_M_memory (GMT, NULL, np, double);
5195 	gmtlib_genper_map_clip_path (GMT, np, work_x, work_y);
5196 
5197 	/* Search for extreme lon/lat coordinates by matching along the genper boundary */
5198 
5199 	/* Need temp datasegment to hold all the longitudes and latitudes we compute */
5200 	gmt_alloc_segment (GMT, S, np, 2, 0, true);
5201 	for (k = 0; k < np; k++) {
5202 		gmt_xy_to_geo (GMT, &S->data[GMT_X][k], &S->data[GMT_Y][k], work_x[k], work_y[k]);
5203 		if (S->data[GMT_Y][k] < s) s = S->data[GMT_Y][k];
5204 		if (S->data[GMT_Y][k] > n) n = S->data[GMT_Y][k];
5205 	}
5206 	gmt_M_free (GMT, work_x);
5207 	gmt_M_free (GMT, work_y);
5208 	gmt_set_seg_minmax (GMT, GMT_IS_POLY, 2, S);	/* Update min/max of x/y */
5209 
5210 	gmtlib_get_lon_minmax (GMT, S->data[GMT_X], np, &w, &e);	/* Determine lon-range by robust quadrant check */
5211 
5212 	/* Then check if one or both poles are inside map; then the above won't be correct.
5213 	 * Because the clip path for genper may be some weird rounded octagon we must use the polygon directly. */
5214 
5215 	gmt_set_inside_mode (GMT, NULL, GMT_IOO_SPHERICAL);	/* Ensure spherical treatment */
5216 	if (gmt_inonout (GMT, GMT->current.proj.central_meridian, -90.0, S) == GMT_INSIDE) { s = -90.0; w = 0.0; e = 360.0; }
5217 	if (gmt_inonout (GMT, GMT->current.proj.central_meridian, +90.0, S) == GMT_INSIDE) { n = +90.0; w = 0.0; e = 360.0; }
5218 
5219 	gmt_free_segment (GMT, &S);
5220 
5221 	if (add_pad) {	/* Extend by 0.1 degrees */
5222 		s -= 0.1;	if (s < -90.0) s = -90.0;	/* Make sure point is not inside area, 0.1 is just a small arbitrary number */
5223 		n += 0.1;	if (n > 90.0) n = 90.0;		/* But don't go crazy beyond the pole */
5224 		w -= 0.1;	e += 0.1;	if (fabs (w - e) > 360.0) { w = 0.0; e = 360.0; }	/* Ensure max 360 range */
5225 	}
5226 	*west = w;	*east = e;	*south = s;	*north = n;	/* Pass back our findings */
5227 }
5228 
5229 /*! . */
gmtmap_horizon_search(struct GMT_CTRL * GMT,double w,double e,double s,double n,double xmin,double xmax,double ymin,double ymax)5230 GMT_LOCAL int gmtmap_horizon_search (struct GMT_CTRL *GMT, double w, double e, double s, double n, double xmin, double xmax, double ymin, double ymax) {
5231 	double dx, dy, d, x, y, lon, lat;
5232 	unsigned int i, j;
5233 	bool beyond = false;
5234 
5235 	/* Search for extreme original coordinates lon/lat and see if any fall beyond the horizon */
5236 
5237 	dx = (xmax - xmin) / GMT->current.map.n_lon_nodes;
5238 	dy = (ymax - ymin) / GMT->current.map.n_lat_nodes;
5239 	if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, w, s)) > GMT->current.proj.f_horizon) beyond = true;
5240 	if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, e, n)) > GMT->current.proj.f_horizon) beyond = true;
5241 	for (i = 0; !beyond && i <= GMT->current.map.n_lon_nodes; i++) {
5242 		x = (i == GMT->current.map.n_lon_nodes) ? xmax : xmin + i * dx;
5243 		gmt_xy_to_geo (GMT, &lon, &lat, x, ymin);
5244 		if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5245 		gmt_xy_to_geo (GMT, &lon, &lat, x, ymax);
5246 		if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5247 	}
5248 	for (j = 0; !beyond && j <= GMT->current.map.n_lat_nodes; j++) {
5249 		y = (j == GMT->current.map.n_lat_nodes) ? ymax : ymin + j * dy;
5250 		gmt_xy_to_geo (GMT, &lon, &lat, xmin, y);
5251 		if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5252 		gmt_xy_to_geo (GMT, &lon, &lat, xmax, y);
5253 		if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5254 	}
5255 	if (beyond) {
5256 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Rectangular region for azimuthal projection extends beyond the horizon\n");
5257 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Please select a region that is completely within the visible hemisphere\n");
5258 		return GMT_PROJECTION_ERROR;
5259 	}
5260 	return (GMT_NOERROR);
5261 }
5262 
5263 /*! . */
gmtmap_wrap_around_check_x(struct GMT_CTRL * GMT,double * angle,double last_x,double last_y,double this_x,double this_y,double * xx,double * yy,unsigned int * sides)5264 GMT_LOCAL unsigned int gmtmap_wrap_around_check_x (struct GMT_CTRL *GMT, double *angle, double last_x, double last_y, double this_x, double this_y, double *xx, double *yy, unsigned int *sides) {
5265 	double dx, dy, width, jump, gmt_half_map_width (struct GMT_CTRL *GMT, double y);
5266 
5267 	jump = this_x - last_x;
5268 	width = MAX (gmt_half_map_width (GMT, this_y), gmt_half_map_width (GMT, last_y));
5269 
5270 	if (fabs (jump) - width <= GMT_CONV4_LIMIT || fabs (jump) <= GMT_CONV4_LIMIT) return (0);
5271 	dy = this_y - last_y;
5272 
5273 	if (jump < -width) {	/* Crossed right boundary */
5274 		dx = GMT->current.map.width + jump;
5275 		yy[0] = yy[1] = last_y + (GMT->current.map.width - last_x) * dy / dx;
5276 		if (yy[0] < 0.0 || yy[0] > GMT->current.proj.rect[YHI]) return (0);
5277 		xx[0] = gmtlib_right_boundary (GMT, yy[0]);	xx[1] = gmtlib_left_boundary (GMT, yy[0]);
5278 		sides[0] = 1;
5279 		angle[0] = d_atan2d (dy, dx);
5280 	}
5281 	else {	/* Crossed left boundary */
5282 		dx = GMT->current.map.width - jump;
5283 		yy[0] = yy[1] = last_y + last_x * dy / dx;
5284 		if (yy[0] < 0.0 || yy[0] > GMT->current.proj.rect[YHI]) return (0);
5285 		xx[0] = gmtlib_left_boundary (GMT, yy[0]);	xx[1] = gmtlib_right_boundary (GMT, yy[0]);
5286 		sides[0] = 3;
5287 		angle[0] = d_atan2d (dy, -dx);
5288 	}
5289 	sides[1] = 4 - sides[0];
5290 	angle[1] = angle[0] + 180.0;
5291 
5292 	return (2);
5293 }
5294 
5295 /*! . */
gmtmap_get_crossings_x(struct GMT_CTRL * GMT,double * xc,double * yc,double x0,double y0,double x1,double y1)5296 GMT_LOCAL void gmtmap_get_crossings_x (struct GMT_CTRL *GMT, double *xc, double *yc, double x0, double y0, double x1, double y1) {
5297 	/* Finds crossings for wrap-arounds */
5298 	double xa, xb, ya, yb, dxa, dxb, dyb, left_yb, c;
5299 
5300 	xa = x0;	xb = x1;
5301 	ya = y0;	yb = y1;
5302 	if (xa > xb) {	/* Make A the minimum x point */
5303 		gmt_M_double_swap (xa, xb);
5304 		gmt_M_double_swap (ya, yb);
5305 	}
5306 
5307 	xb -= 2.0 * gmt_half_map_width (GMT, yb);
5308 
5309 	dxa = xa - gmtlib_left_boundary (GMT, ya);
5310 	left_yb = gmtlib_left_boundary (GMT, yb);
5311 	dxb = left_yb - xb;
5312 	c = (doubleAlmostEqualZero (left_yb, xb)) ? 0.0 : 1.0 + dxa/dxb;
5313 	dyb = (gmt_M_is_zero (c)) ? 0.0 : fabs (yb - ya) / c;
5314 	yc[0] = yc[1] = (ya > yb) ? yb + dyb : yb - dyb;
5315 	xc[0] = gmtlib_left_boundary (GMT, yc[0]);
5316 	xc[1] = gmtlib_right_boundary (GMT, yc[0]);
5317 }
5318 
5319 /*! . */
gmtmap_get_crossings_y(struct GMT_CTRL * GMT,double * xc,double * yc,double x0,double y0,double x1,double y1)5320 GMT_LOCAL void gmtmap_get_crossings_y (struct GMT_CTRL *GMT, double *xc, double *yc, double x0, double y0, double x1, double y1) {
5321 	/* Finds crossings for wrap-arounds in y, assuming rectangular domain */
5322 	double xa, xb, ya, yb, c;
5323 
5324 	xa = x0;	xb = x1;
5325 	ya = y0;	yb = y1;
5326 	if (ya > yb) {	/* Make A the minimum y point */
5327 		gmt_M_double_swap (xa, xb);
5328 		gmt_M_double_swap (ya, yb);
5329 	}
5330 
5331 	yb -= GMT->current.map.height;	/* Now below the y = 0 line */
5332 	c = (doubleAlmostEqualZero (ya, yb)) ? 0.0 : (xa - xb) / (ya - yb);
5333 	xc[0] = xc[1] = xa - c * ya;
5334 	yc[0] = 0.0;
5335 	yc[1] = GMT->current.map.height;
5336 }
5337 
5338 /*! . */
gmtmap_this_point_wraps_x(struct GMT_CTRL * GMT,double x0,double x1,double w_last,double w_this)5339 GMT_LOCAL bool gmtmap_this_point_wraps_x (struct GMT_CTRL *GMT, double x0, double x1, double w_last, double w_this) {
5340 	/* Returns true if the 2 x-points implies a jump at this y-level of the map */
5341 
5342 	double w_min, w_max, dx;
5343 	gmt_M_unused(GMT);
5344 
5345 	if (w_this > w_last) {
5346 		w_max = w_this;
5347 		w_min = w_last;
5348 	}
5349 	else {
5350 		w_max = w_last;
5351 		w_min = w_this;
5352 	}
5353 
5354 	/* Second condition deals with points near bottom/top of maps
5355 	   where map width may shrink to zero */
5356 
5357 	return ((dx = fabs (x1 - x0)) > w_max && w_min > GMT_CONV4_LIMIT);
5358 }
5359 
5360 /*! . */
gmtmap_will_it_wrap_x(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t * start)5361 GMT_LOCAL bool gmtmap_will_it_wrap_x (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t *start) {
5362 	/* Determines if a polygon will wrap at edges */
5363 	uint64_t i;
5364 	bool wrap;
5365 	double w_last, w_this;
5366 
5367 	if (!GMT->current.map.is_world) return (false);
5368 	//if (!GMT->current.map.is_world)
5369 	//	f = (2.0 - GMT_CONV8_LIMIT);
5370 	w_this = gmt_half_map_width (GMT, y[0]);
5371 	for (i = 1, wrap = false; !wrap && i < n; i++) {
5372 		w_last = w_this;
5373 		w_this = gmt_half_map_width (GMT, y[i]);
5374 		wrap = gmtmap_this_point_wraps_x (GMT, x[i-1], x[i], w_last, w_this);
5375 	}
5376 	*start = i - 1;
5377 	return (wrap);
5378 }
5379 
5380 /*! . */
gmtmap_truncate_x(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t start,int l_or_r)5381 GMT_LOCAL uint64_t gmtmap_truncate_x (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t start, int l_or_r) {
5382 	/* Truncates a wrapping polygon against left or right edge */
5383 
5384 	uint64_t i, i1, j, k;
5385 	double xc[4], yc[4], w_last, w_this;
5386 
5387 	/* First initialize variables that differ for left and right truncation */
5388 
5389 	if (l_or_r == -1)	/* Left truncation (-1) */
5390 		/* Find first point that is left of map center */
5391 		i = (x[start] < GMT->current.map.half_width) ? start : start - 1;
5392 	else				/* Right truncation (+1) */
5393 		/* Find first point that is right of map center */
5394 		i = (x[start] > GMT->current.map.half_width) ? start : start - 1;
5395 
5396 	if (!GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5397 
5398 	GMT->current.plot.x[0] = x[i];	GMT->current.plot.y[0] = y[i];
5399 	w_this = gmt_half_map_width (GMT, y[i]);
5400 	k = j = 1;
5401 	while (k <= n) {
5402 		i1 = i;
5403 		i = (i + 1)%n;	/* Next point */
5404 		w_last = w_this;
5405 		w_this = gmt_half_map_width (GMT, y[i]);
5406 		if (gmtmap_this_point_wraps_x (GMT, x[i1], x[i], w_last, w_this)) {
5407 			(*GMT->current.map.get_crossings) (GMT, xc, yc, x[i1], y[i1], x[i], y[i]);
5408 			if (l_or_r == -1)
5409 				GMT->current.plot.x[j] = gmtlib_left_boundary (GMT, yc[0]);
5410 			else
5411 				GMT->current.plot.x[j] = gmtlib_right_boundary (GMT, yc[0]);
5412 			GMT->current.plot.y[j] = yc[0];
5413 			j++;
5414 			if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5415 		}
5416 		if (l_or_r == -1) /* Left */
5417 			GMT->current.plot.x[j] = (x[i] >= GMT->current.map.half_width) ? gmtlib_left_boundary (GMT, y[i]) : x[i];
5418 		else	/* Right */
5419 			GMT->current.plot.x[j] = (x[i] < GMT->current.map.half_width) ? gmtlib_right_boundary (GMT, y[i]) : x[i];
5420 		GMT->current.plot.y[j] = y[i];
5421 		j++, k++;
5422 		if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5423 	}
5424 	return (j);
5425 }
5426 
5427 /*! . */
gmtmap_truncate_tm(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t start,int b_or_t)5428 GMT_LOCAL uint64_t gmtmap_truncate_tm (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t start, int b_or_t) {
5429 	/* Truncates a wrapping polygon against bottom or top edge for global TM maps */
5430 
5431 	uint64_t i, i1, j, k;
5432 	double xc[4], yc[4], trunc_y;
5433 
5434 	/* First initialize variables that differ for bottom and top truncation */
5435 
5436 	if (b_or_t == -1) {	/* Bottom truncation (-1) */
5437 		/* Find first point that is below map center */
5438 		i = (y[start] < GMT->current.map.half_height) ? start : start - 1;
5439 		trunc_y = 0.0;
5440 	}
5441 	else {				/* Top truncation (+1) */
5442 		/* Find first point that is above map center */
5443 		i = (y[start] > GMT->current.map.half_height) ? start : start - 1;
5444 		trunc_y = GMT->current.map.height;
5445 	}
5446 
5447 	if (!GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5448 
5449 	GMT->current.plot.x[0] = x[i];	GMT->current.plot.y[0] = y[i];
5450 	k = j = 1;
5451 	while (k <= n) {
5452 		i1 = i;
5453 		i = (i + 1)%n;	/* Next point */
5454 		if (gmtmap_this_point_wraps_tm (GMT, y[i1], y[i])) {
5455 			gmtmap_get_crossings_tm (GMT, xc, yc, x[i1], y[i1], x[i], y[i]);
5456 			GMT->current.plot.x[j] = xc[0];
5457 			GMT->current.plot.y[j] = trunc_y;
5458 			j++;
5459 			if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5460 		}
5461 		if (b_or_t == -1) /* Bottom */
5462 			GMT->current.plot.y[j] = (y[i] >= GMT->current.map.half_height) ? 0.0 : y[i];
5463 		else	/* Top */
5464 			GMT->current.plot.y[j] = (y[i] < GMT->current.map.half_height) ? GMT->current.map.height : y[i];
5465 		GMT->current.plot.x[j] = x[i];
5466 		j++, k++;
5467 		if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5468 	}
5469 	return (j);
5470 }
5471 
5472 /*! . */
gmtmap_cartesian_dist2(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)5473 GMT_LOCAL double gmtmap_cartesian_dist2 (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
5474 	/* Calculates the good-old straight line distance squared in users units */
5475 	gmt_M_unused(GMT);
5476 	x1 -= x0;	y1 -= y0;
5477 	return (x1*x1 + y1*y1);
5478 }
5479 
5480 /*! . */
gmtmap_cartesian_dist_proj2(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5481 GMT_LOCAL double gmtmap_cartesian_dist_proj2 (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5482 	/* Calculates the good-old straight line distance squared after first projecting the data */
5483 	double x0, y0, x1, y1;
5484 	gmt_geo_to_xy (GMT, lon1, lat1, &x0, &y0);
5485 	gmt_geo_to_xy (GMT, lon2, lat2, &x1, &y1);
5486 	x1 -= x0;	y1 -= y0;
5487 	return (x1*x1 + y1*y1);
5488 }
5489 
5490 /*! . */
gmtmap_flatearth_dist_degree(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)5491 GMT_LOCAL double gmtmap_flatearth_dist_degree (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
5492 	/* Calculates the approximate flat earth distance in degrees.
5493 	   If difference in longitudes exceeds 180 we pick the other
5494 	   offset (360 - offset)
5495 	 */
5496 	double dlon;
5497 	gmt_M_unused(GMT);
5498 
5499 	gmt_M_set_delta_lon (x0, x1, dlon);
5500 	return (hypot ( dlon * cosd (0.5 * (y1 + y0)), (y1 - y0)));
5501 }
5502 
5503 /*! . */
gmtmap_flatearth_dist_meter(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)5504 GMT_LOCAL double gmtmap_flatearth_dist_meter (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
5505 	/* Calculates the approximate flat earth distance in km.
5506 	   If difference in longitudes exceeds 180 we pick the other
5507 	   offset (360 - offset)
5508 	 */
5509 	return (gmtmap_flatearth_dist_degree (GMT, x0, y0, x1, y1) * GMT->current.proj.DIST_M_PR_DEG);
5510 }
5511 
5512 /*! . */
gmtmap_haversine(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5513 GMT_LOCAL double gmtmap_haversine (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5514 	/* Haversine formula for great circle distance.  Intermediate function that returns sin^2 (half_angle).
5515 	 * This avoids problems with short distances where cos(c) is close to 1 and acos is inaccurate.
5516 	 */
5517 
5518 	double sx, sy, sin_half_squared;
5519 
5520 	if (lat1 == lat2 && lon1 == lon2) return (0.0);
5521 
5522 	if (GMT->current.setting.proj_aux_latitude != GMT_LATSWAP_NONE) {	/* Use selected auxiliary latitude */
5523 		lat1 = gmt_lat_swap (GMT, lat1, GMT->current.setting.proj_aux_latitude);
5524 		lat2 = gmt_lat_swap (GMT, lat2, GMT->current.setting.proj_aux_latitude);
5525 	}
5526 
5527 	sy = sind (0.5 * (lat2 - lat1));
5528 	sx = sind (0.5 * (lon2 - lon1));	/* If there is a 360 wrap here then the sign of sx is wrong but we only use sx^2 */
5529 	sin_half_squared = sy * sy + cosd (lat2) * cosd (lat1) * sx * sx;
5530 
5531 	return (sin_half_squared);
5532 }
5533 
5534 /*! . */
gmtmap_geodesic_dist_degree(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)5535 GMT_LOCAL double gmtmap_geodesic_dist_degree (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
5536 	/* Compute the great circle arc length in degrees on an ellipsoidal
5537 	 * Earth.  We do this by converting to geocentric coordinates.
5538 	 */
5539 
5540 	double a, b, c, d, e, f, a1, b1, c1, d1, e1, f1, thg, sc, sd, dist;
5541 
5542 	/* Equations are unstable for latitudes of exactly 0 degrees. */
5543 
5544 	if (latE == 0.0) latE = 1.0e-08;
5545 	if (latS == 0.0) latS = 1.0e-08;
5546 
5547 	/* Must convert from geographic to geocentric coordinates in order
5548 	 * to use the spherical trig equations.  This requires a latitude
5549 	 * correction given by: 1-ECC2=1-2*F + F*F = GMT->current.proj.one_m_ECC2
5550 	 */
5551 
5552 	thg = atan (GMT->current.proj.one_m_ECC2 * tand (latE));
5553 	sincos (thg, &c, &f);		f = -f;
5554 	sincosd (lonE, &d, &e);		e = -e;
5555 	a = f * e;
5556 	b = -f * d;
5557 
5558 	/* Calculate some trig constants. */
5559 
5560 	thg = atan (GMT->current.proj.one_m_ECC2 * tand (latS));
5561 	sincos (thg, &c1, &f1);		f1 = -f1;
5562 	sincosd (lonS, &d1, &e1);	e1 = -e1;
5563 	a1 = f1 * e1;
5564 	b1 = -f1 * d1;
5565 
5566 	/* Spherical trig relationships used to compute angles. */
5567 
5568 	sc = a * a1 + b * b1 + c * c1;
5569 	sd = 0.5 * sqrt ((pow (a-a1,2.0) + pow (b-b1,2.0) + pow (c-c1,2.0)) * (pow (a+a1,2.0) + pow (b+b1, 2.0) + pow (c+c1, 2.0)));
5570 	dist = atan2d (sd, sc);
5571 	if (dist < 0.0) dist += 360.0;
5572 
5573 	return (dist);
5574 }
5575 
5576 /*! . */
gmtmap_andoyer_dist_degree(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5577 GMT_LOCAL double gmtmap_andoyer_dist_degree (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5578 	/* Approximate geodesic distance on an ellipsoid in degrees
5579 	 *  H. Andoyer from Astronomical Algorithms, Jean Meeus, second edition.
5580 	 */
5581 	double sg = sind (0.5 * (lat2 - lat1));
5582 	double sf = sind (0.5 * (lat2 + lat1));
5583 	double sl = sind (0.5 * (lon2 - lon1));	/* Might have wrong sign if 360 wrap but we only use sl^2 */
5584 	double s, c, w, r, h1, h2;
5585 	sg *= sg;
5586 	sl *= sl;
5587 	sf *= sf;
5588 	s = sg * (1.0 - sl) + (1.0 - sf) * sl;
5589 	c = (1.0 - sg) * (1.0 - sl) + sf * sl;
5590 
5591 	w = atan (sqrt (s/c));
5592 	r = sqrt (s*c) / w;
5593 	h1 = 0.5 * (3.0 * r - 1.0) / c;
5594 	h2 = 0.5 * (3.0 * r + 1.0) / s;
5595 	return (2.0 * w * (1.0 + GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening * (h1 * sf * (1.0 - sg) - h2 * (1.0 - sf) * sg)));
5596 }
5597 
5598 /*! . */
gmtmap_andoyer_dist_meter(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5599 GMT_LOCAL double gmtmap_andoyer_dist_meter (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5600 	return (GMT->current.proj.EQ_RAD * gmtmap_andoyer_dist_degree (GMT, lon1, lat1, lon2, lat2));
5601 }
5602 
5603 /*! . */
gmtmap_vincenty_dist_meter(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)5604 GMT_LOCAL double gmtmap_vincenty_dist_meter (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
5605 	/* Translation of NGS FORTRAN code for determination of true distance
5606 	** and respective forward and back azimuths between two points on the
5607 	** ellipsoid.  Good for any pair of points that are not antipodal.
5608 	**
5609 	**      INPUT
5610 	**	latS, lonS -- latitude and longitude of first point in radians.
5611 	**	latE, lonE -- latitude and longitude of second point in radians.
5612 	**
5613 	**	OUTPUT
5614 	**	s -- distance between points in meters.
5615 	** Modified by P.W. from: http://article.gmane.org/gmane.comp.gis.proj-4.devel/3478
5616 	*/
5617 	double s, c, d, e, r, f, d_lon, dx, x, y, sa, cx, cy, cz, sx, sy, c2a, cu1, cu2, su1, tu1, tu2, ts, baz, faz;
5618 	int n_iter = 0;
5619 
5620 	f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
5621 	r = 1.0 - f;
5622 	tu1 = r * tand (latS);
5623 	tu2 = r * tand (latE);
5624 	cu1 = 1.0 / sqrt (tu1 * tu1 + 1.0);
5625 	su1 = cu1 * tu1;
5626 	cu2 = 1.0 / sqrt (tu2 * tu2 + 1.0);
5627 	ts  = cu1 * cu2;
5628 	baz = ts * tu2;
5629 	faz = baz * tu1;
5630 	gmt_M_set_delta_lon (lonS, lonE, d_lon);
5631 	if (gmt_M_is_zero (d_lon) && doubleAlmostEqualZero (latS, latE)) return 0.0;
5632 	x = dx = D2R * d_lon;
5633 	do {
5634 		n_iter++;
5635 		sincos (x, &sx, &cx);
5636 		tu1 = cu2 * sx;
5637 		tu2 = baz - su1 * cu2 * cx;
5638 		sy = sqrt (tu1 * tu1 + tu2 * tu2);
5639 		cy = ts * cx + faz;
5640 		y = atan2 (sy, cy);
5641 		sa = ts * sx / sy;
5642 		c2a = -sa * sa + 1.0;
5643 		cz = faz + faz;
5644 		if (c2a > 0.0) cz = -cz / c2a + cy;
5645 		e = cz * cz * 2.0 - 1.0;
5646 		c = ((c2a * -3.0 + 4.0) * f + 4.0) * c2a * f / 16.0;
5647 		d = x;
5648 		x = ((e * cy * c + cz) * sy * c + y) * sa;
5649 		x = (1.0 - c) * x * f + dx;
5650 	} while (fabs (d - x) > VINCENTY_EPS && n_iter <= 50);
5651 	if (n_iter > VINCENTY_MAX_ITER) {
5652 		GMT->current.proj.n_geodesic_approx++;	/* Count inaccurate results */
5653 		if (GMT->current.proj.n_geodesic_approx == 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "At least one near- or actual antipodal points encountered. Precision may be reduced slightly.\n");
5654 		s = M_PI;
5655 	}
5656 	else {
5657 		x = sqrt ((1.0 / r / r - 1.0) * c2a + 1.0) + 1.0;
5658 		x = (x - 2.0) / x;
5659 		c = (x * x / 4.0 + 1.0) / (1.0 - x);
5660 		d = (x * 0.375 * x - 1.0) * x;
5661 		s = ((((sy * sy * 4.0 - 3.0) * (1.0 - e - e) * cz * d / 6.0 - e * cy) * d / 4.0 + cz) * sy * d + y) * c * r;
5662 		if (s > M_PI) {
5663 			GMT->current.proj.n_geodesic_approx++;	/* Count inaccurate results */
5664 			if (GMT->current.proj.n_geodesic_approx == 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "At least one near- or actual antipodal points encountered. Precision may be reduced slightly.\n");
5665 			s = M_PI;
5666 		}
5667 	}
5668 	GMT->current.proj.n_geodesic_calls++;
5669 	return (s * GMT->current.proj.EQ_RAD);
5670 }
5671 
5672 /*! . */
gmtmap_rudoe_dist_meter(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)5673 GMT_LOCAL double gmtmap_rudoe_dist_meter (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
5674 	/* Compute length of geodesic between locations in meters
5675 	 * We use Rudoe's equation from Bomford.
5676 	 */
5677 
5678 	double e1, el, sinthi, costhi, sinthk, costhk, tanthi, tanthk, sina12, cosa12, d_lon;
5679 	double al, a12top, a12bot, a12, e2, e3, c0, c2, c4, v1, v2, z1, z2, x2, y2, dist;
5680 	double e1p1, sqrte1p1, sin_dl, cos_dl, u1bot, u1, u2top, u2bot, u2, b0, du, pdist;
5681 
5682 
5683 	/* Equations are unstable for latitudes of exactly 0 degrees. */
5684 
5685 	if (latE == 0.0) latE = 1.0e-08;
5686 	if (latS == 0.0) latS = 1.0e-08;
5687 
5688 	/* Now compute the distance between the two points using Rudoe's
5689 	 * formula given in Bomford's GEODESY, section 2.15(b).
5690 	 * (Unclear if it is 1971 or 1980 edition)
5691 	 * (There is some numerical problem with the following formulae.
5692 	 * If the station is in the southern hemisphere and the event in
5693 	 * in the northern, these equations give the longer, not the
5694 	 * shorter distance between the two locations.  Since the equations
5695 	 * are fairly messy, the simplest solution is to reverse the
5696 	 * meanings of the two locations for this case.)
5697 	 */
5698 
5699 	if (latS < 0.0) {	/* Station in southern hemisphere, swap */
5700 		gmt_M_double_swap (lonS, lonE);
5701 		gmt_M_double_swap (latS, latE);
5702 	}
5703 	el = GMT->current.proj.ECC2 / GMT->current.proj.one_m_ECC2;
5704 	e1 = 1.0 + el;
5705 	sincosd (latE, &sinthi, &costhi);
5706 	sincosd (latS, &sinthk, &costhk);
5707 	gmt_M_set_delta_lon (lonS, lonE, d_lon);
5708 	sincosd (d_lon, &sin_dl, &cos_dl);
5709 	tanthi = sinthi / costhi;
5710 	tanthk = sinthk / costhk;
5711 	al = tanthi / (e1 * tanthk) + GMT->current.proj.ECC2 * sqrt ((e1 + tanthi * tanthi) / (e1 + tanthk * tanthk));
5712 	a12top = sin_dl;
5713 	a12bot = (al - cos_dl) * sinthk;
5714 	a12 = atan2 (a12top,a12bot);
5715 	sincos (a12, &sina12, &cosa12);
5716 	e1 = el * (pow (costhk * cosa12, 2.0) + sinthk * sinthk);
5717 	e2 = e1 * e1;
5718 	e3 = e1 * e2;
5719 	c0 = 1.0 + 0.25 * e1 - (3.0 / 64.0) * e2 + (5.0 / 256.0) * e3;
5720 	c2 = -0.125 * e1 + (1.0 / 32) * e2 - (15.0 / 1024.0) * e3;
5721 	c4 = -(1.0 / 256.0) * e2 + (3.0 / 1024.0) * e3;
5722 	v1 = GMT->current.proj.EQ_RAD / sqrt (1.0 - GMT->current.proj.ECC2 * sinthk * sinthk);
5723 	v2 = GMT->current.proj.EQ_RAD / sqrt (1.0 - GMT->current.proj.ECC2 * sinthi * sinthi);
5724 	z1 = v1 * (1.0 - GMT->current.proj.ECC2) * sinthk;
5725 	z2 = v2 * (1.0 - GMT->current.proj.ECC2) * sinthi;
5726 	x2 = v2 * costhi * cos_dl;
5727 	y2 = v2 * costhi * sin_dl;
5728 	e1p1 = e1 + 1.0;
5729 	sqrte1p1 = sqrt (e1p1);
5730 	u1bot = sqrte1p1 * cosa12;
5731 	u1 = atan2 (tanthk, u1bot);
5732 	u2top = v1 * sinthk + e1p1 * (z2 - z1);
5733 	u2bot = sqrte1p1 * (x2 * cosa12 - y2 * sinthk * sina12);
5734 	u2 = atan2 (u2top, u2bot);
5735 	b0 = v1 * sqrt (1.0 + el * pow (costhk * cosa12, 2.0)) / e1p1;
5736 	du = u2  - u1;
5737 	if (fabs (du) > M_PI) du = copysign (TWO_PI - fabs (du), du);
5738 	pdist = b0 * (c2 * (sin (2.0 * u2) - sin(2.0 * u1)) + c4 * (sin (4.0 * u2) - sin (4.0 * u1)));
5739 	dist = fabs (b0 * c0 * du + pdist);
5740 
5741 	return (dist);
5742 }
5743 
5744 /*! . */
gmtmap_loxodrome_dist_degree(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5745 GMT_LOCAL double gmtmap_loxodrome_dist_degree (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5746 	/* Calculates the distance along the loxodrome, in meter */
5747 	double dist, d_lon;
5748 	gmt_M_set_delta_lon (lon1, lon2, d_lon);
5749 	if (doubleAlmostEqualZero (lat1, lat2)) {	/* Along parallel */
5750 		if (GMT->current.proj.GMT_convert_latitudes) lat1 = gmt_M_latg_to_latc (GMT, lat1);
5751 		dist = fabs (d_lon) * cosd (lat1);
5752 	}
5753 	else { /* General case */
5754 		double dx, dy, Az;
5755 		if (GMT->current.proj.GMT_convert_latitudes) {
5756 			lat1 = gmt_M_latg_to_latc (GMT, lat1);
5757 			lat2 = gmt_M_latg_to_latc (GMT, lat2);
5758 		}
5759 		dx = D2R * d_lon;
5760 		dy = d_log (GMT, tand (45.0 + 0.5 * lat2)) - d_log (GMT, tand (45.0 + 0.5 * lat1));
5761 		Az = atan2 (dx, dy);
5762 		dist = fabs (dx / cos (Az));
5763 	}
5764 	return (dist);
5765 }
5766 
5767 /*! . */
gmtmap_loxodrome_dist_meter(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5768 GMT_LOCAL double gmtmap_loxodrome_dist_meter (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5769 	/* Calculates the loxodrome distance in meter */
5770 	return (gmtmap_loxodrome_dist_degree (GMT, lon1, lat1, lon2, lat2) * GMT->current.proj.DIST_M_PR_DEG);
5771 }
5772 
5773 /*! . */
gmtmap_az_backaz_loxodrome(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)5774 GMT_LOCAL double gmtmap_az_backaz_loxodrome (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
5775 	/* Calculate azimuths or backazimuths.  Loxodrome mode.
5776 	 * First point is considered "Event" and second "Station".
5777 	 * Azimuth is direction from Station to Event.
5778 	 * BackAzimuth is direction from Event to Station */
5779 
5780 	double az, d_lon;
5781 
5782 	if (baz) {	/* exchange point one and two */
5783 		gmt_M_double_swap (lonS, lonE);
5784 		gmt_M_double_swap (latS, latE);
5785 	}
5786 	gmt_M_set_delta_lon (lonE, lonS, d_lon);
5787 	if (doubleAlmostEqualZero (latS, latE))	/* Along parallel */
5788 		az = (d_lon > 0.0) ? 90 : -90.0;
5789 	else { /* General case */
5790 		double dx, dy;
5791 		if (GMT->current.proj.GMT_convert_latitudes) {
5792 			latS = gmt_M_latg_to_latc (GMT, latS);
5793 			latE = gmt_M_latg_to_latc (GMT, latE);
5794 		}
5795 		dx = D2R * d_lon;
5796 		dy = d_log (GMT, tand (45.0 + 0.5 * latS)) - d_log (GMT, tand (45.0 + 0.5 * latE));
5797 		az = atan2d (dx, dy);
5798 		if (az < 0.0) az += 360.0;
5799 	}
5800 	return (az);
5801 }
5802 
5803 /*! . */
gmtmap_near_a_point_spherical(struct GMT_CTRL * GMT,double x,double y,struct GMT_DATATABLE * T,double dist)5804 GMT_LOCAL bool gmtmap_near_a_point_spherical (struct GMT_CTRL *GMT, double x, double y, struct GMT_DATATABLE *T, double dist) {
5805 	uint64_t row, seg;
5806 	bool each_point_has_distance;
5807 	double d;
5808 
5809 	each_point_has_distance = (dist <= 0.0 && T->segment[0]->n_columns > 2);
5810 	for (seg = 0; seg < T->n_segments; seg++) {
5811 		for (row = 0; row < T->segment[seg]->n_rows; row++) {
5812 			d = gmt_distance (GMT, x, y, T->segment[seg]->data[GMT_X][row], T->segment[seg]->data[GMT_Y][row]);
5813 			if (each_point_has_distance) dist = T->segment[seg]->data[GMT_Z][row];
5814 			if (d <= dist) return (true);
5815 		}
5816 	}
5817 	return (false);
5818 }
5819 
5820 /*! . */
gmtmap_near_a_point_cartesian(struct GMT_CTRL * GMT,double x,double y,struct GMT_DATATABLE * T,double dist)5821 GMT_LOCAL bool gmtmap_near_a_point_cartesian (struct GMT_CTRL *GMT, double x, double y, struct GMT_DATATABLE *T, double dist) {
5822 	/* Since Cartesian we use a gmt_distance set to return distances^2 (avoiding hypot) */
5823 	bool each_point_has_distance;
5824 	uint64_t row, seg;
5825 	double d, x0, y0, xn, d0, d02 = 0.0, dn;
5826 
5827 	each_point_has_distance = (dist <= 0.0 && T->segment[0]->n_columns > 2);
5828 
5829 	/* Assumes the points have been sorted so xp[0] is xmin and xp[n-1] is xmax] !!! */
5830 
5831 	/* See if we are safely outside the range */
5832 	x0 = T->segment[0]->data[GMT_X][0];
5833 	d0 = (each_point_has_distance) ? T->segment[0]->data[GMT_Z][0] : dist;
5834 	xn = T->segment[T->n_segments-1]->data[GMT_X][T->segment[T->n_segments-1]->n_rows-1];
5835 	dn = (each_point_has_distance) ? T->segment[T->n_segments-1]->data[GMT_Z][T->segment[T->n_segments-1]->n_rows-1] : dist;
5836 	if ((x < (x0 - d0)) || (x > (xn) + dn)) return (false);
5837 
5838 	/* No, must search the points */
5839 	if (!each_point_has_distance) d02 = dist * dist;
5840 
5841 	for (seg = 0; seg < T->n_segments; seg++) {
5842 		for (row = 0; row < T->segment[seg]->n_rows; row++) {
5843 			x0 = T->segment[seg]->data[GMT_X][row];
5844 			d0 = (each_point_has_distance) ? T->segment[seg]->data[GMT_Z][row] : dist;
5845 			if (fabs (x - x0) <= d0) {	/* Simple x-range test first */
5846 				y0 = T->segment[seg]->data[GMT_Y][row];
5847 				if (fabs (y - y0) <= d0) {	/* Simple y-range test next */
5848 					/* Here we must compute distance */
5849 					if (each_point_has_distance) d02 = d0 * d0;
5850 					d = gmt_distance (GMT, x, y, x0, y0);
5851 					if (d <= d02) return (true);
5852 				}
5853 			}
5854 		}
5855 	}
5856 	return (false);
5857 }
5858 
5859 /* Functions involving distance from arbitrary points to a line */
5860 
5861 /*! . */
gmtmap_near_a_line_cartesian(struct GMT_CTRL * GMT,double lon,double lat,uint64_t seg,struct GMT_DATASEGMENT * S,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)5862 GMT_LOCAL bool gmtmap_near_a_line_cartesian (struct GMT_CTRL *GMT, double lon, double lat, uint64_t seg, struct GMT_DATASEGMENT *S, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
5863 	bool perpendicular_only = false, interior, within;
5864 	uint64_t row0, row1;
5865 	double edge, dx, dy, xc, yc, s, s_inv, d, dist_AB, fraction, S_dist, S_dist2;
5866 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
5867 	/* gmtmap_near_a_line_cartesian works in one of two modes, depending on return_mindist.
5868 	   Since return_mindist is composed of two settings we must first set
5869 	   perpendicular_only = (return_mindist >= 10);
5870 	   return_mindist -= 10 * perpendicular_only;
5871 	   That is, if 10 was added it means perpendicular_only is set and then the 10 is
5872 	   removed.  We now consider what is left of return_mindist:
5873 	   (1) return_mindist == 0:
5874 	      We expect each segment to have its dist variable set to a minimum distance,
5875 	      and if the point is within this distance from the line then we return true;
5876 	      otherwise we return false.  If the segments have not set their distances then
5877 	      it will have been initialized at 0 and only a point on the line will return true.
5878 	      If perpendicular_only we ignore a point that is within the distance of the
5879 	      linesegment endpoints but project onto the extension of the line (i.e., it is
5880 	      "outside" the extent of the line).  We return false in that case.
5881 	   (2) return_mindist != 0:
5882 	      Return the minimum distance via dist_min. In addition, if > 1:
5883 	      If == 2 we also return the coordinate of nearest point via x_near, y_near.
5884 	      If == 3 we instead return segment number and point number (fractional) of that point via x_near, y_near.
5885 	      The function will always return true, except if perpendicular_only is set: then we
5886 	      return false if the point projects onto the extension of the line (i.e., it is "outside"
5887 	      the extent of the line).  */
5888 
5889 	if (return_mindist >= 10) {	/* Exclude circular region surrounding line endpoints */
5890 		perpendicular_only = true;
5891 		return_mindist -= 10;
5892 	}
5893 
5894 	if (S->n_rows <= 0) return (false);	/* empty; skip */
5895 
5896 	if (return_mindist)
5897 		S_dist = S_dist2 = 0.0;	/* Explicitly set dist to zero so the shortest distance can be found */
5898 	else {
5899 		S_dist = SH->dist;
5900 		S_dist2 = S_dist * S_dist;	/* Since we compute distances squared for Cartesian cases */
5901 	}
5902 	/* Find nearest point on this line */
5903 
5904 	for (row0 = 0; row0 < S->n_rows; row0++) {	/* loop over nodes on current line */
5905 		d = gmt_distance (GMT, lon, lat, S->data[GMT_X][row0], S->data[GMT_Y][row0]);	/* Distance between our point and j'th node on seg'th line */
5906 		if (return_mindist && d < (*dist_min)) {	/* Update min distance */
5907 			*dist_min = d;
5908 			if (return_mindist == 2) { *x_near = S->data[GMT_X][row0]; *y_near = S->data[GMT_Y][row0]; }	/* Also update (x,y) of nearest point on the line */
5909 			else if (return_mindist == 3) { *x_near = (double)seg; *y_near = (double)row0;}		/* Instead update (seg, pt) of nearest point on the line */
5910 		}
5911 		interior = (row0 > 0 && row0 < (S->n_rows - 1));	/* Only false if we are processing one of the end points */
5912 		if (d <= S_dist2 && (interior || !perpendicular_only)) return (true);		/* Node inside the critical distance; we are done */
5913 	}
5914 
5915 	if (S->n_rows < 2) return (false);	/* 1-point "line" is a point; skip segment check */
5916 
5917 	/* If we get here we must check for intermediate points along the straight lines between segment nodes.
5918 	 * However, since we know all nodes are outside the circle, we first check if the pair of nodes making
5919 	 * up the next line segment are outside of the circumscribing square before we need to solve for the
5920 	 * intersection between the line segment and the normal from our point. */
5921 
5922 	for (row0 = 0, row1 = 1, within = false; row1 < S->n_rows; row0++, row1++) {	/* loop over straight segments on current line */
5923 		if (!return_mindist) {
5924 			edge = lon - S_dist;
5925 			if (S->data[GMT_X][row0] < edge && S->data[GMT_X][row1] < edge) continue;	/* Left of square */
5926 			edge = lon + S_dist;
5927 			if (S->data[GMT_X][row0] > edge && S->data[GMT_X][row1] > edge) continue;	/* Right of square */
5928 			edge = lat - S_dist;
5929 			if (S->data[GMT_Y][row0] < edge && S->data[GMT_Y][row1] < edge) continue;	/* Below square */
5930 			edge = lat + S_dist;
5931 			if (S->data[GMT_Y][row0] > edge && S->data[GMT_Y][row1] > edge) continue;	/* Above square */
5932 		}
5933 
5934 		/* Here there is potential for the line segment crossing inside the circle */
5935 
5936 		dx = S->data[GMT_X][row1] - S->data[GMT_X][row0];
5937 		dy = S->data[GMT_Y][row1] - S->data[GMT_Y][row0];
5938 		if (dx == 0.0) {		/* Line segment is vertical, our normal is thus horizontal */
5939 			if (dy == 0.0) continue;	/* Dummy segment with no length */
5940 			xc = S->data[GMT_X][row0];
5941 			yc = lat;
5942 			if (S->data[GMT_Y][row0] < yc && S->data[GMT_Y][row1] < yc ) continue;	/* Cross point is on extension */
5943 			if (S->data[GMT_Y][row0] > yc && S->data[GMT_Y][row1] > yc ) continue;	/* Cross point is on extension */
5944 		}
5945 		else {	/* Line segment is not vertical */
5946 			if (dy == 0.0) {	/* Line segment is horizontal, our normal is thus vertical */
5947 				xc = lon;
5948 				yc = S->data[GMT_Y][row0];
5949 			}
5950 			else {	/* General case of oblique line */
5951 				s = dy / dx;
5952 				s_inv = -1.0 / s;
5953 				xc = (lat - S->data[GMT_Y][row0] + s * S->data[GMT_X][row0] - s_inv * lon ) / (s - s_inv);
5954 				yc = S->data[GMT_Y][row0] + s * (xc - S->data[GMT_X][row0]);
5955 
5956 			}
5957 			/* To be inside, (xc, yc) must (1) be on the line segment and not its extension and (2) be within dist of our point */
5958 
5959 			if (S->data[GMT_X][row0] < xc && S->data[GMT_X][row1] < xc ) continue;	/* Cross point is on extension */
5960 			if (S->data[GMT_X][row0] > xc && S->data[GMT_X][row1] > xc ) continue;	/* Cross point is on extension */
5961 		}
5962 
5963 		/* OK, here we must check how close the crossing point is */
5964 
5965 		d = gmt_distance (GMT, lon, lat, xc, yc);			/* Distance between our point and intersection */
5966 		if (return_mindist && d < (*dist_min)) {			/* Update min distance */
5967 			*dist_min = d;
5968 			if (return_mindist == 2) { *x_near = xc; *y_near = yc;}	/* Also update nearest point on the line */
5969 			else if (return_mindist == 3) {	/* Instead update (seg, pt) of nearest point on the line */
5970 				*x_near = (double)seg;
5971 				dist_AB = gmt_distance (GMT, S->data[GMT_X][row0], S->data[GMT_Y][row0], S->data[GMT_X][row1], S->data[GMT_Y][row1]);
5972 				fraction = (dist_AB > 0.0) ? gmt_distance (GMT, S->data[GMT_X][row0], S->data[GMT_Y][row0], xc, yc) / dist_AB : 0.0;
5973 				*y_near = (double)row0 + fraction;
5974 			}
5975 			within = true;
5976 		}
5977 		if (d <= S_dist2) return (true);		/* Node inside the critical distance; we are done */
5978 	}
5979 
5980 	if (return_mindist) *dist_min = sqrt (*dist_min);	/* Undo the squared distances */
5981 
5982 	return (within);	/* All tests failed, we are not close to the line(s), or we just return distance and interior (see comments above) */
5983 }
5984 
5985 /*! . */
gmtmap_near_lines_cartesian(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)5986 GMT_LOCAL bool gmtmap_near_lines_cartesian (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
5987 	uint64_t seg;
5988 	int mode = return_mindist, status;
5989 	bool OK = false;
5990 	if (mode >= 10) mode -= 10;	/* Exclude (or flag) circular region surrounding line endpoints */
5991 	if (mode) *dist_min = DBL_MAX;	/* Want to find the minimum distance so init to huge */
5992 
5993 	for (seg = 0; seg < T->n_segments; seg++) {	/* Loop over each line segment */
5994 		status = gmtmap_near_a_line_cartesian (GMT, lon, lat, seg, T->segment[seg], return_mindist, dist_min, x_near, y_near);
5995 		if (status) {	/* Got a min distance or satisfied the min dist requirement */
5996 			if (!return_mindist) return (true);	/* Done, we are within distance of one of the lines */
5997 			OK = true;
5998 		}
5999 	}
6000 	return (OK);
6001 }
6002 
6003 /*! . */
gmtmap_near_a_line_spherical(struct GMT_CTRL * P,double lon,double lat,uint64_t seg,struct GMT_DATASEGMENT * S,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)6004 GMT_LOCAL bool gmtmap_near_a_line_spherical (struct GMT_CTRL *P, double lon, double lat, uint64_t seg, struct GMT_DATASEGMENT *S, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
6005 	bool perpendicular_only = false, interior, within;
6006 	uint64_t row, prev_row;
6007 	double d, A[3], B[3], GMT[3], X[3], xlon, xlat, cx_dist, cos_dist, dist_AB, fraction;
6008 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
6009 
6010 	/* gmtmap_near_a_line_spherical works in one of two modes, depending on return_mindist.
6011 	   Since return_mindist is composed of two settings we must first set
6012 	   perpendicular_only = (return_mindist >= 10);
6013 	   return_mindist -= 10 * perpendicular_only;
6014 	   That is, if 10 was added it means perpendicular_only is set and then the 10 is
6015 	   removed.  We now consider what is left of return_mindist:
6016 	   (1) return_mindist == 0:
6017 	      We expect each segment to have its dist variable set to a minimum distance,
6018 	      and if the point is within this distance from the line then we return true;
6019 	      otherwise we return false.  If the segments have not set their distances then
6020 	      it will have been initialized at 0 and only a point on the line will return true.
6021 	      If perpendicular_only we ignore a point that is within the distance of the
6022 	      linesegment endpoints but project onto the extension of the line (i.e., it is
6023 	      "outside" the extent of the line).  We return false in that case.
6024 	   (2) return_mindist != 0:
6025 	      Return the minimum distance via dist_min. In addition, if > 1:
6026 	      If == 2 we also return the coordinate of nearest point via x_near, y_near.
6027 	      If == 3 we instead return segment number and point number (fractional) of that point via x_near, y_near.
6028 	      The function will always return true, except if perpendicular_only is set: then we
6029 	      return false if the point projects onto the extension of the line (i.e., it is "outside"
6030 	      the extent of the line).  */
6031 
6032 	if (return_mindist >= 10) {	/* Exclude (or flag) circular region surrounding line endpoints */
6033 		perpendicular_only = true;
6034 		return_mindist -= 10;
6035 	}
6036 	gmt_geo_to_cart (P, lat, lon, GMT, true);	/* Our point to test is now GMT */
6037 
6038 	if (S->n_rows <= 0) return (false);	/* Empty ; skip */
6039 
6040 	/* Find nearest point on this line */
6041 
6042 	if (return_mindist) SH->dist = 0.0;	/* Explicitly set dist to zero so the shortest distance can be found */
6043 
6044 	for (row = 0; row < S->n_rows; row++) {	/* loop over nodes on current line */
6045 		d = gmt_distance (P, lon, lat, S->data[GMT_X][row], S->data[GMT_Y][row]);	/* Distance between our point and row'th node on seg'th line */
6046 		if (return_mindist && d < (*dist_min)) {	/* Update minimum distance */
6047 			*dist_min = d;
6048 			if (return_mindist == 2) *x_near = S->data[GMT_X][row], *y_near = S->data[GMT_Y][row];	/* Also update (x,y) of nearest point on the line */
6049 			else if (return_mindist == 3) *x_near = (double)seg, *y_near = (double)row;	/* Also update (seg, pt) of nearest point on the line */
6050 		}
6051 		interior = (row > 0 && row < (S->n_rows - 1));	/* Only false if we are processing one of the end points */
6052 		if (d <= SH->dist && (interior || !perpendicular_only)) return (true);			/* Node inside the critical distance; we are done */
6053 	}
6054 
6055 	if (S->n_rows < 2) return (false);	/* 1-point "line" is a point; skip segment check */
6056 
6057 	/* If we get here we must check for intermediate points along the great circle lines between segment nodes.*/
6058 
6059 	if (return_mindist)		/* Cosine of the great circle distance we are checking for. 2 ensures failure to be closer */
6060 		cos_dist = 2.0;
6061 	else if (P->current.map.dist[GMT_MAP_DIST].arc)	/* Used angular distance measure */
6062 		cos_dist = cosd (SH->dist / P->current.map.dist[GMT_MAP_DIST].scale);
6063 	else	/* Used distance units (e.g., meter, km). Conv to meters, then to degrees */
6064 		cos_dist = cosd ((SH->dist / P->current.map.dist[GMT_MAP_DIST].scale) / P->current.proj.DIST_M_PR_DEG);
6065 	gmt_geo_to_cart (P, S->data[GMT_Y][0], S->data[GMT_X][0], B, true);		/* 3-D vector of end of last segment */
6066 
6067 	for (row = 1, within = false; row < S->n_rows; row++) {				/* loop over great circle segments on current line */
6068 		gmt_M_memcpy (A, B, 3, double);	/* End of last segment is start of new segment */
6069 		gmt_geo_to_cart (P, S->data[GMT_Y][row], S->data[GMT_X][row], B, true);	/* 3-D vector of end of this segment */
6070 		if (gmtlib_great_circle_intersection (P, A, B, GMT, X, &cx_dist)) continue;	/* X not between A and B */
6071 		if (return_mindist) {		/* Get lon, lat of X, calculate distance, and update min_dist if needed */
6072 			gmt_cart_to_geo (P, &xlat, &xlon, X, true);
6073 			d = gmt_distance (P, xlon, xlat, lon, lat);	/* Distance between our point and closest perpendicular point on seg'th line */
6074 			if (d < (*dist_min)) {	/* Update minimum distance */
6075 				*dist_min = d;
6076 				if (return_mindist == 2) { *x_near = xlon; *y_near = xlat;}	/* Also update (x,y) of nearest point on the line */
6077 				else if (return_mindist == 3) {	/* Also update (seg, pt) of nearest point on the line */
6078 					*x_near = (double)seg;
6079 					prev_row = row - 1;
6080 					dist_AB = gmt_distance (P, S->data[GMT_X][prev_row], S->data[GMT_Y][prev_row], S->data[GMT_X][row], S->data[GMT_Y][row]);
6081 					fraction = (dist_AB > 0.0) ? gmt_distance (P, S->data[GMT_X][prev_row], S->data[GMT_Y][prev_row], xlon, xlat) / dist_AB : 0.0;
6082 					*y_near = (double)prev_row + fraction;
6083 				}
6084 				within = true;	/* Found at least one segment with a valid inside distance */
6085 			}
6086 		}
6087 		if (cx_dist >= cos_dist) return (true);	/* X is on the A-B extension AND within specified distance */
6088 	}
6089 
6090 	if (return_mindist && *dist_min < GMT_CONV8_LIMIT) *dist_min = 0.0;
6091 
6092 	return (within);	/* All tests failed, we are not close to the line(s), or we return a mindist (see comments above) */
6093 }
6094 
6095 /*! . */
gmtmap_near_lines_spherical(struct GMT_CTRL * P,double lon,double lat,struct GMT_DATATABLE * T,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)6096 GMT_LOCAL bool gmtmap_near_lines_spherical (struct GMT_CTRL *P, double lon, double lat, struct GMT_DATATABLE *T, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
6097 	uint64_t seg;
6098 	int mode = return_mindist, status;
6099 	bool OK = false;
6100 	if (mode >= 10) mode -= 10;	/* Exclude (or flag) circular region surrounding line endpoints */
6101 	if (mode) *dist_min = DBL_MAX;	/* Want to find the minimum distance so init to huge */
6102 
6103 	for (seg = 0; seg < T->n_segments; seg++) {	/* Loop over each line segment */
6104 		status = gmtmap_near_a_line_spherical (P, lon, lat, seg, T->segment[seg], return_mindist, dist_min, x_near, y_near);
6105 		if (status) {	/* Got a min distance or satisfied the min dist requirement */
6106 			if (!return_mindist) return (true);	/* Done, we are within distance of one of the lines */
6107 			OK = true;
6108 		}
6109 	}
6110 	return (OK);
6111 }
6112 
6113 /*! . */
gmtmap_init_three_D(struct GMT_CTRL * GMT)6114 GMT_LOCAL int gmtmap_init_three_D (struct GMT_CTRL *GMT) {
6115 	unsigned int i;
6116 	bool easy, positive;
6117 	double x, y, zmin = 0.0, zmax = 0.0, z_range;
6118 
6119 	GMT->current.proj.three_D = (GMT->current.proj.z_project.view_azimuth != 180.0 || GMT->current.proj.z_project.view_elevation != 90.0);
6120 	GMT->current.proj.scale[GMT_Z] = GMT->current.proj.z_pars[0];
6121 	GMT->current.proj.xyz_pos[GMT_Z] = (GMT->current.proj.scale[GMT_Z] >= 0.0);	/* Increase z up or not */
6122 	/* z_level == DBL_MAX is signaling that it was not set by the user. In that case we change it to the lower z level */
6123 	if (GMT->current.proj.z_level == DBL_MAX) GMT->current.proj.z_level = (GMT->current.proj.xyz_pos[GMT_Z]) ?  GMT->common.R.wesn[ZLO] : GMT->common.R.wesn[ZHI];
6124 
6125 	switch (GMT->current.proj.xyz_projection[GMT_Z]%3) {	/* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
6126 		case GMT_LINEAR:	/* Regular scaling */
6127 			zmin = (GMT->current.proj.xyz_pos[GMT_Z]) ? GMT->common.R.wesn[ZLO] : GMT->common.R.wesn[ZHI];
6128 			zmax = (GMT->current.proj.xyz_pos[GMT_Z]) ? GMT->common.R.wesn[ZHI] : GMT->common.R.wesn[ZLO];
6129 			GMT->current.proj.fwd_z = &gmtlib_translin;
6130 			GMT->current.proj.inv_z = &gmtlib_itranslin;
6131 			break;
6132 		case GMT_LOG10:	/* Log10 transformation */
6133 			if (GMT->common.R.wesn[ZLO] <= 0.0 || GMT->common.R.wesn[ZHI] <= 0.0) {
6134 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Jz -JZ: limits must be positive for log10 projection\n");
6135 				return GMT_PROJECTION_ERROR;
6136 			}
6137 			zmin = (GMT->current.proj.xyz_pos[GMT_Z]) ? d_log10 (GMT, GMT->common.R.wesn[ZLO]) : d_log10 (GMT, GMT->common.R.wesn[ZHI]);
6138 			zmax = (GMT->current.proj.xyz_pos[GMT_Z]) ? d_log10 (GMT, GMT->common.R.wesn[ZHI]) : d_log10 (GMT, GMT->common.R.wesn[ZLO]);
6139 			GMT->current.proj.fwd_z = &gmtproj_translog10;
6140 			GMT->current.proj.inv_z = &gmtproj_itranslog10;
6141 			break;
6142 		case GMT_POW:	/* x^y transformation */
6143 			GMT->current.proj.xyz_pow[GMT_Z] = GMT->current.proj.z_pars[1];
6144 			GMT->current.proj.xyz_ipow[GMT_Z] = 1.0 / GMT->current.proj.z_pars[1];
6145 			positive = !((GMT->current.proj.xyz_pos[GMT_Z] + (GMT->current.proj.xyz_pow[GMT_Z] > 0.0)) % 2);
6146 			zmin = (positive) ? pow (GMT->common.R.wesn[ZLO], GMT->current.proj.xyz_pow[GMT_Z]) : pow (GMT->common.R.wesn[ZHI], GMT->current.proj.xyz_pow[GMT_Z]);
6147 			zmax = (positive) ? pow (GMT->common.R.wesn[ZHI], GMT->current.proj.xyz_pow[GMT_Z]) : pow (GMT->common.R.wesn[ZLO], GMT->current.proj.xyz_pow[GMT_Z]);
6148 			GMT->current.proj.fwd_z = &gmtproj_transpowz;
6149 			GMT->current.proj.inv_z = &gmtproj_itranspowz;
6150 	}
6151 	z_range = zmax - zmin;
6152 	if (z_range == 0.0 && GMT->current.proj.compute_scale[GMT_Z])
6153 		GMT->current.proj.scale[GMT_Z] = 0.0;	/* No range given, just flat projected map */
6154 	else if (GMT->current.proj.compute_scale[GMT_Z])
6155 		GMT->current.proj.scale[GMT_Z] /= fabs (z_range);
6156 	GMT->current.proj.zmax = z_range * GMT->current.proj.scale[GMT_Z];
6157 	GMT->current.proj.origin[GMT_Z] = -zmin * GMT->current.proj.scale[GMT_Z];
6158 
6159 	if (GMT->current.proj.z_project.view_azimuth >= 360.0) GMT->current.proj.z_project.view_azimuth -= 360.0;
6160 	if (GMT->current.proj.z_project.view_azimuth < 0.0)    GMT->current.proj.z_project.view_azimuth += 360.0;
6161 	GMT->current.proj.z_project.quadrant = urint (floor (GMT->current.proj.z_project.view_azimuth / 90.0)) + 1;
6162 	sincosd (GMT->current.proj.z_project.view_azimuth, &GMT->current.proj.z_project.sin_az, &GMT->current.proj.z_project.cos_az);
6163 	sincosd (GMT->current.proj.z_project.view_elevation, &GMT->current.proj.z_project.sin_el, &GMT->current.proj.z_project.cos_el);
6164 
6165 	/* Determine min/max y of plot */
6166 
6167 	/* easy = true means we can use 4 corner points to find min x and y, false
6168 	   means we must search along wesn perimeter the hard way */
6169 
6170 	switch (GMT->current.proj.projection_GMT) {
6171 		case GMT_LINEAR:
6172 		case GMT_MERCATOR:
6173 		case GMT_OBLIQUE_MERC:
6174 		case GMT_CYL_EQ:
6175 		case GMT_CYL_EQDIST:
6176 		case GMT_CYL_STEREO:
6177 		case GMT_MILLER:
6178 			easy = true;
6179 			break;
6180 		case GMT_POLAR:
6181 		case GMT_LAMBERT:
6182 		case GMT_TM:
6183 		case GMT_UTM:
6184 		case GMT_CASSINI:
6185 		case GMT_STEREO:
6186 		case GMT_ALBERS:
6187 		case GMT_ECONIC:
6188 		case GMT_POLYCONIC:
6189 		case GMT_LAMB_AZ_EQ:
6190 		case GMT_ORTHO:
6191 		case GMT_GENPER:
6192 		case GMT_GNOMONIC:
6193 		case GMT_AZ_EQDIST:
6194 		case GMT_SINUSOIDAL:
6195 		case GMT_MOLLWEIDE:
6196 		case GMT_HAMMER:
6197 		case GMT_VANGRINTEN:
6198 		case GMT_WINKEL:
6199 		case GMT_ECKERT4:
6200 		case GMT_ECKERT6:
6201 		case GMT_ROBINSON:
6202 			easy = GMT->common.R.oblique;
6203 			break;
6204 		default:
6205 			easy = false;
6206 			break;
6207 	}
6208 
6209 	if (!GMT->current.proj.three_D) easy = true;
6210 
6211 	GMT->current.proj.z_project.xmin = GMT->current.proj.z_project.ymin = DBL_MAX;
6212 	GMT->current.proj.z_project.xmax = GMT->current.proj.z_project.ymax = -DBL_MAX;
6213 
6214 	if (easy) {
6215 		double xx[4], yy[4];
6216 
6217 		xx[0] = xx[3] = GMT->current.proj.rect[XLO]; xx[1] = xx[2] = GMT->current.proj.rect[XHI];
6218 		yy[0] = yy[1] = GMT->current.proj.rect[YLO]; yy[2] = yy[3] = GMT->current.proj.rect[YHI];
6219 
6220 		for (i = 0; i < 4; i++) {
6221 			gmt_xy_to_geo (GMT, &GMT->current.proj.z_project.corner_x[i], &GMT->current.proj.z_project.corner_y[i], xx[i], yy[i]);
6222 			gmt_xyz_to_xy (GMT, xx[i], yy[i], gmt_z_to_zz(GMT, GMT->common.R.wesn[ZLO]), &x, &y);
6223 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6224 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6225 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6226 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6227 			gmt_xyz_to_xy (GMT, xx[i], yy[i], gmt_z_to_zz(GMT, GMT->common.R.wesn[ZHI]), &x, &y);
6228 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6229 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6230 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6231 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6232 		}
6233 	}
6234 	else if (GMT->current.proj.r > 0.0) {	/* Do not think the next four lines mean anything in this case, just copied from the general case */
6235 		GMT->current.proj.z_project.corner_x[0] = GMT->current.proj.z_project.corner_x[3] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
6236 		GMT->current.proj.z_project.corner_x[1] = GMT->current.proj.z_project.corner_x[2] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO];
6237 		GMT->current.proj.z_project.corner_y[0] = GMT->current.proj.z_project.corner_y[1] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
6238 		GMT->current.proj.z_project.corner_y[2] = GMT->current.proj.z_project.corner_y[3] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO];
6239 		for (i = 0; i < 360; i++) {	/* Go around the circle */
6240 			sincosd (i * 1.0, &y, &x);
6241 			gmt_xyz_to_xy (GMT, GMT->current.proj.r * (1.0 + x), GMT->current.proj.r * (1.0 + y), gmt_z_to_zz(GMT, GMT->common.R.wesn[ZLO]), &x, &y);
6242 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6243 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6244 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6245 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6246 			gmt_xyz_to_xy (GMT, GMT->current.proj.r * (1.0 + x), GMT->current.proj.r * (1.0 + y), gmt_z_to_zz(GMT, GMT->common.R.wesn[ZHI]), &x, &y);
6247 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6248 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6249 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6250 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6251 		}
6252 	}
6253 	else {
6254 		GMT->current.proj.z_project.corner_x[0] = GMT->current.proj.z_project.corner_x[3] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
6255 		GMT->current.proj.z_project.corner_x[1] = GMT->current.proj.z_project.corner_x[2] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO];
6256 		GMT->current.proj.z_project.corner_y[0] = GMT->current.proj.z_project.corner_y[1] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
6257 		GMT->current.proj.z_project.corner_y[2] = GMT->current.proj.z_project.corner_y[3] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO];
6258 		for (i = 0; i < GMT->current.map.n_lon_nodes; i++) {	/* S and N */
6259 			gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], GMT->common.R.wesn[ZLO], &x, &y);
6260 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6261 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6262 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6263 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6264 			if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6265 				gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], GMT->common.R.wesn[ZHI], &x, &y);
6266 				GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6267 				GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6268 				GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6269 				GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6270 			}
6271 			gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], GMT->common.R.wesn[ZLO], &x, &y);
6272 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6273 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6274 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6275 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6276 			if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6277 				gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], GMT->common.R.wesn[ZHI], &x, &y);
6278 				GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6279 				GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6280 				GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6281 				GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6282 			}
6283 		}
6284 		for (i = 0; i < GMT->current.map.n_lat_nodes; i++) {	/* W and E */
6285 			gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZLO], &x, &y);
6286 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6287 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6288 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6289 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6290 			if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6291 				gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZHI], &x, &y);
6292 				GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6293 				GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6294 				GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6295 				GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6296 			}
6297 			gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZLO], &x, &y);
6298 			GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6299 			GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6300 			GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6301 			GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6302 			if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6303 				gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZHI], &x, &y);
6304 				GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6305 				GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6306 				GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6307 				GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6308 			}
6309 		}
6310 	}
6311 
6312 	GMT->current.proj.z_project.face[0] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 2) ? 0 : 1;
6313 	GMT->current.proj.z_project.face[1] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 4) ? 2 : 3;
6314 	GMT->current.proj.z_project.face[2] = (GMT->current.proj.z_project.view_elevation >= 0.0) ? 4 : 5;
6315 	GMT->current.proj.z_project.draw[0] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 4) ? true : false;
6316 	GMT->current.proj.z_project.draw[1] = (GMT->current.proj.z_project.quadrant == 3 || GMT->current.proj.z_project.quadrant == 4) ? true : false;
6317 	GMT->current.proj.z_project.draw[2] = (GMT->current.proj.z_project.quadrant == 2 || GMT->current.proj.z_project.quadrant == 3) ? true : false;
6318 	GMT->current.proj.z_project.draw[3] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 2) ? true : false;
6319 	GMT->current.proj.z_project.sign[0] = GMT->current.proj.z_project.sign[3] = -1.0;
6320 	GMT->current.proj.z_project.sign[1] = GMT->current.proj.z_project.sign[2] = 1.0;
6321 	GMT->current.proj.z_project.z_axis = (GMT->current.proj.z_project.quadrant%2) ? GMT->current.proj.z_project.quadrant : GMT->current.proj.z_project.quadrant - 2;
6322 
6323 	if (GMT->current.proj.z_project.fixed) {
6324 		if (!GMT->current.proj.z_project.world_given) {	/* Pick center point of region */
6325 			GMT->current.proj.z_project.world_x = (gmt_M_is_geographic (GMT, GMT_IN)) ? GMT->current.proj.central_meridian : 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
6326 			GMT->current.proj.z_project.world_y = 0.5 * (GMT->common.R.wesn[YLO] + GMT->common.R.wesn[YHI]);
6327 			GMT->current.proj.z_project.world_z = GMT->current.proj.z_level;
6328 		}
6329 		gmt_geoz_to_xy (GMT, GMT->current.proj.z_project.world_x, GMT->current.proj.z_project.world_y, GMT->current.proj.z_project.world_z, &x, &y);
6330 		if (!GMT->current.proj.z_project.view_given) {	/* Pick center of current page */
6331 			GMT->current.proj.z_project.view_x = 0.5 * GMT->current.setting.ps_page_size[0] * GMT->session.u2u[GMT_PT][GMT_INCH];
6332 			GMT->current.proj.z_project.view_y = 0.5 * GMT->current.setting.ps_page_size[1] * GMT->session.u2u[GMT_PT][GMT_INCH];
6333 		}
6334 		GMT->current.proj.z_project.x_off = GMT->current.proj.z_project.view_x - x;
6335 		GMT->current.proj.z_project.y_off = GMT->current.proj.z_project.view_y - y;
6336 	}
6337 	else {
6338 		GMT->current.proj.z_project.x_off = -GMT->current.proj.z_project.xmin;
6339 		GMT->current.proj.z_project.y_off = -GMT->current.proj.z_project.ymin;
6340 	}
6341 
6342 	/* Adjust the xmin/xmax and ymin/ymax because of xoff and yoff */
6343 	GMT->current.proj.z_project.xmin += GMT->current.proj.z_project.x_off;
6344 	GMT->current.proj.z_project.xmax += GMT->current.proj.z_project.x_off;
6345 	GMT->current.proj.z_project.ymin += GMT->current.proj.z_project.y_off;
6346 	GMT->current.proj.z_project.ymax += GMT->current.proj.z_project.y_off;
6347 
6348 	return (GMT_NOERROR);
6349 }
6350 
6351 /*! . */
gmtmap_geodesic_dist_cos(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)6352 GMT_LOCAL double gmtmap_geodesic_dist_cos (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
6353 	/* Convenience function to get cosine instead */
6354 	return (cosd (gmtmap_geodesic_dist_degree (GMT, lonS, latS, lonE, latE)));
6355 }
6356 
6357 /*! . */
gmtlib_great_circle_dist_cos(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)6358 double gmtlib_great_circle_dist_cos (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
6359 	/* Return cosine of great circle distance */
6360 
6361 	double sin_half_squared = gmtmap_haversine (GMT, lon1, lat1, lon2, lat2);
6362 	return (1.0 - 2.0 * sin_half_squared);	/* Convert sin^2 (half-angle) to cos (angle) */
6363 }
6364 
6365 /*! . */
gmtmap_cartesian_dist_periodic(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)6366 GMT_LOCAL double gmtmap_cartesian_dist_periodic (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
6367 	/* Calculates the good-old straight line distance in users units */
6368 	double dx = x1 - x0, dy = y1 - y0;
6369 	if (GMT->common.n.periodic[GMT_X] && (dx = fabs (dx)) > GMT->common.n.half_range[GMT_X]) dx = GMT->common.n.range[GMT_X] - dx;
6370 	if (GMT->common.n.periodic[GMT_Y] && (dy = fabs (dy)) > GMT->common.n.half_range[GMT_Y]) dy = GMT->common.n.range[GMT_Y] - dy;
6371 	return (hypot (dx, dy));
6372 }
6373 
6374 /*! . */
gmtmap_set_distaz(struct GMT_CTRL * GMT,unsigned int mode,unsigned int type,char * unit_name)6375 GMT_LOCAL int gmtmap_set_distaz (struct GMT_CTRL *GMT, unsigned int mode, unsigned int type, char *unit_name) {
6376 	/* Assigns pointers to the chosen distance and azimuth functions */
6377 	char *type_name[3] = {"Map", "Contour", "Contour annotation"};
6378 	char *aux[6] = {"no", "authalic", "conformal", "meridional", "geocentric", "parametric"};
6379 	char *rad[5] = {"mean (R_1)", "authalic (R_2)", "volumetric (R_3)", "meridional", "quadratic"};
6380 	int choice = (GMT->current.setting.proj_aux_latitude == GMT_LATSWAP_NONE) ? 0 : 1 + GMT->current.setting.proj_aux_latitude/2;
6381 	GMT->current.map.dist[type].scale = 1.0;	/* Default scale */
6382 
6383 	switch (mode) {	/* Set pointers to distance functions */
6384 		case GMT_CARTESIAN_DIST_PERIODIC:	/* Cartesian 2-D x,y data but with one or two periodic dimensions */
6385 			GMT->current.map.dist[type].func = &gmtmap_cartesian_dist_periodic;
6386 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian;
6387 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian [periodic]\n", type_name[type]);
6388 			break;
6389 		case GMT_CARTESIAN_DIST:	/* Cartesian 2-D x,y data */
6390 			GMT->current.map.dist[type].func = &gmtlib_cartesian_dist;
6391 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian;
6392 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian\n", type_name[type]);
6393 			break;
6394 		case GMT_CARTESIAN_DIST2:	/* Cartesian 2-D x,y data, use r^2 instead of hypot */
6395 			GMT->current.map.dist[type].func = &gmtmap_cartesian_dist2;
6396 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian;
6397 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian\n", type_name[type]);
6398 			break;
6399 		case GMT_CARTESIAN_DIST_PROJ:	/* Cartesian distance after projecting 2-D lon,lat data */
6400 			GMT->current.map.dist[type].func = &gmtlib_cartesian_dist_proj;
6401 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian_proj;
6402 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian after first projecting via -J\n", type_name[type]);
6403 			break;
6404 		case GMT_CARTESIAN_DIST_PROJ2:	/* Cartesian distance after projecting 2-D lon,lat data, use r^2 instead of hypot  */
6405 			GMT->current.map.dist[type].func = &gmtmap_cartesian_dist_proj2;
6406 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian_proj;
6407 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian after first projecting via -J\n", type_name[type]);
6408 			break;
6409 		case GMT_DIST_M+GMT_FLATEARTH:	/* 2-D lon, lat data, but scale to Cartesian flat earth in meter */
6410 			GMT->current.map.dist[type].func = &gmtmap_flatearth_dist_meter;
6411 			GMT->current.map.azimuth_func  = &gmtmap_az_backaz_flatearth;
6412 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Flat Earth in %s\n", type_name[type], unit_name);
6413 			break;
6414 		case GMT_DIST_M+GMT_GREATCIRCLE:	/* 2-D lon, lat data, use spherical distances in meter */
6415 			GMT->current.map.dist[type].func = &gmt_great_circle_dist_meter;
6416 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_sphere;
6417 			GMT->current.map.second_point = &gmtmap_translate_point_spherical;
6418 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using great circle approximation with %s auxiliary latitudes and %s radius = %.4f m, in %s.\n",
6419 				type_name[type], aux[choice], rad[GMT->current.setting.proj_mean_radius], GMT->current.proj.mean_radius, unit_name);
6420 			break;
6421 		case GMT_DIST_M+GMT_GEODESIC:	/* 2-D lon, lat data, use geodesic distances in meter */
6422 			GMT->current.map.dist[type].func = GMT->current.map.geodesic_meter;
6423 			GMT->current.map.azimuth_func = GMT->current.map.geodesic_az_backaz;
6424 			GMT->current.map.second_point = &gmtmap_translate_point_geodesic;
6425 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using %s geodesics in %s\n", type_name[type], GEOD_TEXT[GMT->current.setting.proj_geodesic], unit_name);
6426 			break;
6427 		case GMT_DIST_DEG+GMT_FLATEARTH:	/* 2-D lon, lat data, use Flat Earth distances in degrees */
6428 			GMT->current.map.dist[type].func = gmtmap_flatearth_dist_degree;
6429 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_flatearth;
6430 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Flat Earth in %s\n", type_name[type], unit_name);
6431 			break;
6432 		case GMT_DIST_DEG+GMT_GREATCIRCLE:	/* 2-D lon, lat data, use spherical distances in degrees */
6433 			GMT->current.map.dist[type].func = &gmtlib_great_circle_dist_degree;
6434 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_sphere;
6435 			GMT->current.map.second_point = &gmtmap_translate_point_spherical;
6436 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using great circle approximation with %s auxiliary latitudes and return lengths in %s.\n", unit_name,
6437 				type_name[type], aux[choice]);
6438 			break;
6439 		case GMT_DIST_DEG+GMT_GEODESIC:	/* 2-D lon, lat data, use geodesic distances in degrees */
6440 			GMT->current.map.dist[type].func = &gmtmap_geodesic_dist_degree;
6441 			GMT->current.map.azimuth_func = GMT->current.map.geodesic_az_backaz;
6442 			GMT->current.map.second_point = &gmtmap_translate_point_geodesic;
6443 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using geodesics in %s\n", type_name[type], unit_name);
6444 			break;
6445 		case GMT_DIST_COS+GMT_GREATCIRCLE:	/* 2-D lon, lat data, and Green's function needs cosine of spherical distance */
6446 			GMT->current.map.dist[type].func = &gmtlib_great_circle_dist_cos;
6447 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_sphere;
6448 			GMT->current.map.second_point = &gmtmap_translate_point_spherical;
6449 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using great circle approximation with %s auxiliary latitudes and return cosine of spherical angles.\n",
6450 				type_name[type], aux[choice]);
6451 			break;
6452 		case GMT_DIST_COS+GMT_GEODESIC:	/* 2-D lon, lat data, and Green's function needs cosine of geodesic distance */
6453 			GMT->current.map.dist[type].func = &gmtmap_geodesic_dist_cos;
6454 			GMT->current.map.azimuth_func = GMT->current.map.geodesic_az_backaz;
6455 			GMT->current.map.second_point = &gmtmap_translate_point_geodesic;
6456 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using cosine of geodesic angle\n", type_name[type]);
6457 			break;
6458 		case GMT_DIST_M+GMT_LOXODROME:	/* 2-D lon, lat data, but measure distance along rhumblines in meter */
6459 			GMT->current.map.dist[type].func = &gmtmap_loxodrome_dist_meter;
6460 			GMT->current.map.azimuth_func  = &gmtmap_az_backaz_loxodrome;
6461 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be along loxodromes in meters\n", type_name[type]);
6462 			break;
6463 		case GMT_DIST_DEG+GMT_LOXODROME:	/* 2-D lon, lat data, but measure distance along rhumblines in degrees */
6464 			GMT->current.map.dist[type].func = &gmtmap_loxodrome_dist_degree;
6465 			GMT->current.map.azimuth_func = &gmtmap_az_backaz_loxodrome;
6466 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be along loxodromes with %s auxiliary latitudes and return lengths in degrees.\n",
6467 				type_name[type], aux[choice]);
6468 			break;
6469 		default:	/* Cannot happen unless we make a bug */
6470 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Mode (=%d) for distance function is unknown. Must be bug.\n", mode);
6471 			return (GMT_PROJECTION_ERROR);
6472 			break;
6473 	}
6474 	if (type > 0) return (GMT_NOERROR);	/* Contour-related assignments end here */
6475 
6476 	/* Mapping only */
6477 	if (mode == GMT_CARTESIAN_DIST || mode == GMT_CARTESIAN_DIST2)	{	/* Cartesian data */
6478 		GMT->current.map.near_lines_func   = &gmtmap_near_lines_cartesian;
6479 		GMT->current.map.near_a_line_func  = &gmtmap_near_a_line_cartesian;
6480 		GMT->current.map.near_point_func   = &gmtmap_near_a_point_cartesian;
6481 	}
6482 	else {	/* Geographic data */
6483 		GMT->current.map.near_lines_func   = &gmtmap_near_lines_spherical;
6484 		GMT->current.map.near_a_line_func  = &gmtmap_near_a_line_spherical;
6485 		GMT->current.map.near_point_func   = &gmtmap_near_a_point_spherical;
6486 	}
6487 	return (GMT_NOERROR);
6488 }
6489 
6490 
6491 #if 0
6492 void GMT_set_geocentric (struct GMT_CTRL *GMT, bool notify)
6493 {	/* The idea is to call this from sample1d/grdtrack */
6494 	/* Set up ellipsoid parameters for spherical approximation with geocentric parameters */
6495 
6496 	GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2O;	/* Geocentric/Geodetic conversion */
6497 	GMT->current.setting.proj_mean_radius = GMT_RADIUS_MERIDIONAL;
6498 	gmtlib_init_ellipsoid (GMT);
6499 }
6500 #endif
6501 
6502 /* GMT_dateline_clip simply clips a polygon against the dateline and results in two polygons in L */
6503 
6504 /*! . */
gmtlib_set_oblique_pole_and_origin(struct GMT_CTRL * GMT,double plon,double plat,double olon,double olat)6505 void gmtlib_set_oblique_pole_and_origin (struct GMT_CTRL *GMT, double plon, double plat, double olon, double olat) {
6506 	/* The set quantities are used in gmtproj_obl and gmtlib_iobl */
6507 	/* Get forward pole and origin vectors FP, FC */
6508 	double P[3];
6509 	gmt_geo_to_cart (GMT, plat, plon, GMT->current.proj.o_FP, true);	/* Set forward Cartesian pole o_FP */
6510 	gmt_geo_to_cart (GMT, olat, olon, P, true);			/* P points to the origin  */
6511 	gmt_cross3v (GMT, GMT->current.proj.o_FP, P, GMT->current.proj.o_FC);	/* Set forward Cartesian center o_FC */
6512 	gmt_normalize3v (GMT, GMT->current.proj.o_FC);
6513 
6514 	/* Get inverse pole and origin vectors FP, FC */
6515 	gmtproj_obl (GMT, 0.0, M_PI_2, &plon, &plat);
6516 	gmt_geo_to_cart (GMT, plat, plon, GMT->current.proj.o_IP, false);	/* Set inverse Cartesian pole o_IP */
6517 	gmtproj_obl (GMT, 0.0, 0.0, &olon, &olat);
6518 	gmt_geo_to_cart (GMT, olat, olon, P, false);			/* P points to origin  */
6519 	gmt_cross3v (GMT, GMT->current.proj.o_IP, P, GMT->current.proj.o_IC);	/* Set inverse Cartesian center o_FC */
6520 	gmt_normalize3v (GMT, GMT->current.proj.o_IC);
6521 }
6522 
6523 /*! . */
gmt_cart_outside(struct GMT_CTRL * GMT,double x,double y)6524 bool gmt_cart_outside (struct GMT_CTRL *GMT, double x, double y) {
6525 	/* Expects x,y to be projected and comparing with rectangular projected domain */
6526 	/* Note PW: 8-20-2014: The on_border_is_outside tests had GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT.  Trying the latter */
6527 	if (GMT->current.map.on_border_is_outside && fabs (x - GMT->current.proj.rect[XLO]) < GMT_CONV8_LIMIT)
6528 		GMT->current.map.this_x_status = -1;
6529 	else if (GMT->current.map.on_border_is_outside && fabs (x - GMT->current.proj.rect[XHI]) < GMT_CONV8_LIMIT)
6530 		GMT->current.map.this_x_status = 1;
6531 	else if (x < GMT->current.proj.rect[XLO] - GMT_CONV8_LIMIT)
6532 		GMT->current.map.this_x_status = -2;
6533 	else if (x > GMT->current.proj.rect[XHI] + GMT_CONV8_LIMIT)
6534 		GMT->current.map.this_x_status = 2;
6535 	else
6536 		GMT->current.map.this_x_status = 0;
6537 
6538 	if (GMT->current.map.on_border_is_outside && fabs (y -GMT->current.proj.rect[YLO]) < GMT_CONV8_LIMIT)
6539 		GMT->current.map.this_y_status = -1;
6540 	else if (GMT->current.map.on_border_is_outside && fabs (y - GMT->current.proj.rect[YHI]) < GMT_CONV8_LIMIT)
6541 		GMT->current.map.this_y_status = 1;
6542 	else if (y < GMT->current.proj.rect[YLO] - GMT_CONV8_LIMIT)
6543 		GMT->current.map.this_y_status = -2;
6544 	else if (y > GMT->current.proj.rect[YHI] + GMT_CONV8_LIMIT)
6545 		GMT->current.map.this_y_status = 2;
6546 	else
6547 		GMT->current.map.this_y_status = 0;
6548 
6549 	return ((GMT->current.map.this_x_status != 0 || GMT->current.map.this_y_status != 0) ? true : false);
6550 }
6551 
6552 /*! . */
gmt_clip_to_map(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t np,double ** x,double ** y)6553 uint64_t gmt_clip_to_map (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t np, double **x, double **y) {
6554 	/* This routine makes sure that all points are either inside or on the map boundary
6555 	 * and returns the number of points to be used for plotting (in x,y units) */
6556 
6557 	uint64_t i, out, n;
6558 	uint64_t total_nx = 0;
6559 	int64_t out_x, out_y, np2;
6560 	bool polygon;
6561 	double *xx = NULL, *yy = NULL;
6562 
6563 	/* First check for trivial cases:  All points outside or all points inside */
6564 
6565 	for (i = out = out_x = out_y = 0; i < np; i++)  {
6566 		(void) gmt_map_outside (GMT, lon[i], lat[i]);
6567 		out_x += GMT->current.map.this_x_status;	/* Completely left of west gives -2 * np, right of east gives + 2 * np */
6568 		out_y += GMT->current.map.this_y_status;	/* Completely below south gives -2 * np, above north gives + 2 * np */
6569 		out += (abs (GMT->current.map.this_x_status) == 2 || abs (GMT->current.map.this_y_status) == 2);
6570 	}
6571 	if (out == 0) {		/* All points are inside map boundary; no clipping required */
6572 		gmt_M_malloc2 (GMT, xx, yy, np, NULL, double);
6573 		for (i = 0; i < np; i++) gmt_geo_to_xy (GMT, lon[i], lat[i], &xx[i], &yy[i]);
6574 		*x = xx;	*y = yy;	n = np;
6575 	}
6576 	else if (out == np) {	/* All points are outside map boundary */
6577 		np2 = 2 * np;
6578 		if (int64_abs (out_x) == np2 || int64_abs (out_y) == np2)	/* All points safely outside the region, no part of polygon survives */
6579 			n = 0;
6580 		else {	/* All points are outside, but they are not just to one side so lines _may_ intersect the region */
6581 			n = (*GMT->current.map.clip) (GMT, lon, lat, np, x, y, &total_nx);
6582 			polygon = (np > 2 && !gmt_polygon_is_open (GMT, lon, lat, np));	/* The following can only be used on closed polygons */
6583 			/* Polygons that completely contains the -R region will not generate crossings, just duplicate -R box */
6584 			if (polygon && n > 0 && total_nx == 0) {	/* No crossings and all points outside means one of two things: */
6585 				/* Either the polygon contains portions of the -R region including corners or it does not.  We pick the corners and check for insidedness: */
6586 				bool ok = false;
6587 				if (gmt_non_zero_winding (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], lon, lat, np)) ok = true;		/* true if inside */
6588 				if (!ok && gmt_non_zero_winding (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], lon, lat, np)) ok = true;	/* true if inside */
6589 				if (!ok && gmt_non_zero_winding (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], lon, lat, np)) ok = true;	/* true if inside */
6590 				if (!ok && gmt_non_zero_winding (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], lon, lat, np)) ok = true;	/* true if inside */
6591 				if (!ok) {
6592 					/* Polygon does NOT contain the region and we delete it */
6593 					n = 0;
6594 					gmt_M_free (GMT, *x);
6595 					gmt_M_free (GMT, *y);
6596 				}
6597 				/* Otherwise the polygon completely contains -R and we pass it along */
6598 			}
6599 			else if (GMT->common.R.oblique && GMT->current.proj.projection_GMT == GMT_AZ_EQDIST && n <= 5 && !strncmp (GMT->init.module_name, "pscoast", 7U)) {
6600 				/* Special check for -JE where a coastline block is completely outside yet fully surrounds the rectangular -R -JE...r region.
6601 				   This results in a rectangular closed polygon after the clipping. */
6602 				n = 0;
6603 				gmt_M_free (GMT, *x);
6604 				gmt_M_free (GMT, *y);
6605 			}
6606 		}
6607 	}
6608 	else	/* Mixed case so we must clip the polygon */
6609 		n = (*GMT->current.map.clip) (GMT, lon, lat, np, x, y, &total_nx);
6610 
6611 	return (n);
6612 }
6613 
6614 /*! . */
gmt_split_poly_at_dateline(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,struct GMT_DATASEGMENT *** Lout)6615 unsigned int gmt_split_poly_at_dateline (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, struct GMT_DATASEGMENT ***Lout) {
6616 	int side, j, np, cross = 0;
6617 	unsigned int smode = (S->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
6618 	uint64_t row, m;
6619 	size_t n_alloc = 0;
6620 	char label[GMT_LEN256] = {""}, *part = "EW";
6621 	double xx[2], yy[2];
6622 	struct GMT_DATASEGMENT **L = NULL;
6623 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
6624 	bool (*inside[2]) (double, double);
6625 	bool (*outside[2]) (double, double);
6626 
6627 
6628 	inside[0] = gmtmap_inside_upper_boundary;	outside[0] = gmtmap_outside_upper_boundary;
6629 	inside[1] = gmtmap_inside_lower_boundary;	outside[1] = gmtmap_outside_lower_boundary;
6630 	L = gmt_M_memory (GMT, NULL, 2, struct GMT_DATASEGMENT *);	/* The two polygons */
6631 
6632 	for (row = 0; row < S->n_rows; row++) gmt_lon_range_adjust (GMT_IS_0_TO_P360_RANGE, &S->data[GMT_X][row]);	/* First enforce 0 <= lon < 360 so we don't have to check again */
6633 
6634 	for (side = 0; side < 2; side++) {	/* Do it twice to get two truncated polygons */
6635 		if (S->n_rows == 0) continue;	/* Nothing! */
6636 		n_alloc = lrint (1.05*S->n_rows + 5);	/* Anticipate just a few crossings (5%)+5, allocate more later if needed */
6637 		L[side] = GMT_Alloc_Segment (GMT->parent, smode, n_alloc, S->n_columns, NULL, NULL);
6638 		m = 0;		/* Start with nuthin' */
6639 
6640 		/* Must ensure we copy the very first point if it is left of the Dateline */
6641 		if (S->data[GMT_X][0] < 180.0) { L[side]->data[GMT_X][0] = S->data[GMT_X][0]; L[side]->data[GMT_Y][0] = S->data[GMT_Y][0]; }	/* First point is inside; add it */
6642 		for (row = 1; row < S->n_rows; row++) {	/* For each line segment */
6643 			np = gmtmap_clip_we (S->data[GMT_X][row-1], S->data[GMT_Y][row-1], S->data[GMT_X][row], S->data[GMT_Y][row], xx, yy, 180.0, inside[side], outside[side], &cross);	/* Returns 0, 1, or 2 points */
6644 			for (j = 0; j < np; j++) {	/* Add the np returned points to the new clipped polygon path */
6645 				if (m == n_alloc) L[side] = GMT_Alloc_Segment (GMT->parent, smode, n_alloc << 2, S->n_columns, NULL, L[side]);
6646 				L[side]->data[GMT_X][m] = xx[j]; L[side]->data[GMT_Y][m] = yy[j]; m++;
6647 			}
6648 		}
6649 		if (m > 2 && gmt_polygon_is_open (GMT, L[side]->data[GMT_X], L[side]->data[GMT_Y], m)) {	/* Do we need to explicitly close this clipped polygon? */
6650 			if (m == n_alloc) L[side] = GMT_Alloc_Segment (GMT->parent, smode, n_alloc << 2, S->n_columns, NULL, L[side]);
6651 			L[side]->data[GMT_X][m] = L[side]->data[GMT_X][0];	L[side]->data[GMT_Y][m] = L[side]->data[GMT_Y][0];	m++;	/* Yes. */
6652 		}
6653 		if (m != n_alloc) L[side] = GMT_Alloc_Segment (GMT->parent, smode, m, S->n_columns, NULL, L[side]);
6654 		L[side]->n_rows = m;
6655 		if (S->label) {
6656 			snprintf (label, GMT_LEN256, "%s part %c", S->label, part[side]);
6657 			L[side]->label = strdup (label);
6658 		}
6659 		if (S->header) L[side]->header = strdup (S->header);
6660 		if (SH->ogr) gmt_duplicate_ogr_seg (GMT, L[side], S);
6661 	}
6662 	SH = gmt_get_DS_hidden (L[0]);
6663 	SH->range = GMT_IS_0_TO_P360;
6664 	SH = gmt_get_DS_hidden (L[1]);
6665 	SH->range = GMT_IS_M360_TO_0_RANGE;
6666 	*Lout = L;
6667 	return (2);
6668 }
6669 
6670 /*! . */
gmtlib_get_point_from_r_az(struct GMT_CTRL * GMT,double lon0,double lat0,double r,double azim,double * lon1,double * lat1)6671 void gmtlib_get_point_from_r_az (struct GMT_CTRL *GMT, double lon0, double lat0, double r, double azim, double *lon1, double *lat1) {
6672 	/* Given point (lon0, lat0), find coordinates of a point r degrees away in the azim direction */
6673 
6674 	double sinr, cosr, sinaz, cosaz, siny, cosy;
6675 	gmt_M_unused(GMT);
6676 
6677 	sincosd (azim, &sinaz, &cosaz);
6678 	sincosd (r, &sinr, &cosr);
6679 	sincosd (lat0, &siny, &cosy);
6680 
6681 	*lon1 = lon0 + atan2d (sinr * sinaz, (cosy * cosr - siny * sinr * cosaz));
6682 	*lat1 = d_asind (siny * cosr + cosy * sinr * cosaz);
6683 }
6684 
6685 /*! . */
gmt_az_backaz(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)6686 double gmt_az_backaz (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
6687 	return (GMT->current.map.azimuth_func (GMT, lonE, latE, lonS, latS, baz));
6688 }
6689 
gmtmap_auto_time_increment(double inc,char * unit)6690 GMT_LOCAL double gmtmap_auto_time_increment (double inc, char *unit) {
6691 	/* Given the first guess interval inc, determine closest time unit and return
6692 	 * the number of seconds in that unit and its code */
6693 	int k, kk = GMT_NOTSET;
6694 	double f;
6695 	static char units[6]  = {'S', 'M', 'H', 'D', 'O', 'Y'};
6696 	static double incs[6] = {1.0, GMT_MIN2SEC_F, GMT_HR2SEC_F, GMT_DAY2SEC_F, GMT_MON2SEC_F, GMT_YR2SEC_F};
6697 	for (k = 5; kk == GMT_NOTSET && k >= 0; k--) {	/* Find the largest time unit that is smaller than guess interval */
6698 		f = inc / incs[k];
6699 		if (irint (f) >= 1)
6700 			kk = k;
6701 	}
6702 	if (kk == GMT_NOTSET) kk = 0;	/* Safety valve */
6703 	*unit = units[kk];
6704 	return (incs[kk]);
6705 }
6706 
gmtmap_polar_az(struct GMT_CTRL * GMT)6707 GMT_LOCAL bool gmtmap_polar_az (struct GMT_CTRL *GMT) {
6708 	return (gmt_M_is_azimuthal (GMT) && GMT->current.proj.polar && !GMT->common.R.oblique);
6709 }
6710 
6711 /*! . */
gmt_auto_frame_interval(struct GMT_CTRL * GMT,unsigned int axis,unsigned int item)6712 void gmt_auto_frame_interval (struct GMT_CTRL *GMT, unsigned int axis, unsigned int item) {
6713 	/* Determine the annotation and frame tick interval when they are not set (interval = 0) */
6714 	int i = 0, n = 6;
6715 	char unit = 's', sunit[2] = {""}, tmp[GMT_LEN16] = {""}, string[GMT_LEN64] = {""}, par[GMT_LEN128] = {""}, ax_code[4] = "xyz";
6716 	bool set_a = false, interval = false, is_time = gmt_M_axis_is_time (GMT, axis);
6717 	double defmaj[7] = {2.0, 5.0, 10.0, 15.0, 30.0, 60.0, 90.0}, defsub[7] = {1.0, 1.0, 2.0, 5.0, 10.0, 15.0, 30.0};
6718 	double Hmaj[4] = {2.0, 3.0, 6.0, 12.0}, Hsub[4] = {1.0, 1.0, 3.0, 3.0};
6719 	double Omaj[4] = {3.0, 6.0}, Osub[4] = {1.0, 3.0};
6720 	double Dmaj[4] = {2.0, 3.0, 7.0, 14.0}, Dsub[4] = {1.0, 1.0, 1.0, 7.0};
6721 	double d, f, p, sxy = 1.0, sz = 1.0, *maj = defmaj, *sub = defsub;
6722 	struct GMT_PLOT_AXIS *A = &GMT->current.map.frame.axis[axis];
6723 	struct GMT_PLOT_AXIS_ITEM *T;
6724 
6725 	if (A->special == GMT_CUSTOM) return;	/* Got custom annotation/tick information via user file */
6726 	if (A->type == GMT_LOG10 || A->type == GMT_POW) return;	/* These axes still needs to have automatic selections implemented */
6727 	if (GMT->current.io.cycle_operator) is_time = false;	/* This axis is time, but now wrapped so treated differently */
6728 	if (!(A->item[item].active && A->item[item].interval == 0.0) &&
6729 		!(A->item[item+2].active && A->item[item+2].interval == 0.0) &&
6730 		!(A->item[item+4].active && A->item[item+4].interval == 0.0)) return;
6731 
6732 	if (GMT->current.proj.three_D) {	/* Make an approximate adjustment to the "sizes" given a perspective view */
6733 		sxy = 1.0 - cosd (GMT->current.proj.z_project.view_elevation);
6734 		sz  = 1.0 - fabs (sind (GMT->current.proj.z_project.view_elevation));
6735 	}
6736 	/* f = frame width/height (inch); d = domain width/height (world coordinates) */
6737 	if (axis == GMT_X) {
6738 		f = sxy * fabs (GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]);
6739 		d = fabs (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]);
6740 		if (gmtmap_polar_az (GMT) && d > 120) f *= M_PI_2;	/* Circle perimeter is the right comparison to circular longitudes */
6741 	}
6742 	else if (axis == GMT_Y) {
6743 		f = sxy * fabs (GMT->current.proj.rect[YHI] - GMT->current.proj.rect[YLO]);
6744 		d = fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]);
6745 		if (gmtmap_polar_az (GMT)) f /= 4;	/* Latitude range is only covering half the plot */
6746 	}
6747 	else {
6748 		f = sz * fabs (GMT->current.proj.zmax - GMT->current.proj.zmin);
6749 		d = fabs (GMT->common.R.wesn[ZHI] - GMT->common.R.wesn[ZLO]);
6750 	}
6751 	f *= GMT->session.u2u[GMT_INCH][GMT_PT];	/* Change to points */
6752 
6753 #ifndef NO_THEMES
6754 	if (GMT->current.setting.run_mode == GMT_MODERN && gmt_M_axis_is_geo (GMT, axis)) {	/* Need more space for degree symbol and WESN letters not considered in the algorithm */
6755 		if (strchr (GMT->current.setting.format_geo_map, 'F'))	/* Need more space for degree symbol and letter */
6756 			d *= 1.2;
6757 		else	/* Just more space for degree symbol */
6758 			d *= 1.1;
6759 	}
6760 #endif
6761 
6762 	/* First guess of interval */
6763 	if (gmtmap_polar_az (GMT))
6764 		d *= MAX (0.05, MIN (5.0 * GMT->current.setting.font_annot[item].size / f, 0.3));
6765 	else
6766 		d *= MAX (0.05, MIN (5.0 * GMT->current.setting.font_annot[item].size / f, 0.2));
6767 
6768 	/* Now determine 'round' major and minor tick intervals */
6769 	if (gmt_M_axis_is_geo (GMT, axis))	/* Geographical coordinate */
6770 		p = (d < GMT_MIN2DEG) ? GMT_SEC2DEG : (d < 1.0) ? GMT_MIN2DEG : 1.0;
6771 	else if (GMT->current.io.cycle_col == axis && GMT->current.io.cycle_operator != GMT_CYCLE_CUSTOM) {
6772 		switch (GMT->current.io.cycle_operator) {
6773 			case GMT_CYCLE_MIN: case GMT_CYCLE_HOUR:	/* With a range of 60 it behaves like geo */
6774 				p = (d < GMT_MIN2DEG) ? GMT_SEC2DEG : (d < 1.0) ? GMT_MIN2DEG : 1.0;
6775 				break;
6776 			case GMT_CYCLE_DAY:	/* With a range of 24 */
6777 				p = (d < 1.0) ? GMT_HR2DAY : 1.0;
6778 				maj = Hmaj; sub = Hsub; n = 3;
6779 				break;
6780 			case GMT_CYCLE_ANNUAL:	/* 12 months */
6781 				p = 1.0;
6782 				maj = Omaj; sub = Osub; n = 1;
6783 				break;
6784 			case GMT_CYCLE_WEEK:	/* Need individual days here */
6785 				d = p = f = 1.0;	n = 0;
6786 				break;
6787 			default:	/* This is the normalized ones which we treat like general linear axis */
6788 				p = pow (10.0, floor (log10 (d)));
6789 				break;
6790 		}
6791 	}
6792 	else if (is_time)	/* Time axis coordinate, get p in seconds and the unit it represents */
6793 		p = gmtmap_auto_time_increment (d, &unit);
6794 	else	/* General (linear) axis */
6795 		p = pow (10.0, floor (log10 (d)));
6796 	d /= p;	/* d is now in degrees, minutes or seconds (or time seconds), or in the range [1;10) */
6797 	if (is_time) {	/* p was in seconds but we will use unit, so reset p = 1 */
6798 		p = 1.0;
6799 		switch (unit) {	/* Select other steps more suitable for month, day, hour.  */
6800 			case 'O': maj = Omaj; sub = Osub; n = 1; break;
6801 			case 'D': maj = Dmaj; sub = Dsub; n = 3; break;
6802 			case 'H': maj = Hmaj; sub = Hsub; n = 3;
6803 				break;
6804 		}
6805 		if ((unit == 'H' || unit == 'M') && !strcmp (GMT->current.setting.format_clock_map, "hh:mm:ss")) {
6806 			/* Strip off the seconds from the formatting if default setting is used */
6807 			strcpy (GMT->current.setting.format_clock_map, "hh:mm");
6808 			gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_map, &GMT->current.plot.calclock.clock, 2);
6809 			sprintf (par, " --FORMAT_CLOCK_MAP=hh:mm");
6810 		}
6811 		else if (unit == 'D' && !strcmp (GMT->current.setting.format_date_map, "yyyy-mm-dd")) {
6812 			/* Use month name and day */
6813 			strcpy (GMT->current.setting.format_date_map, "o dd");
6814 			gmtlib_date_C_format (GMT, GMT->current.setting.format_date_map, &GMT->current.plot.calclock.date, 2);
6815 			sprintf (par, " --FORMAT_DATE_MAP=\"o dd\"");
6816 		}
6817 		else if (unit == 'O' && !strcmp (GMT->current.setting.format_date_map, "yyyy-mm-dd")) {
6818 			/* Use year and month name for month intervals */
6819 			strcpy (GMT->current.setting.format_date_map, "o yyyy");
6820 			gmtlib_date_C_format (GMT, GMT->current.setting.format_date_map, &GMT->current.plot.calclock.date, 2);
6821 			sprintf (par, " --FORMAT_DATE_MAP=\"o yyyy\"");
6822 		}
6823 
6824 		interval = (unit == 'Y' || unit == 'O' || unit == 'D');
6825 	}
6826 	if (n) {
6827 		while (i < n && maj[i] < d) i++;	/* Wind up to largest reasonable interval */
6828 		if ((GMT->current.proj.projection == GMT_MOLLWEIDE || GMT->current.proj.projection == GMT_HAMMER || \
6829 			GMT->current.proj.projection == GMT_ECKERT4) && i == 6) /* Rounded misc. projections */
6830 			d = maj[i-1] * p; 		/* Annotation interval needs to be less than 90 */
6831 		else d = maj[i] * p;		/* Scale up intervals in multiple of unit */
6832 		f = sub[i] * p;
6833 	}
6834 	if (is_time) {	/* Last check to change a 12 month unit to 1 year and 24 hours to 1 day */
6835 		if (unit == 'O' && d == 12.0) d = 1.0, f /= 12.0, unit = 'Y';
6836 		if (unit == 'H' && d == 24.0) d = 1.0, f /= 24.0, unit = 'D';
6837 		sunit[0] = unit;	/* Since we need a string in strcat */
6838 		A->type = GMT_TIME;
6839 	}
6840 
6841 	/* Set annotation/major tick interval */
6842 	T = &A->item[item];
6843 	if (T->active && T->interval == 0.0) {
6844 		T->interval = d, T->generated = set_a = true;
6845 		snprintf (tmp, GMT_LEN16, "a%g", T->interval); strcat (string, tmp);
6846 		if (is_time) T->unit = unit, strcat (string, sunit);
6847 		if (interval) T->type = 'i', T->flavor = 1;
6848 	}
6849 
6850 	/* Set minor ticks as well (if copied from annotation, set to major interval) */
6851 	T = &A->item[item+2];
6852 	if (T->active && T->interval == 0.0) {
6853 		T->interval = (T->type == 'f' || T->type == 'F') ? f : d, T->generated = true;
6854 		snprintf (tmp, GMT_LEN16, "f%g", T->interval); strcat (string, tmp);
6855 		if (is_time) T->unit = unit, strcat (string, sunit);
6856 	}
6857 
6858 	/* Finally set grid interval (if annotation set as well, use major, otherwise minor interval) */
6859 	T = &A->item[item+4];
6860 	if (T->active && T->interval == 0.0) {
6861 		T->interval = set_a ? d : f, T->generated = true; /* Commented out because f is too fine for gridline spacing, even if no annotations */
6862 		// T->interval = d, T->generated = true;
6863 		snprintf (tmp, GMT_LEN16, "g%g", T->interval); strcat (string, tmp);
6864 		if (is_time) T->unit = unit, strcat (string, sunit);
6865 	}
6866 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Auto-frame interval for axis %d item %d: d = %g  f = %g\n", axis, item, d, f);
6867 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Auto-frame interval for %c-axis (item %d): %s%s\n", ax_code[axis], item, string, par);
6868 }
6869 
6870 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6871  *
6872  *	S E C T I O N  1 :	M A P  - T R A N S F O R M A T I O N S
6873  *
6874  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
6875 
6876 /*
6877  * gmt_map_setup sets up the transformations for the chosen map projection.
6878  * The user must pass:
6879  *   w,e,s,n,parameters[0] - parameters[np-1] (np = number of parameters), and project:
6880  *   w,e,s,n defines the area in degrees.
6881  *   project == GMT_LINEAR, GMT_POLAR, GMT_MERCATOR, GMT_STEREO, GMT_LAMBERT, GMT_OBLIQUE_MERC, GMT_UTM,
6882  *	GMT_TM, GMT_ORTHO, GMT_AZ_EQDIST, GMT_LAMB_AZ_EQ, GMT_WINKEL, GMT_ROBINSON, GMT_CASSINI, GMT_ALBERS, GMT_ECONIC,
6883  *	GMT_ECKERT4, GMT_ECKERT6, GMT_CYL_EQ, GMT_CYL_EQDIST, GMT_CYL_STEREO, GMT_MILLER, GMT_VANGRINTEN
6884  *	For GMT_LINEAR, we may have GMT_LINEAR, GMT_LOG10, or GMT_POW
6885  *
6886  * parameters[0] through parameters[np-1] mean different things to the various
6887  * projections, as explained below. (np also varies, of course)
6888  *
6889  * LINEAR projection:
6890  *	parameters[0] is inch (or cm)/x_user_unit.
6891  *	parameters[1] is inch (or cm)/y_user_unit. If 0, then yscale = xscale.
6892  *	parameters[2] is pow for x^pow (if GMT_POW is on).
6893  *	parameters[3] is pow for y^pow (if GMT_POW is on).
6894  *
6895  * POLAR (r,theta) projection:
6896  *	parameters[0] is inch (or cm)/x_user_unit (radius)
6897  *
6898  * MERCATOR projection:
6899  *	parameters[0] is central meridian
6900  *	parameters[1] is standard parallel
6901  *	parameters[2] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6902  *
6903  * STEREOGRAPHIC projection:
6904  *	parameters[0] is longitude of pole
6905  *	parameters[1] is latitude of pole
6906  *	parameters[2] is radius in inches (or cm) from pole to the latitude specified
6907  *	   by parameters[3] OR 1:xxxxx OR map-width.
6908  *
6909  * LAMBERT projection (Conic):
6910  *	parameters[0] is first standard parallel
6911  *	parameters[1] is second standard parallel
6912  *	parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
6913  *
6914  * OBLIQUE MERCATOR projection:
6915  *	parameters[0] is longitude of origin
6916  *	parameters[1] is latitude of origin
6917  *	Definition a:
6918  *		parameters[2] is longitude of second point along oblique equator
6919  *		parameters[3] is latitude of second point along oblique equator
6920  *		parameters[4] is scale in inch (or cm)/degree along oblique equator OR 1:xxxxx OR map-width
6921  *	Definition b:
6922  *		parameters[2] is azimuth along oblique equator at origin
6923  *		parameters[3] is scale in inch (or cm)/degree along oblique equator OR 1:xxxxx OR map-width
6924  *	Definition c:
6925  *		parameters[2] is longitude of pole of projection
6926  *		parameters[3] is latitude of pole of projection
6927  *		parameters[4] is scale in inch (cm)/degree along oblique equator OR 1:xxxxx OR map-width
6928  *
6929  * TRANSVERSE MERCATOR (TM) projection
6930  *	parameters[0] is central meridian
6931  *	parameters[1] is central parallel
6932  *	parameters[2] is scale in inch (cm)/degree along this meridian OR 1:xxxxx OR map-width
6933  *
6934  * UNIVERSAL TRANSVERSE MERCATOR (UTM) projection
6935  *	parameters[0] is UTM zone (0-60, use negative for S hemisphere)
6936  *	parameters[1] is scale in inch (cm)/degree along this meridian OR 1:xxxxx OR map-width
6937  *
6938  * LAMBERT AZIMUTHAL EQUAL AREA projection:
6939  *	parameters[0] is longitude of origin
6940  *	parameters[1] is latitude of origin
6941  *	parameters[2] is radius in inches (or cm) from pole to the latitude specified
6942  *	   by parameters[3] OR 1:xxxxx OR map-width.
6943  *
6944  * ORTHOGRAPHIC AZIMUTHAL projection:
6945  *	parameters[0] is longitude of origin
6946  *	parameters[1] is latitude of origin
6947  *	parameters[2] is radius in inches (or cm) from pole to the latitude specified
6948  *	   by parameters[3] OR 1:xxxxx OR map-width.
6949  *
6950  * GENERAL PERSPECTIVE projection:
6951  *      parameters[0] is longitude of origin
6952  *      parameters[1] is latitude of origin
6953  *      parameters[2] is radius in inches (or cm) from pole to the latitude specified
6954  *         by parameters[3] OR 1:xxxxx OR map-width.
6955  *      parameters[4] is the altitude of the view point in kilometers
6956  *         if altitude is < 10 then it is the distance from the center of the Earth
6957  *              divided by the radius of the Earth
6958  *      parameters[5] is the azimuth east for North of the viewpoint
6959  *      parameters[6] is the tilt upward of the plane of projection
6960  *      parameters[7] is the width of the viewpoint in degrees
6961  *         if = 0, no viewpoint clipping
6962  *      parameters[8] is the height of the viewpoint in degrees
6963  *         if = 0, no viewpoint clipping
6964  *
6965  * AZIMUTHAL EQUIDISTANCE projection:
6966  *	parameters[0] is longitude of origin
6967  *	parameters[1] is latitude of origin
6968  *	parameters[2] is radius in inches (or cm) from pole to the latitude specified
6969  *	   by parameters[3] OR 1:xxxxx OR map-width.
6970  *
6971  * MOLLWEIDE EQUAL-AREA projection
6972  *	parameters[0] is longitude of origin
6973  *	parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6974  *
6975  * HAMMER-AITOFF EQUAL-AREA projection
6976  *	parameters[0] is longitude of origin
6977  *	parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6978  *
6979  * SINUSOIDAL EQUAL-AREA projection
6980  *	parameters[0] is longitude of origin
6981  *	parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6982  *
6983  * WINKEL TRIPEL MODIFIED AZIMUTHAL projection
6984  *	parameters[0] is longitude of origin
6985  *	parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6986  *
6987  * ROBINSON PSEUDOCYLINDRICAL projection
6988  *	parameters[0] is longitude of origin
6989  *	parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6990  *
6991  * VAN DER VANGRINTEN projection
6992  *	parameters[0] is longitude of origin
6993  *	parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6994  *
6995  * CASSINI projection
6996  *	parameters[0] is longitude of origin
6997  *	parameters[1] is latitude of origin
6998  *	parameters[2] is scale in inch (cm)/degree along this meridian OR 1:xxxxx OR map-width
6999  *
7000  * ALBERS projection (Conic):
7001  *	parameters[0] is first standard parallel
7002  *	parameters[1] is second standard parallel
7003  *	parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7004  *
7005  * CONIC EQUIDISTANT projection:
7006  *	parameters[0] is first standard parallel
7007  *	parameters[1] is second standard parallel
7008  *	parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7009  *
7010  * ECKERT6 IV projection:
7011  *	parameters[0] is longitude of origin
7012  *	parameters[1] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7013  *
7014  * ECKERT6 IV projection:
7015  *	parameters[0] is longitude of origin
7016  *	parameters[1] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7017  *
7018  * CYLINDRICAL EQUAL-AREA projections (Behrmann, Gall):
7019  *	parameters[0] is longitude of origin
7020  *	parameters[1] is the standard parallel
7021  *	parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7022  *
7023  * CYLINDRICAL STEREOGRAPHIC projections (Braun, Gall, B.S.A.M.):
7024  *	parameters[0] is longitude of origin
7025  *	parameters[1] is the standard parallel
7026  *	parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7027  *
7028  * MILLER CYLINDRICAL projection:
7029  *	parameters[0] is longitude of origin
7030  *	parameters[1] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7031  *
7032  * Pointers to the correct map transformation functions will be set up so that
7033  * there are no if tests to determine which routine to call. These pointers
7034  * are forward and inverse, and are called from gmt_geo_to_xy and gmt_xy_to_geo.
7035  *
7036  */
7037 
7038 /*
7039  *	GENERIC TRANSFORMATION ROUTINES FOR THE LINEAR PROJECTION
7040  */
7041 
7042 /*! . */
gmt_x_to_xx(struct GMT_CTRL * GMT,double x)7043 double gmt_x_to_xx (struct GMT_CTRL *GMT, double x) {
7044 	/* Converts x to xx using the current linear projection */
7045 	double xx = 0.0;
7046 	(*GMT->current.proj.fwd_x) (GMT, x, &xx);
7047 	return (xx * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
7048 }
7049 
7050 /*! . */
gmt_y_to_yy(struct GMT_CTRL * GMT,double y)7051 double gmt_y_to_yy (struct GMT_CTRL *GMT, double y) {
7052 	/* Converts y to yy using the current linear projection */
7053 	double yy = 0.0;
7054 	(*GMT->current.proj.fwd_y) (GMT, y, &yy);
7055 	return (yy * GMT->current.proj.scale[GMT_Y] + GMT->current.proj.origin[GMT_Y]);
7056 }
7057 
7058 /*! . */
gmt_z_to_zz(struct GMT_CTRL * GMT,double z)7059 double gmt_z_to_zz (struct GMT_CTRL *GMT, double z) {
7060 	/* Converts z to zz using the current linear projection */
7061 	double zz = 0.0;
7062 	(*GMT->current.proj.fwd_z) (GMT, z, &zz);
7063 	return (zz * GMT->current.proj.scale[GMT_Z] + GMT->current.proj.origin[GMT_Z]);
7064 }
7065 
7066 /*! . */
gmt_geo_to_xy(struct GMT_CTRL * GMT,double lon,double lat,double * x,double * y)7067 bool gmt_geo_to_xy (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
7068 	/* Converts lon/lat to x/y using the current projection */
7069 	if (gmt_M_is_dnan (lon) || gmt_M_is_dnan (lat)) {(*x) = (*y) = GMT->session.d_NaN; return true;}	/* Quick and safe way to ensure NaN-input results in NaNs */
7070 	(*GMT->current.proj.fwd) (GMT, lon, lat, x, y);
7071 	(*x) = (*x) * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X];
7072 	(*y) = (*y) * GMT->current.proj.scale[GMT_Y] + GMT->current.proj.origin[GMT_Y];
7073 	return false;
7074 }
7075 
7076 /*! . */
gmt_geo_to_xy_noshift(struct GMT_CTRL * GMT,double lon,double lat,double * x,double * y)7077 bool gmt_geo_to_xy_noshift (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
7078 	/* Converts lon/lat to x/y using the current projection but applies no shift */
7079 	if (gmt_M_is_dnan (lon) || gmt_M_is_dnan (lat)) {(*x) = (*y) = GMT->session.d_NaN; return true;}	/* Quick and safe way to ensure NaN-input results in NaNs */
7080 	(*GMT->current.proj.fwd) (GMT, lon, lat, x, y);
7081 	(*x) = (*x) * GMT->current.proj.scale[GMT_X];
7082 	(*y) = (*y) * GMT->current.proj.scale[GMT_Y];
7083 	return false;
7084 }
7085 
7086 /*! . */
gmt_geo_to_xy_noshiftscale(struct GMT_CTRL * GMT,double lon,double lat,double * x,double * y)7087 bool gmt_geo_to_xy_noshiftscale (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
7088 	/* Converts lon/lat to x/y using the current projection but applies no shift */
7089 	if (gmt_M_is_dnan (lon) || gmt_M_is_dnan (lat)) {(*x) = (*y) = GMT->session.d_NaN; return true;}	/* Quick and safe way to ensure NaN-input results in NaNs */
7090 	(*GMT->current.proj.fwd) (GMT, lon, lat, x, y);
7091 	return false;
7092 }
7093 
7094 /*! . */
gmt_xy_to_geo(struct GMT_CTRL * GMT,double * lon,double * lat,double x,double y)7095 void gmt_xy_to_geo (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
7096 	/* Converts x/y to lon/lat using the current projection */
7097 
7098 	if (gmt_M_is_dnan (x) || gmt_M_is_dnan (y)) {(*lon) = (*lat) = GMT->session.d_NaN; return;}	/* Quick and safe way to ensure NaN-input results in NaNs */
7099 	x = (x - GMT->current.proj.origin[GMT_X]) * GMT->current.proj.i_scale[GMT_X];
7100 	y = (y - GMT->current.proj.origin[GMT_Y]) * GMT->current.proj.i_scale[GMT_Y];
7101 
7102 	(*GMT->current.proj.inv) (GMT, lon, lat, x, y);
7103 }
7104 
7105 /*! . */
gmt_xy_to_geo_noshiftscale(struct GMT_CTRL * GMT,double * lon,double * lat,double x,double y)7106 void gmt_xy_to_geo_noshiftscale (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
7107 	/* Converts x/y to lon/lat using the current projection but applies no shift */
7108 
7109 	if (gmt_M_is_dnan (x) || gmt_M_is_dnan (y)) {(*lon) = (*lat) = GMT->session.d_NaN; return;}	/* Quick and safe way to ensure NaN-input results in NaNs */
7110 	(*GMT->current.proj.inv) (GMT, lon, lat, x, y);
7111 }
7112 
7113 /*! . */
gmt_xy_to_geo_noshift(struct GMT_CTRL * GMT,double * lon,double * lat,double x,double y)7114 void gmt_xy_to_geo_noshift (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
7115 	/* Converts x/y to lon/lat using the current projection but applies no shift */
7116 
7117 	if (gmt_M_is_dnan (x) || gmt_M_is_dnan (y)) {(*lon) = (*lat) = GMT->session.d_NaN; return;}	/* Quick and safe way to ensure NaN-input results in NaNs */
7118 	(*GMT->current.proj.inv) (GMT, lon, lat, x, y);
7119 }
7120 
7121 /*! . */
gmt_geoz_to_xy(struct GMT_CTRL * GMT,double x,double y,double z,double * x_out,double * y_out)7122 void gmt_geoz_to_xy (struct GMT_CTRL *GMT, double x, double y, double z, double *x_out, double *y_out) {
7123 	/* Map-projects xy first, the projects xyz onto xy plane */
7124 	double x0, y0;
7125 	gmt_geo_to_xy (GMT, x, y, &x0, &y0);
7126 	gmt_xyz_to_xy (GMT, x0, y0, gmt_z_to_zz (GMT, z), x_out, y_out);
7127 }
7128 
7129 /*! . */
gmt_xyz_to_xy(struct GMT_CTRL * GMT,double x,double y,double z,double * x_out,double * y_out)7130 void gmt_xyz_to_xy (struct GMT_CTRL *GMT, double x, double y, double z, double *x_out, double *y_out) {
7131 	/* projects xyz (inches) onto perspective xy plane (inches) */
7132 	*x_out = - x * GMT->current.proj.z_project.cos_az + y * GMT->current.proj.z_project.sin_az + GMT->current.proj.z_project.x_off;
7133 	*y_out = - (x * GMT->current.proj.z_project.sin_az + y * GMT->current.proj.z_project.cos_az) * GMT->current.proj.z_project.sin_el + z * GMT->current.proj.z_project.cos_el + GMT->current.proj.z_project.y_off;
7134 }
7135 
7136 
7137 /*! Setting w/e/s/n for a fully qualified UTM zone */
7138 
gmt_UTMzone_to_wesn(struct GMT_CTRL * GMT,unsigned int zone_x,char zone_y,int hemi,double wesn[])7139 bool gmt_UTMzone_to_wesn (struct GMT_CTRL *GMT, unsigned int zone_x, char zone_y, int hemi, double wesn[]) {
7140 	/* Given the full UTM zone specification, return w/e/s/n */
7141 
7142 	bool error = false;
7143 	gmt_M_unused(GMT);
7144 
7145 	wesn[XHI] = -180.0 + 6.0 * zone_x;	wesn[XLO] = wesn[XHI] - 6.0;
7146 
7147 	if (zone_y == 0) {	/* Latitude zone is not specified */
7148 		if (hemi == -1) {
7149 			wesn[YLO] = -80.0;	wesn[YHI] = 0.0;
7150 		}
7151 		else if (hemi == +1) {
7152 			wesn[YLO] = 0.0;	wesn[YHI] = 84.0;
7153 		}
7154 		else
7155 			error = true;
7156 		return (error);
7157 	}
7158 	else if (zone_y < 'A' || zone_y > 'Z')
7159 		error = true;
7160 	else if (zone_y <= 'B') {
7161 		wesn[YLO] = -90.0;	wesn[YHI] = -80.0;
7162 		wesn[XHI] = 180.0 * (zone_y - 'A');
7163 		wesn[XLO] = wesn[XHI] - 180.0;
7164 	}
7165 	else if (zone_y <= 'I') {	/* I will behave as J */
7166 		wesn[YLO] = -80.0 + 8.0 * (zone_y - 'C');	wesn[YHI] = wesn[YLO] + 8.0;
7167 	}
7168 	else if (zone_y <= 'O') {	/* O will behave as P */
7169 		wesn[YLO] = -80.0 + 8.0 * (zone_y - 'D');	wesn[YHI] = wesn[YLO] + 8.0;
7170 	}
7171 	else if (zone_y <= 'W') {
7172 		wesn[YLO] = -80.0 + 8.0 * (zone_y - 'E');	wesn[YHI] = wesn[YLO] + 8.0;
7173 		if (zone_y == 'V' && zone_x == 31) wesn[XHI] = 3.0;
7174 		if (zone_y == 'V' && zone_x == 32) wesn[XLO] = 3.0;
7175 	}
7176 	else if (zone_y == 'X') {
7177 		wesn[YLO] = 72.0;	wesn[YHI] = 84.0;
7178 		if (zone_x == 31) wesn[XHI] = 9.0;
7179 		if (zone_x == 33) {wesn[XLO] = 9.0; wesn[XHI] = 21.0;}
7180 		if (zone_x == 35) {wesn[XLO] = 21.0; wesn[XHI] = 33.0;}
7181 		if (zone_x == 37) wesn[XLO] = 33.0;
7182 		if (zone_x == 32 || zone_x == 34 || zone_x == 36) error = true;
7183 	}
7184 	else {	/* Y or Z */
7185 		wesn[YLO] = 84.0;	wesn[YHI] = 90.0;
7186 		wesn[XHI] = 180.0 * (zone_y - 'Y');
7187 		wesn[XLO] = wesn[XHI] - 180.0;
7188 	}
7189 
7190 	return (error);
7191 }
7192 
7193 /*! . */
gmtlib_genper_reset(struct GMT_CTRL * GMT,bool reset)7194 bool gmtlib_genper_reset (struct GMT_CTRL *GMT, bool reset) {
7195 	/* Switch between the windowed functions for lines etc and the rect ones for annotations.
7196 	 * Because of issue # 667 it became clear that we had problems with gridlines when the
7197 	 * projection became windowed, i.e., view is a clipped circle with both straight and
7198 	 * curved boundaries.  I added several new functions to deal with overlaps, crossings,
7199 	 * left and right boundary x-value, etc (all called gmtproj_genper_*) but they yield way too
7200 	 * many annotations (probably due to some confusion) so until that might be looked into I
7201 	 * have kept the older, simpler functions and switch from those to the new ones when we
7202 	 * do gridlines.  P. Wessel, Feb 10, 2015.
7203 	 */
7204 	if (GMT->current.proj.projection_GMT == GMT_GENPER && GMT->current.proj.windowed) {
7205 		if (reset) {
7206 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Revert to old genper crossing/overlap functions\n");
7207 			GMT->current.map.crossing = &gmtmap_rect_crossing;
7208 			GMT->current.map.overlap = &gmtmap_rect_overlap;
7209 		}
7210 		else {
7211 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Switch to new genper crossing/overlap functions\n");
7212 			GMT->current.map.crossing = &gmtmap_genper_crossing;
7213 			GMT->current.map.overlap = &gmtmap_genperw_overlap;
7214 		}
7215 		return true;
7216 	}
7217 	return false;
7218 }
7219 
7220 /*! . */
gmt_map_outside(struct GMT_CTRL * GMT,double lon,double lat)7221 bool gmt_map_outside (struct GMT_CTRL *GMT, double lon, double lat) {
7222 	/* Save current status in previous status and update current in/out status */
7223 	GMT->current.map.prev_x_status = GMT->current.map.this_x_status;
7224 	GMT->current.map.prev_y_status = GMT->current.map.this_y_status;
7225 	if (GMT->current.map.outside == NULL) {
7226 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_map_outside: FATAL ERROR - the pointer to the projection function is NULL.\n");
7227 		return GMT_NOTSET;
7228 	}
7229 	return ((*GMT->current.map.outside) (GMT, lon, lat));
7230 }
7231 
7232 /*! . */
gmt_half_map_width(struct GMT_CTRL * GMT,double y)7233 double gmt_half_map_width (struct GMT_CTRL *GMT, double y) {
7234 	/* Returns 1/2-width of map in inches given y value */
7235 	double half_width;
7236 
7237 	switch (GMT->current.proj.projection_GMT) {
7238 
7239 		case GMT_STEREO:	/* Must compute width of circular map based on y value (ASSUMES FULL CIRCLE!!!) */
7240 		case GMT_LAMB_AZ_EQ:
7241 		case GMT_GNOMONIC:
7242 		case GMT_AZ_EQDIST:
7243 		case GMT_VANGRINTEN:
7244 			if (!GMT->common.R.oblique && GMT->current.map.is_world) {
7245 				y -= GMT->current.proj.r;
7246 				half_width = d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y);
7247 			}
7248 			else
7249 				half_width = GMT->current.map.half_width;
7250 			break;
7251 
7252 		case GMT_ORTHO:
7253 		case GMT_GENPER:
7254 			if (!GMT->common.R.oblique && gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) {
7255 				y -= GMT->current.proj.r;
7256 				half_width = d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y);
7257 			}
7258 			else
7259 				half_width = GMT->current.map.half_width;
7260 			break;
7261 		case GMT_MOLLWEIDE:		/* Must compute width of Mollweide map based on y value */
7262 		case GMT_HAMMER:
7263 		case GMT_WINKEL:
7264 		case GMT_SINUSOIDAL:
7265 		case GMT_ROBINSON:
7266 		case GMT_ECKERT4:
7267 		case GMT_ECKERT6:
7268 			if (!GMT->common.R.oblique && GMT->current.map.is_world)
7269 				half_width = gmtlib_right_boundary (GMT, y) - GMT->current.map.half_width;
7270 			else
7271 				half_width = GMT->current.map.half_width;
7272 			break;
7273 
7274 		default:	/* Rectangular maps are easy */
7275 			half_width = GMT->current.map.half_width;
7276 			break;
7277 	}
7278 	return (half_width);
7279 }
7280 
7281 /*! . */
gmt_map_truncate(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t start,int side)7282 uint64_t gmt_map_truncate (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t start, int side) {
7283 	/* Truncates a wrapping polygon against left or right edge.
7284 	   (bottom or top edge when projection is TM)
7285 	   x, y : arrays of plot coordinates
7286 	   n    : length of the arrays
7287 	   start: first point of array to consider
7288 	   side : -1 = left (bottom); +1 = right (top)
7289 	*/
7290 
7291 	if (GMT->current.proj.projection_GMT == GMT_TM)
7292 		return (gmtmap_truncate_tm (GMT, x, y, n, start, side));
7293 	else
7294 		return (gmtmap_truncate_x (GMT, x, y, n, start, side));
7295 }
7296 
7297 /* GMT generic distance calculations between a pair of points in 2-D */
7298 
7299 /*! . */
gmtlib_distance_type(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE,unsigned int id)7300 double gmtlib_distance_type (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE, unsigned int id) {
7301 	/* Generic function available to programs for contour/label distance calculations */
7302 	return (GMT->current.map.dist[id].scale * GMT->current.map.dist[id].func (GMT, lonS, latS, lonE, latE));
7303 }
7304 
7305 /*! . */
gmt_distance(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)7306 double gmt_distance (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
7307 	/* Generic function available to programs */
7308 	return (gmtlib_distance_type (GMT, lonS, latS, lonE, latE, 0));
7309 }
7310 
7311 /*! . */
gmt_near_a_point(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,double dist)7312 bool gmt_near_a_point (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, double dist) {
7313 	/* Compute distance to nearest point in T from (lon, lat) */
7314 	return (GMT->current.map.near_point_func (GMT, lon, lat, T, dist));
7315 }
7316 
7317 /*! . */
gmt_near_lines(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)7318 bool gmt_near_lines (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
7319 	/* Compute distance to nearest line in T from (lon,lat) */
7320 	return (GMT->current.map.near_lines_func (GMT, lon, lat, T, return_mindist, dist_min, x_near, y_near));
7321 }
7322 
7323 /*! . */
gmt_near_a_line(struct GMT_CTRL * GMT,double lon,double lat,uint64_t seg,struct GMT_DATASEGMENT * S,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)7324 bool gmt_near_a_line (struct GMT_CTRL *GMT, double lon, double lat, uint64_t seg, struct GMT_DATASEGMENT *S, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
7325 	/* Compute distance to the line S from (lon,lat) */
7326 	return (GMT->current.map.near_a_line_func (GMT, lon, lat, seg, S, return_mindist, dist_min, x_near, y_near));
7327 }
7328 
7329 /* Specific functions that are accessed via pointer only */
7330 
7331 /*! . */
gmtlib_cartesian_dist(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)7332 double gmtlib_cartesian_dist (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
7333 	/* Calculates the good-old straight line distance in users units */
7334 	gmt_M_unused(GMT);
7335 	return (hypot ( (x1 - x0), (y1 - y0)));
7336 }
7337 
7338 /*! . */
gmtlib_cartesian_dist_proj(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)7339 double gmtlib_cartesian_dist_proj (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
7340 	/* Calculates the good-old straight line distance after first projecting the data */
7341 	double x0, y0, x1, y1;
7342 	gmt_geo_to_xy (GMT, lon1, lat1, &x0, &y0);
7343 	gmt_geo_to_xy (GMT, lon2, lat2, &x1, &y1);
7344 	return (hypot ( (x1 - x0), (y1 - y0)));
7345 }
7346 
7347 /*! . */
gmtlib_great_circle_dist_degree(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)7348 double gmtlib_great_circle_dist_degree (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
7349 	/* Great circle distance on a sphere in degrees */
7350 
7351 	double sin_half_squared = gmtmap_haversine (GMT, lon1, lat1, lon2, lat2);
7352 	return (2.0 * d_asind (d_sqrt (sin_half_squared)));
7353 }
7354 
7355 /*! . */
gmt_great_circle_dist_meter(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)7356 double gmt_great_circle_dist_meter (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
7357 	/* Calculates the great circle distance in meter */
7358 	return (gmtlib_great_circle_dist_degree (GMT, lon1, lat1, lon2, lat2) * GMT->current.proj.DIST_M_PR_DEG);
7359 }
7360 
7361 /* Functions dealing with distance between points */
7362 
7363 /*! . */
gmt_mindist_to_point(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,uint64_t * id)7364 double gmt_mindist_to_point (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, uint64_t *id) {
7365 	uint64_t row, seg;
7366 	double d, d_min;
7367 
7368 	d_min = DBL_MAX;
7369 	for (seg = 0; seg < T->n_segments; seg++) {
7370 		for (row = 0; row < T->segment[seg]->n_rows; row++) {
7371 			d = gmt_distance (GMT, lon, lat, T->segment[seg]->data[GMT_X][row], T->segment[seg]->data[GMT_Y][row]);
7372 			if (d < d_min) {	/* Update the shortest distance and the point responsible */
7373 				d_min = d;	id[0] = seg;	id[1] = row;
7374 			}
7375 		}
7376 	}
7377 	return (d_min);
7378 }
7379 
7380 #if 0
7381 int gmtlib_small_circle_intersection (struct GMT_CTRL *GMT, double Ax, double Ay, double Ar, double Bx, double By, double Br, double Xx[2], double Yy[2]) {
7382 	/* Let (Ax,Ay) and (Bx,By) be the poles of two small circles, each with radius Ar and Br, respectively, in degrees.
7383 	 * Pass out the 0-2 intersections via Xx, Yx and return the number of intersections 0-2.
7384 	 * Modified from http://gis.stackexchange.com/questions/48937/how-to-calculate-the-intersection-of-2-circles
7385 	 */
7386 	unsigned int k;
7387 	double A[3], B[3], X[3], N[3], P[3], t, a, b, L_X2, L_N2, cos_AB, AB, cos_Ar, cos_Br, cos_AB2;
7388 
7389 	gmt_geo_to_cart (GMT, Ay, Ax, A, true);	/* Get pole vector A */
7390 	gmt_geo_to_cart (GMT, By, Bx, B, true);	/* Get pole vector B */
7391 	cos_AB = gmt_dot3v (GMT, A, B);		/* Cos of spherical distance between A and B */
7392 	AB = d_acosd (cos_AB);			/* Distance between the two poles in degrees */
7393 	if ((Ar + Br) < AB) return 0;		/* No intersection possible */
7394 	if (doubleAlmostEqual (Ar + Br, AB)) {	/* Tangent point lines along great circle form A to B */
7395 		t = Ar / AB;	/* Fractional distance of X from A */
7396 		for (k = 0; k < 3; k++) X[k] = A[k] * (1.0 - t) + B[k] * t;
7397 		gmt_normalize3v (GMT, X);	/* Make sure X has unit length */
7398 		gmt_cart_to_geo (GMT, &Yy[0], &Xx[0], X, true);	/* Recover lon,lat of tangent point */
7399 		return 1;
7400 	}
7401 	/* Here we have two intersections */
7402 	cos_AB2 = cos_AB * cos_AB;
7403 	cos_Ar = cosd (Ar);	cos_Br = cosd (Br);
7404 	a = (cos_Ar - cos_Br * cos_AB) / (1.0 - cos_AB2);
7405 	b = (cos_Br - cos_Ar * cos_AB) / (1.0 - cos_AB2);
7406 	for (k = 0; k < 3; k++) X[k] = a * A[k] + b * B[k];
7407 	L_X2 = gmt_dot3v (GMT, X, X);	/* Length^2 of X */
7408 	gmt_cross3v (GMT, A, B, N);	/* Get pole position of plane through A and B (and origin O) */
7409 	L_N2 = gmt_dot3v (GMT, N, N);	/* Length^2 of N */
7410 	t = sqrt ((1.0 - L_X2) / L_N2);
7411 	for (k = 0; k < 3; k++) P[k] = X[k] + t * N[k];
7412 	gmt_normalize3v (GMT, P);	/* Make sure P has unit length */
7413 	gmt_cart_to_geo (GMT, &Yy[0], &Xx[0], P, true);		/* Recover lon,lat of 1st intersection point */
7414 	for (k = 0; k < 3; k++) P[k] = X[k] - t * N[k];
7415 	gmt_normalize3v (GMT, P);	/* Make sure P has unit length */
7416 	gmt_cart_to_geo (GMT, &Yy[1], &Xx[1], P, true);		/* Recover lon,lat of 2nd intersection point */
7417 	return 2;
7418 }
7419 #endif
7420 
7421 /*! . */
gmtlib_great_circle_intersection(struct GMT_CTRL * GMT,double A[],double B[],double C[],double X[],double * CX_dist)7422 int gmtlib_great_circle_intersection (struct GMT_CTRL *GMT, double A[], double B[], double C[], double X[], double *CX_dist) {
7423 	/* A, B, C are 3-D Cartesian unit vectors, i.e., points on the sphere.
7424 	 * Let points A and B define a great circle, and consider a
7425 	 * third point C.  A second great circle goes through C and
7426 	 * is orthogonal to the first great circle.  Their intersection
7427 	 * X is the point on (A,B) closest to C.  We must test if X is
7428 	 * between A,B or outside.
7429 	 */
7430 	unsigned int i;
7431 	double P[3], E[3], M[3], Xneg[3], cos_AB, cos_MX1, cos_MX2, cos_test;
7432 
7433 	gmt_cross3v (GMT, A, B, P);			/* Get pole position of plane through A and B (and origin O) */
7434 	gmt_normalize3v (GMT, P);			/* Make sure P has unit length */
7435 	gmt_cross3v (GMT, C, P, E);			/* Get pole E to plane through C (and origin) but normal to A,B (hence going through P) */
7436 	gmt_normalize3v (GMT, E);			/* Make sure E has unit length */
7437 	gmt_cross3v (GMT, P, E, X);			/* Intersection between the two planes is oriented line*/
7438 	gmt_normalize3v (GMT, X);			/* Make sure X has unit length */
7439 	/* The X we want could be +x or -X; must determine which might be closest to A-B midpoint M */
7440 	for (i = 0; i < 3; i++) {
7441 		M[i] = A[i] + B[i];
7442 		Xneg[i] = -X[i];
7443 	}
7444 	gmt_normalize3v (GMT, M);			/* Make sure M has unit length */
7445 	/* Must first check if X is along the (A,B) segment and not on its extension */
7446 
7447 	cos_MX1 = gmt_dot3v (GMT, M, X);		/* Cos of spherical distance between M and +X */
7448 	cos_MX2 = gmt_dot3v (GMT, M, Xneg);	/* Cos of spherical distance between M and -X */
7449 	if (cos_MX2 > cos_MX1) gmt_M_memcpy (X, Xneg, 3, double);		/* -X is closest to A-B midpoint */
7450 	cos_AB = fabs (gmt_dot3v (GMT, A, B));	/* Cos of spherical distance between A,B */
7451 	cos_test = fabs (gmt_dot3v (GMT, A, X));	/* Cos of spherical distance between A and X */
7452 	if (cos_test < cos_AB) return 1;	/* X must be on the A-B extension if its distance to A exceeds the A-B length */
7453 	cos_test = fabs (gmt_dot3v (GMT, B, X));	/* Cos of spherical distance between B and X */
7454 	if (cos_test < cos_AB) return 1;	/* X must be on the A-B extension if its distance to B exceeds the A-B length */
7455 
7456 	/* X is between A and B.  Now calculate distance between C and X */
7457 
7458 	*CX_dist = gmt_dot3v (GMT, C, X);		/* Cos of spherical distance between C and X */
7459 	return (0);				/* Return zero if intersection is between A and B */
7460 }
7461 
7462 /*! . */
gmtlib_split_line(struct GMT_CTRL * GMT,double ** xx,double ** yy,uint64_t * nn,bool add_crossings)7463 uint64_t *gmtlib_split_line (struct GMT_CTRL *GMT, double **xx, double **yy, uint64_t *nn, bool add_crossings) {
7464 	/* Accepts x/y array for a line in projected inches and looks for
7465 	 * map jumps.  If found it will insert the boundary crossing points and
7466 	 * build a split integer array with the nodes of the first point
7467 	 * for each segment.  The number of segments is returned.  If
7468 	 * no jumps are found then NULL is returned.  This function is needed
7469 	 * so that the new PS contouring machinery only sees lines that do no
7470 	 * jump across the map.
7471 	 * add_crossings is true if we need to find the crossings; false means
7472 	 * they are already part of the line. */
7473 
7474 	uint64_t i, j, jc, k, n, n_seg, *split = NULL, *pos = NULL;
7475 	size_t n_alloc = 0;
7476  	int l_or_r;
7477  	char *way = NULL;
7478 	double *x = NULL, *y = NULL, *xin = NULL, *yin = NULL, xc[2], yc[2];
7479 
7480 	/* First quick scan to see how many jumps there are */
7481 
7482 	xin = *xx;	yin = *yy;
7483 	gmt_set_meminc (GMT, GMT_SMALL_CHUNK);
7484 	for (n_seg = 0, i = 1; i < *nn; i++) {
7485 		if ((l_or_r = gmtmap_jump_xy (GMT, xin[i], yin[i], xin[i-1], yin[i-1]))) {
7486 			if (n_seg == n_alloc) {
7487 				pos = gmt_M_malloc (GMT, pos, n_seg, &n_alloc, uint64_t);
7488 				n_alloc = n_seg;
7489 				way = gmt_M_malloc (GMT, way, n_seg, &n_alloc, char);
7490 			}
7491 			pos[n_seg] = i;		/* 2nd of the two points that generate the jump */
7492 			way[n_seg] = (char)l_or_r;	/* Which way we jump : +1 is right to left, -1 is left to right, +2 is top to bottom, -2 is bottom to top */
7493 			n_seg++;
7494 		}
7495 	}
7496 	gmt_reset_meminc (GMT);
7497 
7498 	if (n_seg == 0) return (NULL);	/* No jumps, just return NULL */
7499 
7500 	/* Here we have one or more jumps so we need to split the line */
7501 
7502 	n = *nn;				/* Original line count */
7503 	if (add_crossings) n += 2 * n_seg;	/* Must add 2 crossing points per jump */
7504 	n_alloc = 0;
7505 	gmt_M_malloc2 (GMT, x, y, n, &n_alloc, double);
7506 	split = gmt_M_memory (GMT, NULL, n_seg+2, uint64_t);
7507 	split[0] = n_seg;	/* Number of segments we will need */
7508 
7509 	x[0] = xin[0];	y[0] = yin[0];
7510 	for (i = j = 1, k = 0; i < *nn; i++, j++) {
7511 		if (k < n_seg && i == pos[k]) {	/* At jump point */
7512 			if (add_crossings) {	/* Find and insert the crossings */
7513 				if (way[k] == -2 || way[k] == +2) {	/* Jump in y for TM */
7514 					gmtmap_get_crossings_y (GMT, xc, yc, xin[i], yin[i], xin[i-1], yin[i-1]);
7515 					if (way[k] == -2) {	/* Add top border point first */
7516 						gmt_M_double_swap (xc[0], xc[1]);
7517 						gmt_M_double_swap (yc[0], yc[1]);
7518 					}
7519 				}
7520 				else {	/* Jump in x */
7521 					gmtmap_get_crossings_x (GMT, xc, yc, xin[i], yin[i], xin[i-1], yin[i-1]);
7522 					if (way[k] == 1) {	/* Add right border point first */
7523 						gmt_M_double_swap (xc[0], xc[1]);
7524 						gmt_M_double_swap (yc[0], yc[1]);
7525 					}
7526 				}
7527 				x[j] = xc[0];	y[j++] = yc[0];	/* End of one segment */
7528 				jc = j;		/* Node of first point in next segment */
7529 				x[j] = xc[1];	y[j++] = yc[1];	/* Start of another */
7530 			}
7531 			else
7532 				jc = j;
7533 			split[++k] = jc;		/* Node of first point in new segment */
7534 		}
7535 		/* Then copy the regular points */
7536 		x[j] = xin[i];	y[j] = yin[i];
7537 	}
7538 	split[++k] = j;		/* End of last segment */
7539 
7540 	/* Time to return the pointers to new data */
7541 
7542 	gmt_M_free (GMT, pos);
7543 	gmt_M_free (GMT, way);
7544 	gmt_M_free (GMT, xin);
7545 	gmt_M_free (GMT, yin);
7546 	*xx = x;
7547 	*yy = y;
7548 	*nn = j;
7549 
7550 	return (split);
7551 }
7552 
7553 /*  Routines to add pieces of parallels or meridians */
7554 
7555 /*! . */
gmt_graticule_path(struct GMT_CTRL * GMT,double ** x,double ** y,int dir,bool check,double w,double e,double s,double n)7556 uint64_t gmt_graticule_path (struct GMT_CTRL *GMT, double **x, double **y, int dir, bool check, double w, double e, double s, double n) {
7557 	/* Returns the path of a graticule (box of meridians and parallels).
7558 	 * dir determines if we go counter-clockwise or clockwise.
7559 	 * check, if true, forces a straddle check at the end for geographic data.
7560 	*/
7561 	size_t n_alloc = 0;
7562 	uint64_t np = 0;
7563 	double *xx = NULL, *yy = NULL;
7564 	double px0, px1, px2, px3;
7565 
7566 	if (dir == 1) {	/* Forward sense */
7567 		px0 = px3 = w;	px1 = px2 = e;
7568 	}
7569 	else {	/* Reverse sense */
7570 		px0 = px3 = e;	px1 = px2 = w;
7571 	}
7572 
7573 	/* Close graticule from point 0 through point 4 */
7574 
7575 	if (gmt_M_is_rect_graticule(GMT)) {	/* Simple rectangle in this projection */
7576 		gmt_M_malloc2 (GMT, xx, yy, 5U, NULL, double);
7577 		xx[0] = xx[4] = px0;	xx[1] = px1;	xx[2] = px2;	xx[3] = px3;
7578 		yy[0] = yy[1] = yy[4] = s;	yy[2] = yy[3] = n;
7579 		np = 5U;
7580 	}
7581 	else {	/* Must assemble path from meridians and parallel pieces */
7582 		double *xtmp = NULL, *ytmp = NULL;
7583 		size_t add;
7584 		uint64_t k;
7585 
7586 		/* SOUTH BORDER */
7587 
7588 		if (gmt_M_is_geographic (GMT, GMT_IN) && s == -90.0 && gmt_M_pole_is_point(GMT)) {	/* No path, just a point */
7589 			gmt_M_malloc2 (GMT, xx, yy, 1U, &n_alloc, double);
7590 			xx[0] = px1;	yy[0] = -90.0;
7591 		}
7592 		else
7593 			n_alloc = gmtlib_latpath (GMT, s, px0, px1, &xx, &yy);	/* South */
7594 		np = n_alloc;
7595 
7596 		/* EAST (OR WEST) BORDER */
7597 
7598 		add = gmtlib_lonpath (GMT, px1, s, n, &xtmp, &ytmp);	/* east (or west if dir == -1) */
7599 		k = n_alloc + add;
7600 		gmt_M_malloc2 (GMT, xx, yy, k, &n_alloc, double);
7601 		gmt_M_memcpy (&xx[np], xtmp, add, double);
7602 		gmt_M_memcpy (&yy[np], ytmp, add, double);
7603 		np += add;
7604 		gmt_M_free (GMT, xtmp);	gmt_M_free (GMT, ytmp);
7605 
7606 		/* NORTH BORDER */
7607 
7608 		if (gmt_M_is_geographic (GMT, GMT_IN) && n == 90.0 && gmt_M_pole_is_point(GMT)) {	/* No path, just a point */
7609 			add = 0;
7610 			gmt_M_malloc2 (GMT, xtmp, ytmp, 1U, &add, double);
7611 			xtmp[0] = px3;	ytmp[0] = +90.0;
7612 		}
7613 		else
7614 			add = gmtlib_latpath (GMT, n, px2, px3, &xtmp, &ytmp);	/* North */
7615 
7616 		k = n_alloc + add;
7617 		gmt_M_malloc2 (GMT, xx, yy, k, &n_alloc, double);
7618 		gmt_M_memcpy (&xx[np], xtmp, add, double);
7619 		gmt_M_memcpy (&yy[np], ytmp, add, double);
7620 		np += add;
7621 		gmt_M_free (GMT, xtmp);	gmt_M_free (GMT, ytmp);
7622 
7623 		/* WEST (OR EAST) BORDER */
7624 
7625 		add = gmtlib_lonpath (GMT, px3, n, s, &xtmp, &ytmp);	/* west */
7626 		k = n_alloc + add;
7627 		gmt_M_malloc2 (GMT, xx, yy, k, &n_alloc, double);
7628 		gmt_M_memcpy (&xx[np], xtmp, add, double);
7629 		gmt_M_memcpy (&yy[np], ytmp, add, double);
7630 		np += add;
7631 		n_alloc = np;
7632 		gmt_M_free (GMT, xtmp);	gmt_M_free (GMT, ytmp);
7633 		gmt_M_malloc2 (GMT, xx, yy, 0, &n_alloc, double);
7634 	}
7635 
7636 	if (check && gmt_M_x_is_lon (GMT, GMT_IN)) {
7637 		bool straddle;
7638 		uint64_t i;
7639 		straddle = (GMT->common.R.wesn[XLO] < 0.0 && GMT->common.R.wesn[XHI] > 0.0);
7640 		for (i = 0; straddle && i < np; i++) {
7641 			while (xx[i] < 0.0) xx[i] += 360.0;
7642 			if (straddle && xx[i] > 180.0) xx[i] -= 360.0;
7643 		}
7644 	}
7645 
7646 	*x = xx;
7647 	*y = yy;
7648 	return (np);
7649 }
7650 
7651 /*! . */
gmtlib_map_path(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double ** x,double ** y)7652 uint64_t gmtlib_map_path (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double **x, double **y) {
7653 	if (doubleAlmostEqualZero (lat1, lat2))
7654 		return (gmtlib_latpath (GMT, lat1, lon1, lon2, x, y));
7655 	else
7656 		return (gmtlib_lonpath (GMT, lon1, lat1, lat2, x, y));
7657 }
7658 
7659 /*! . */
gmtlib_lonpath(struct GMT_CTRL * GMT,double lon,double lat1,double lat2,double ** x,double ** y)7660 uint64_t gmtlib_lonpath (struct GMT_CTRL *GMT, double lon, double lat1, double lat2, double **x, double **y) {
7661 	size_t n_alloc = 0;
7662 	uint64_t n, k;
7663 	int n_try, pos;
7664 	bool keep_trying;
7665 	double dlat, dlat0, *tlon = NULL, *tlat = NULL, x0, x1, y0, y1, d, min_gap;
7666 
7667 	if (GMT->current.map.meridian_straight == 2) {	/* Special non-sampling for gmtselect/grdlandmask */
7668 		gmt_M_malloc2 (GMT, tlon, tlat, 2U, NULL, double);
7669 		tlon[0] = tlon[1] = lon;
7670 		tlat[0] = lat1;	tlat[1] = lat2;
7671 		*x = tlon;
7672 		*y = tlat;
7673 		return (2ULL);
7674 	}
7675 
7676 	if (GMT->current.map.meridian_straight) {	/* Easy, just a straight line connect via quarter-points */
7677 		gmt_M_malloc2 (GMT, tlon, tlat, 5, &n_alloc, double);
7678 		tlon[0] = tlon[1] = tlon[2] = tlon[3] = tlon[4] = lon;
7679 		dlat = lat2 - lat1;
7680 		tlat[0] = lat1;	tlat[1] = lat1 + 0.25 * dlat;	tlat[2] = lat1 + 0.5 * dlat;
7681 		tlat[3] = lat1 + 0.75 * dlat;	tlat[4] = lat2;
7682 		*x = tlon;
7683 		*y = tlat;
7684 		return (n = n_alloc);
7685 	}
7686 
7687 	/* Must do general case */
7688 	n = 0;
7689 	min_gap = 0.1 * GMT->current.setting.map_line_step;
7690 	if ((n_alloc = lrint (ceil (fabs (lat2 - lat1) / GMT->current.map.dlat))) == 0) return (0);
7691 
7692 	n_alloc++;	/* So n_alloc is at least 2 */
7693 	dlat0 = (lat2 - lat1) / n_alloc;
7694 	pos = (dlat0 > 0.0);
7695 
7696 	k = n_alloc;	n_alloc = 0;
7697 	gmt_M_malloc2 (GMT, tlon, tlat, k, &n_alloc, double);
7698 
7699 	tlon[0] = lon;
7700 	tlat[0] = lat1;
7701 	gmt_geo_to_xy (GMT, tlon[0], tlat[0], &x0, &y0);
7702 	while ((pos && (tlat[n] < lat2)) || (!pos && (tlat[n] > lat2))) {
7703 		n++;
7704 		if (n == n_alloc-1) {
7705 			n_alloc += GMT_SMALL_CHUNK;
7706 			tlon = gmt_M_memory (GMT, tlon, n_alloc, double);
7707 			tlat = gmt_M_memory (GMT, tlat, n_alloc, double);
7708 		}
7709 		n_try = 0;
7710 		keep_trying = true;
7711 		dlat = dlat0;
7712 		tlon[n] = lon;
7713 		do {
7714 			n_try++;
7715 			tlat[n] = tlat[n-1] + dlat;
7716 			if (gmt_M_y_is_lat (GMT, GMT_IN) && fabs (tlat[n]) > 90.0) tlat[n] = copysign (90.0, tlat[n]);
7717 			gmt_geo_to_xy (GMT, tlon[n], tlat[n], &x1, &y1);
7718 			if ((*GMT->current.map.jump) (GMT, x0, y0, x1, y1) || (y0 < GMT->current.proj.rect[YLO] || y0 > GMT->current.proj.rect[YHI]))
7719 				keep_trying = false;
7720 			else {
7721 				d = hypot (x1 - x0, y1 - y0);
7722 				if (d > GMT->current.setting.map_line_step)
7723 					dlat *= 0.5;
7724 				else if (d < min_gap)
7725 					dlat *= 2.0;
7726 				else
7727 					keep_trying = false;
7728 			}
7729 		} while (keep_trying && n_try < 10);
7730 		x0 = x1;	y0 = y1;
7731 	}
7732 	tlon[n] = lon;
7733 	tlat[n] = lat2;
7734 	n++;
7735 
7736 	if (n != n_alloc) {
7737 		tlon = gmt_M_memory (GMT, tlon, n, double);
7738 		tlat = gmt_M_memory (GMT, tlat, n, double);
7739 	}
7740 
7741 	*x = tlon;	*y = tlat;
7742 	return (n);
7743 }
7744 
7745 /*! . */
gmtlib_latpath(struct GMT_CTRL * GMT,double lat,double lon1,double lon2,double ** x,double ** y)7746 uint64_t gmtlib_latpath (struct GMT_CTRL *GMT, double lat, double lon1, double lon2, double **x, double **y) {
7747 	size_t n_alloc = 0;
7748 	uint64_t k, n;
7749 	int n_try, pos;
7750 	bool keep_trying;
7751 	double dlon, dlon0, *tlon = NULL, *tlat = NULL, x0, x1, y0, y1, d, min_gap;
7752 
7753 	if (GMT->current.map.parallel_straight == 2) {	/* Special non-sampling for gmtselect/grdlandmask */
7754 		gmt_M_malloc2 (GMT, tlon, tlat, 2U, NULL, double);
7755 		tlat[0] = tlat[1] = lat;
7756 		tlon[0] = lon1;	tlon[1] = lon2;
7757 		*x = tlon;	*y = tlat;
7758 		return (2ULL);
7759 	}
7760 	if (GMT->current.map.parallel_straight) {	/* Easy, just a straight line connection via quarter points */
7761 		gmt_M_malloc2 (GMT, tlon, tlat, 5U, NULL, double);
7762 		tlat[0] = tlat[1] = tlat[2] = tlat[3] = tlat[4] = lat;
7763 		dlon = lon2 - lon1;
7764 		tlon[0] = lon1;	tlon[1] = lon1 + 0.25 * dlon;	tlon[2] = lon1 + 0.5 * dlon;
7765 		tlon[3] = lon1 + 0.75 * dlon;	tlon[4] = lon2;
7766 		*x = tlon;	*y = tlat;
7767 		return (5ULL);
7768 	}
7769 	/* Here we try to walk along lat for small increment in longitude to make sure our steps are smaller than the line_step */
7770 	min_gap = 0.1 * GMT->current.setting.map_line_step;
7771 	if ((n_alloc = lrint (ceil (fabs (lon2 - lon1) / GMT->current.map.dlon))) == 0) return (0);	/* Initial guess to path length */
7772 
7773 	n_alloc++;	/* n_alloc is at least 2 */
7774 	dlon0 = (lon2 - lon1) / n_alloc;
7775 	pos = (dlon0 > 0.0);
7776 
7777 	k = n_alloc;	n_alloc = 0;
7778 	gmt_M_malloc2 (GMT, tlon, tlat, k, &n_alloc, double);
7779 	tlon[0] = lon1;	tlat[0] = lat;
7780 	gmt_geo_to_xy (GMT, tlon[0], tlat[0], &x0, &y0);
7781 	n = 0;
7782 	while ((pos && (tlon[n] < lon2)) || (!pos && (tlon[n] > lon2))) {
7783 		n++;
7784 		if (n == n_alloc) gmt_M_malloc2 (GMT, tlon, tlat, n, &n_alloc, double);
7785 		n_try = 0;
7786 		keep_trying = true;
7787 		dlon = dlon0;
7788 		tlat[n] = lat;
7789 		do {
7790 			n_try++;
7791 			tlon[n] = tlon[n-1] + dlon;
7792 			gmt_geo_to_xy (GMT, tlon[n], tlat[n], &x1, &y1);
7793 			if ((*GMT->current.map.jump) (GMT, x0, y0, x1, y1) || (y0 < GMT->current.proj.rect[YLO] || y0 > GMT->current.proj.rect[YHI]))
7794 				keep_trying = false;
7795 			else {
7796 				d = hypot (x1 - x0, y1 - y0);
7797 				if (d > GMT->current.setting.map_line_step)
7798 					dlon *= 0.5;
7799 				else if (d < min_gap)
7800 					dlon *= 2.0;
7801 				else
7802 					keep_trying = false;
7803 			}
7804 		} while (keep_trying && n_try < 10);
7805 		x0 = x1;	y0 = y1;
7806 	}
7807 	tlon[n] = lon2;
7808 	tlat[n] = lat;
7809 	n++;
7810 	n_alloc = n;
7811 	gmt_M_malloc2 (GMT, tlon, tlat, 0, &n_alloc, double);
7812 
7813 	*x = tlon;	*y = tlat;
7814 	return (n);
7815 }
7816 
gmtmap_accept_the_jump(struct GMT_CTRL * GMT,double lon1,double lon0,double xx[],bool cartesian)7817 GMT_LOCAL bool gmtmap_accept_the_jump (struct GMT_CTRL *GMT, double lon1, double lon0, double xx[], bool cartesian) {
7818 	/* Carefully examine if we really want to draw line from left to right boundary.
7819 	 * We want to avoid E-W wrapping lines for near-global areas where points simply move
7820 	 * from being > 180 degrees from the map area to < -180 even though the points do not
7821 	 * really reflect motion across the area */
7822 	double dlon0, dlon1, dlon;
7823 	int xm, ym;
7824 	gmt_M_unused(xx);
7825 	if (cartesian) return true;	/* No wrap issues if Cartesian x,y */
7826 	xm = GMT->current.map.this_x_status - GMT->current.map.prev_x_status;
7827 	ym = GMT->current.map.this_y_status - GMT->current.map.prev_y_status;
7828 	gmt_M_set_delta_lon (lon0, GMT->current.proj.central_meridian, dlon0);
7829 	gmt_M_set_delta_lon (lon1, GMT->current.proj.central_meridian, dlon1);
7830 	dlon = dlon1 - dlon0;
7831 	/* When the line is so far away from the regino that it crosses 180 from the central meridian
7832 	 * we get false jumps.  However, we find those by looking at the two dlons and that the change
7833 	 * in x status is 4 (going from left of region to right of region of the opposite).  We then
7834 	 * reject the jump, otherwise we accept it. */
7835 	//fprintf (stderr, "dlon0 = %g dlon1 = %g dlon = %g xx0 = %g xx1 = %g xm = %d ym = %d\n", dlon0, dlon1, dlon, xx[0], xx[1], xm, ym);
7836 	if (!(fabs (dlon) > 180.0 && abs (xm) == 4 && ym == 0)) return true;	/* Very unlikely to be a real wrap/jump */
7837 	return false;
7838 }
7839 
7840 /*! . */
gmt_cart_to_xy_line(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n)7841 uint64_t gmt_cart_to_xy_line (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n) {
7842 	/* Cartesian data has no wrapping but may go outside in a benign way  */
7843 	uint64_t k;
7844 
7845 	while (n > GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7846 
7847 	for (k = 0; k < n; k++) {
7848 		gmt_geo_to_xy (GMT, x[k], y[k], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);
7849 		GMT->current.plot.pen[k] = PSL_DRAW;
7850 	}
7851 	GMT->current.plot.pen[0] = PSL_MOVE;
7852 	return (n);
7853 }
7854 
7855 /*! . */
gmt_geo_to_xy_line(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n)7856 uint64_t gmt_geo_to_xy_line (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n) {
7857 	/* Traces the lon/lat array and returns x,y plus appropriate pen moves
7858 	 * Pen moves are caused by breakthroughs of the map boundary or when
7859 	 * a point has lon = NaN or lat = NaN (this means "pick up pen") */
7860 	uint64_t j, k, np, n_sections;
7861 	bool this_inside, jump, cartesian = gmt_M_is_cartesian (GMT, GMT_IN);
7862 	/* bool last_inside = false; */
7863 	unsigned int sides[4];
7864 	unsigned int nx;
7865 	double xlon[4], xlat[4], xx[4], yy[4];
7866 	double this_x, this_y, last_x, last_y, dummy[4];
7867 
7868 	if (n == 0) return 0;	/* Absolutely nothing to do */
7869 	while (n > GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7870 
7871 	/* Here we know n is at least 1 */
7872 	np = 0;
7873 	gmt_geo_to_xy (GMT, lon[0], lat[0], &last_x, &last_y);
7874 	if (!gmt_map_outside (GMT, lon[0], lat[0])) {	/* First point is inside the region */
7875 		GMT->current.plot.x[0] = last_x;	GMT->current.plot.y[0] = last_y;
7876 		GMT->current.plot.pen[np++] = PSL_MOVE;
7877 		/* last_inside = true; */
7878 	}
7879 	for (j = 1; j < n; j++) {
7880 		gmt_geo_to_xy (GMT, lon[j], lat[j], &this_x, &this_y);
7881 		this_inside = !gmt_map_outside (GMT, lon[j], lat[j]);
7882 		if (gmt_M_is_dnan (lon[j]) || gmt_M_is_dnan (lat[j])) continue;	/* Skip NaN point now */
7883 		if (gmt_M_is_dnan (lon[j-1]) || gmt_M_is_dnan (lat[j-1])) {		/* Point after NaN needs a move */
7884 			GMT->current.plot.x[np] = this_x;	GMT->current.plot.y[np] = this_y;
7885 			GMT->current.plot.pen[np++] = PSL_MOVE;
7886 			if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7887 			last_x = this_x;	last_y = this_y;	/* last_inside = this_inside; */
7888 			continue;
7889 		}
7890 		if ((nx = gmtmap_crossing (GMT, lon[j-1], lat[j-1], lon[j], lat[j], xlon, xlat, xx, yy, sides))) { /* Do nothing if we get crossings*/ }
7891 		else if (GMT->current.map.is_world)	/* Check global wrapping if 360 range */
7892 			nx = (*GMT->current.map.wrap_around_check) (GMT, dummy, last_x, last_y, this_x, this_y, xx, yy, sides);
7893 		if (nx == 1) {	/* inside-outside or outside-inside; set move&clip vs draw flags */
7894 			GMT->current.plot.x[np] = xx[0];	GMT->current.plot.y[np] = yy[0];
7895 			/* If next point is inside then we want to move to the crossing, otherwise we want to draw to the crossing */
7896 			GMT->current.plot.pen[np++] = (this_inside) ? PSL_MOVE|PSL_CLIP : PSL_DRAW|PSL_CLIP;
7897 			if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7898 		}
7899 		else if (nx == 2) {	/* outside-inside-outside or (with wrapping) inside-outside-inside */
7900 			/* PW: I will be working on things here to solve the polygon wrap problem reported by Nicky */
7901 			jump = gmtmap_accept_the_jump (GMT, lon[j], lon[j-1], xx, cartesian);
7902 			if (jump) {
7903 			//if ((this_inside && last_inside) || cartesian || dy > 0.1) {	/* outside-inside-outside or (with wrapping) inside-outside-inside */
7904 				GMT->current.plot.x[np] = xx[0];	GMT->current.plot.y[np] = yy[0];
7905 				GMT->current.plot.pen[np++] = (this_inside) ? PSL_DRAW|PSL_CLIP : PSL_MOVE|PSL_CLIP;
7906 				if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7907 				GMT->current.plot.x[np] = xx[1];	GMT->current.plot.y[np] = yy[1];
7908 				GMT->current.plot.pen[np++] = (this_inside) ? PSL_MOVE|PSL_CLIP : PSL_DRAW|PSL_CLIP;
7909 				if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7910 			}
7911 		}
7912 		if (this_inside) {
7913 			if ( np >= GMT->current.plot.n_alloc ) {
7914 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "bad access: cannot access current.plot.x[%" PRIu64 "], np=%" PRIu64 ", GMT->current.plot.n=%" PRIu64 "\n", np, np, GMT->current.plot.n);
7915 			}
7916 			else {
7917 				GMT->current.plot.x[np] = this_x;	GMT->current.plot.y[np] = this_y;
7918 				GMT->current.plot.pen[np++] = PSL_DRAW;
7919 			}
7920 			if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7921 		}
7922 		last_x = this_x;	last_y = this_y;	/* last_inside = this_inside; */
7923 	}
7924 	if (np) GMT->current.plot.pen[0] |= PSL_MOVE;	/* Sanity override: Gotta start off with new start point */
7925 
7926 	/* When a line that starts and ends inside the domain exits and reenters, we end up with two pieces.
7927 	 * When these are plotted separately the sections are not joined properly and ugly gaps can appear, especially if
7928 	 * the pen width is large.  Here we try to fix this case since it happens fairly frequently */
7929 
7930 	for (j = n_sections = k = 0; j < np; j++) if (GMT->current.plot.pen[j] & PSL_MOVE) {
7931 		n_sections++;
7932 		if (n_sections == 2) k = j;	/* Start of 2nd section */
7933 	}
7934 	if (n_sections == 2 && doubleAlmostEqualZero (GMT->current.plot.x[0], GMT->current.plot.x[np-1]) && doubleAlmostEqualZero (GMT->current.plot.y[0], GMT->current.plot.y[np-1])) {
7935 		double *tmp = gmt_M_memory (GMT, NULL, np, double);
7936 		/* Shuffle x-array safely */
7937 		gmt_M_memcpy (tmp, &GMT->current.plot.x[k], np-k, double);
7938 		gmt_M_memcpy (&tmp[np-k], GMT->current.plot.x, k, double);
7939 		gmt_M_memcpy (GMT->current.plot.x, tmp, np, double);
7940 		/* Shuffle y-array safely */
7941 		gmt_M_memcpy (tmp, &GMT->current.plot.y[k], np-k, double);
7942 		gmt_M_memcpy (&tmp[np-k], GMT->current.plot.y, k, double);
7943 		gmt_M_memcpy (GMT->current.plot.y, tmp, np, double);
7944 		/* Change PSL_MOVE to PSL_DRAW at start of 2nd section */
7945 		GMT->current.plot.pen[k] = PSL_DRAW;
7946 		if (k && GMT->current.plot.pen[k-1] & PSL_CLIP) GMT->current.plot.pen[k-1] = PSL_DRAW;
7947 		if (k < (np-1) && GMT->current.plot.pen[k+1] & PSL_CLIP) GMT->current.plot.pen[k+1] = PSL_DRAW;
7948 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_geo_to_xy_line: Clipping in two separate abutting lines that were joined into a single line\n");
7949 		gmt_M_free (GMT, tmp);
7950 	}
7951 
7952 	return (np);
7953 }
7954 
7955 /*! . */
gmt_compact_line(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,int pen_flag,int * pen)7956 uint64_t gmt_compact_line (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, int pen_flag, int *pen) {
7957 	/* true if pen movements is present */
7958 	/* gmt_compact_line will remove unnecessary points in paths, but does not reallocate to the shorter size */
7959 	uint64_t i, j;
7960 	double old_slope, new_slope, dx;
7961 	char *flag = NULL;
7962 
7963 	if (n < 3) return (n);	/* Nothing to do */
7964 	flag = gmt_M_memory (GMT, NULL, n, char);
7965 
7966 	dx = x[1] - x[0];
7967 	old_slope = (doubleAlmostEqualZero (x[1], x[0])) ? copysign (HALF_DBL_MAX, y[1] - y[0]) : (y[1] - y[0]) / dx;
7968 
7969 	for (i = 1; i < n-1; ++i) {
7970 		dx = x[i+1] - x[i];
7971 		new_slope = (doubleAlmostEqualZero (x[i+1], x[i])) ? copysign (HALF_DBL_MAX, y[i+1] - y[i]) : (y[i+1] - y[i]) / dx;
7972 		if (doubleAlmostEqualZero (new_slope, old_slope) && !(pen_flag && (pen[i]+pen[i+1]) > 4))	/* 4 is 2+2 which is draw line; a 3 will produce > 4 */
7973 			flag[i] = 1;
7974 		else
7975 			old_slope = new_slope;
7976 	}
7977 
7978 	for (i = j = 1; i < n; i++) {	/* i = 1 since first point must be included */
7979 		if (flag[i] == 0) {
7980 			x[j] = x[i];
7981 			y[j] = y[i];
7982 			if (pen_flag) pen[j] = pen[i];
7983 			j++;
7984 		}
7985 	}
7986 	gmt_M_free (GMT, flag);
7987 
7988 	return (j);
7989 }
7990 
7991 /* Routines to transform grdfiles to/from map projections */
7992 
7993 /*! . */
gmt_project_init(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,double * inc,unsigned int n_columns,unsigned int n_rows,unsigned int dpi,unsigned int offset)7994 int gmt_project_init (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, double *inc, unsigned int n_columns, unsigned int n_rows, unsigned int dpi, unsigned int offset) {
7995 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
7996 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_project_init: IN: Inc [%.12g/%.12g] n_columns/n_rows [%u/%u] dpi = %u offset = %u\n",
7997 		inc[0], inc[1], n_columns, n_rows, dpi, offset);
7998 
7999 	if (inc[GMT_X] > 0.0 && inc[GMT_Y] > 0.0) {
8000 		if (GMT->current.io.inc_code[GMT_X] || GMT->current.io.inc_code[GMT_Y]) {	/* Must convert from distance units to degrees */
8001 			gmt_M_memcpy (header->inc, inc, 2, double);	/* Set these temporarily as the grids incs */
8002 			gmt_RI_prepare (GMT, header);			/* Converts the proper xinc/yinc units */
8003 			gmt_M_memcpy (inc, header->inc, 2, double);	/* Restore the inc array for use below */
8004 			GMT->current.io.inc_code[GMT_X] = GMT->current.io.inc_code[GMT_Y] = 0;
8005 		}
8006 #if 0
8007 		if (<to ensure x-ymax is adjusted so the increments are exact>) {
8008 			header->wesn[XHI] = ceil  (header->wesn[XHI] / inc[GMT_X]) * inc[GMT_X];
8009 			header->wesn[YHI] = ceil  (header->wesn[YHI] / inc[GMT_Y]) * inc[GMT_Y];
8010 		}
8011 #endif
8012 		header->n_columns = gmt_M_get_n (GMT, header->wesn[XLO], header->wesn[XHI], inc[GMT_X], offset);
8013 		header->n_rows = gmt_M_get_n (GMT, header->wesn[YLO], header->wesn[YHI], inc[GMT_Y], offset);
8014 		header->inc[GMT_X] = gmt_M_get_inc (GMT, header->wesn[XLO], header->wesn[XHI], header->n_columns, offset);
8015 		header->inc[GMT_Y] = gmt_M_get_inc (GMT, header->wesn[YLO], header->wesn[YHI], header->n_rows, offset);
8016 	}
8017 	else if (n_columns > 0 && n_rows > 0) {
8018 		header->n_columns = n_columns;	header->n_rows = n_rows;
8019 		header->inc[GMT_X] = gmt_M_get_inc (GMT, header->wesn[XLO], header->wesn[XHI], header->n_columns, offset);
8020 		header->inc[GMT_Y] = gmt_M_get_inc (GMT, header->wesn[YLO], header->wesn[YHI], header->n_rows, offset);
8021 	}
8022 	else if (dpi > 0) {
8023 		header->n_columns = urint ((header->wesn[XHI] - header->wesn[XLO]) * dpi) + 1 - offset;
8024 		header->n_rows = urint ((header->wesn[YHI] - header->wesn[YLO]) * dpi) + 1 - offset;
8025 		header->inc[GMT_X] = gmt_M_get_inc (GMT, header->wesn[XLO], header->wesn[XHI], header->n_columns, offset);
8026 		header->inc[GMT_Y] = gmt_M_get_inc (GMT, header->wesn[YLO], header->wesn[YHI], header->n_rows, offset);
8027 	}
8028 	else {
8029 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_project_init: Necessary arguments not set\n");
8030 		return GMT_PROJECTION_ERROR;
8031 	}
8032 	header->registration = offset;
8033 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_project_init: OUT: Inc [%.12g/%.12g] n_columns/n_rows [%u/%u] dpi = %u offset = %u\n",
8034 		inc[0], inc[1], n_columns, n_rows, dpi, offset);
8035 
8036 	gmt_RI_prepare (GMT, header);	/* Ensure -R -I consistency and set n_columns, n_rows */
8037 	gmt_M_err_pass (GMT, gmt_grd_RI_verify (GMT, header, 1), "");
8038 	if (!HH->reset_pad) gmt_M_grd_setpad (GMT, header, GMT->current.io.pad);			/* Assign default pad */
8039 	gmt_set_grddim (GMT, header);	/* Set all dimensions before returning */
8040 
8041 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid projection from size %dx%d to %dx%d\n", n_columns, n_rows, header->n_columns, header->n_rows);
8042 	return (GMT_NOERROR);
8043 }
8044 
8045 /*! . */
gmt_grd_project(struct GMT_CTRL * GMT,struct GMT_GRID * I,struct GMT_GRID * O,bool inverse)8046 int gmt_grd_project (struct GMT_CTRL *GMT, struct GMT_GRID *I, struct GMT_GRID *O, bool inverse) {
8047 	/* Generalized grid projection that deals with both interpolation and averaging effects.
8048 	 * It requires the input grid to have 2 boundary rows/cols so that the bcr
8049 	 * functions can be used.  The I struct represents the input grid which is either in original
8050 	 * (i.e., lon/lat) coordinates or projected x/y (if inverse = true).
8051 	 *
8052 	 * I:	Grid and header with input grid on a padded grid with 2 extra rows/columns
8053 	 * O:	Grid and header for output grid, no padding needed (but allowed)
8054 	 * inverse:	true if input is x/y and we want to invert for a lon/lat grid
8055 	 *
8056 	 * In addition, these settings (via -n) control interpolation:
8057 	 * antialias:	true if we need to do the antialiasing STEP 1 (below)
8058 	 * interpolant:	0 = nearest neighbor, 1 = bilinear, 2 = B-spline, 3 = bicubic
8059 	 * threshold:	minimum weight to be used. If weight < threshold interpolation yields NaN.
8060 	 * We initialize the O->data array to NaN.
8061 	 *
8062 	 * Changed 10-Sep-07 to include the argument "antialias" and "threshold" and
8063 	 * made "interpolant" an integer (was int bilinear).
8064 	 */
8065 
8066 	bool in = false, out = false;
8067 	int col_in, row_in, col_out, row_out;
8068  	uint64_t ij_in, ij_out;
8069 	short int *nz = NULL;
8070 	double x_proj = 0.0, y_proj = 0.0, z_int, inv_nz;
8071 	double *x_in = NULL, *x_out = NULL, *x_in_proj = NULL, *x_out_proj = NULL;
8072 	double *y_in = NULL, *y_out = NULL, *y_in_proj = NULL, *y_out_proj = NULL;
8073 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
8074 	struct GMT_GRID *I2 = NULL;
8075 
8076 	/* Only input grid MUST have at least 2 rows/cols padding - otherwise we must allocate a temp grid */
8077 	if (I->header->pad[XLO] < 2 || I->header->pad[XHI] < 2 || I->header->pad[YLO] < 2 || I->header->pad[YHI] < 2) {
8078 		unsigned int def_pad[4], pad2[4] = {2, 2, 2, 2};
8079 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: Input grid has insufficient padding - create and work on a duplicate with r row/col pad\n");
8080 		gmt_M_memcpy (def_pad, GMT->current.io.pad, 4, unsigned int);	/* Save default pad */
8081 		gmt_M_memcpy (GMT->current.io.pad, pad2, 4, unsigned int);		/* Change default pad */
8082 		if ((I2 = GMT_Duplicate_Data (GMT->parent, GMT_IS_GRID, GMT_DUPLICATE_DATA | GMT_DUPLICATE_RESET, I)) == NULL) {
8083 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_grd_project: Unable to duplicate grid with padding\n");
8084 			return GMT_RUNTIME_ERROR;
8085 		}
8086 		gmt_BC_init (GMT, I2->header);	/* Initialize grid interpolation and boundary condition parameters */
8087 		gmt_grd_BC_set (GMT, I2, GMT_IN);	/* Set boundary conditions */
8088 		I = I2;	/* Use this input grid instead */
8089 		gmt_M_memcpy (GMT->current.io.pad, def_pad, 4, unsigned int);		/* Restore default pad */
8090 	}
8091 
8092 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_grd_project: In [%.12g/%.12g/%.12g/%.12g] and out [%.12g/%.12g/%.12g/%.12g]\n",
8093 		I->header->wesn[XLO], I->header->wesn[XHI], I->header->wesn[YLO], I->header->wesn[YHI],
8094 		O->header->wesn[XLO], O->header->wesn[XHI], O->header->wesn[YLO], O->header->wesn[YHI]);
8095 
8096 	/* Precalculate grid coordinates */
8097 
8098 	if (I->x && HH->reset_pad == 0) {	/* Reuse existing arrays if present, but not if grid has had its pad adjusted to make subsets of read-only memory grids */
8099 		x_in = I->x;	y_in = I->y;	in = true;
8100 	}
8101 	else {	/* Must allocate here */
8102 		x_in  = gmt_grd_coord (GMT, I->header, GMT_X);
8103 		y_in  = gmt_grd_coord (GMT, I->header, GMT_Y);
8104 	}
8105 	if (O->x) {	/* Reuse existing arrays */
8106 		x_out = O->x;	y_out = O->y;	out = true;
8107 	}
8108 	else {	/* Must allocate here */
8109 		x_out = gmt_grd_coord (GMT, O->header, GMT_X);
8110 		y_out = gmt_grd_coord (GMT, O->header, GMT_Y);
8111 	}
8112 
8113 	if (gmt_M_is_rect_graticule (GMT)) {	/* Since lon/lat parallels x/y it pays to precalculate projected grid coordinates up front */
8114 		x_in_proj  = gmt_M_memory (GMT, NULL, I->header->n_columns, double);
8115 		y_in_proj  = gmt_M_memory (GMT, NULL, I->header->n_rows, double);
8116 		x_out_proj = gmt_M_memory (GMT, NULL, O->header->n_columns, double);
8117 		y_out_proj = gmt_M_memory (GMT, NULL, O->header->n_rows, double);
8118 		if (inverse) {
8119 			gmt_M_row_loop  (GMT, I, row_in)  gmt_xy_to_geo (GMT, &x_proj, &y_in_proj[row_in], I->header->wesn[XLO], y_in[row_in]);
8120 			gmt_M_col_loop2 (GMT, I, col_in)  gmt_xy_to_geo (GMT, &x_in_proj[col_in], &y_proj, x_in[col_in], I->header->wesn[YLO]);
8121 			gmt_M_row_loop  (GMT, O, row_out) gmt_geo_to_xy (GMT, I->header->wesn[YLO], y_out[row_out], &x_proj, &y_out_proj[row_out]);
8122 			gmt_M_col_loop2 (GMT, O, col_out) gmt_geo_to_xy (GMT, x_out[col_out], I->header->wesn[YLO], &x_out_proj[col_out], &y_proj);
8123 		}
8124 		else {
8125 			gmt_M_row_loop  (GMT, I, row_in) gmt_geo_to_xy (GMT, I->header->wesn[XLO], y_in[row_in], &x_proj, &y_in_proj[row_in]);
8126 			gmt_M_col_loop2 (GMT, I, col_in) gmt_geo_to_xy (GMT, x_in[col_in], I->header->wesn[YLO], &x_in_proj[col_in], &y_proj);
8127 			gmt_M_row_loop  (GMT, O, row_out) gmt_xy_to_geo (GMT, &x_proj, &y_out_proj[row_out], I->header->wesn[YLO], y_out[row_out]);
8128 			gmt_M_col_loop2 (GMT, O, col_out) {	/* Here we must also align longitudes properly */
8129 				gmt_xy_to_geo (GMT, &x_out_proj[col_out], &y_proj, x_out[col_out], I->header->wesn[YLO]);
8130 				if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_out_proj[col_out])) {
8131 					while (x_out_proj[col_out] < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_out_proj[col_out] += 360.0;
8132 					while (x_out_proj[col_out] > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_out_proj[col_out] -= 360.0;
8133 				}
8134 			}
8135 		}
8136 	}
8137 
8138 	gmt_M_grd_loop (GMT, O, row_out, col_out, ij_out) O->data[ij_out] = GMT->session.f_NaN;	/* So that nodes outside will retain a NaN value */
8139 
8140 	/* PART 1: Project input grid points and do a blockmean operation */
8141 
8142 	O->header->z_min = FLT_MAX; O->header->z_max = -FLT_MAX;	/* Min/max for out */
8143 	if (GMT->common.n.antialias) {	/* Blockaverage repeat pixels, at least the first ~32767 of them... */
8144 		bool skip_repeat = false, duplicate_east = false;
8145 		int n_columns = O->header->n_columns, n_rows = O->header->n_rows, kase, duplicate_col;
8146 		nz = gmt_M_memory (GMT, NULL, O->header->size, short int);
8147 		/* Cannot do OPENMP yet here since it would require a reduction into an output array (nz) */
8148 		kase = gmt_whole_earth (GMT, I->header->wesn, GMT->common.R.wesn);
8149 		if (kase == 1 && I->header->registration == GMT_GRID_NODE_REG) {
8150 			/* Need to avoid giving the repeated east and west meridian values double weight when they plot inside the image.
8151 			 * This is only likely to happen when external global grids are passed in via GMT_IS_REFERENCE. */
8152 			skip_repeat = true;	/* Since both -180 and +180 fall inside the image, we only want to use one of then (-180) */
8153 			duplicate_east = gmt_M_is_periodic (GMT);	/* Since the meridian corresponding to map west only appears once we may need to duplicate on east */
8154 			if (duplicate_east)	/* Find the column in I->data that corresponds to the longitude of the left boundary of the map */
8155 				duplicate_col = gmt_M_grd_x_to_col (GMT, GMT->common.R.wesn[XLO], I->header);
8156 		}
8157 
8158 		gmt_M_row_loop (GMT, I, row_in) {	/* Loop over the input grid row coordinates */
8159 			if (gmt_M_is_rect_graticule (GMT)) y_proj = y_in_proj[row_in];
8160 			gmt_M_col_loop (GMT, I, row_in, col_in, ij_in) {	/* Loop over the input grid col coordinates */
8161 				if (skip_repeat && col_in == 0) continue;
8162 				if (gmt_M_is_rect_graticule (GMT))
8163 					x_proj = x_in_proj[col_in];
8164 				else if (inverse)
8165 					gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_in[col_in], y_in[row_in]);
8166 				else {
8167 					if (GMT->current.map.outside (GMT, x_in[col_in], y_in[row_in])) continue;	/* Quite possible we are beyond the horizon */
8168 					gmt_geo_to_xy (GMT, x_in[col_in], y_in[row_in], &x_proj, &y_proj);
8169 				}
8170 
8171 				/* Here, (x_proj, y_proj) is the projected grid point.  Now find nearest node on the output grid */
8172 
8173 				row_out = gmt_M_grd_y_to_row (GMT, y_proj, O->header);
8174 				if (row_out < 0 || row_out >= n_rows) continue;	/* Outside our grid region */
8175 				col_out = gmt_M_grd_x_to_col (GMT, x_proj, O->header);
8176 				if (col_out < 0 || col_out >= n_columns) continue;	/* Outside our grid region */
8177 
8178 				/* OK, this projected point falls inside the projected grid's rectangular domain */
8179 
8180 				ij_out = gmt_M_ijp (O->header, row_out, col_out);	/* The output node */
8181 				if (nz[ij_out] == 0) O->data[ij_out] = 0.0f;	/* First time, override the initial value */
8182 				if (nz[ij_out] < SHRT_MAX) {			/* Avoid overflow */
8183 					O->data[ij_out] += I->data[ij_in];	/* Add up the z-sum inside this rect... */
8184 					nz[ij_out]++;				/* ..and how many points there were */
8185 				}
8186 				if (gmt_M_is_fnan (O->data[ij_out])) continue;
8187 				if (O->data[ij_out] < O->header->z_min) O->header->z_min = O->data[ij_out];
8188 				else if (O->data[ij_out] > O->header->z_max) O->header->z_max = O->data[ij_out];
8189 			}
8190 			if (duplicate_east) {
8191 				ij_in = ij_in - I->header->n_columns + duplicate_col;	/* Rewind to the col to be duplicated */
8192 				col_in = duplicate_col;
8193 				if (gmt_M_is_rect_graticule (GMT))
8194 					x_proj = GMT->current.proj.rect[XHI];
8195 				else if (inverse)
8196 					gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_in[col_in], y_in[row_in]);
8197 				else {
8198 					if (GMT->current.map.outside (GMT, x_in[col_in], y_in[row_in])) continue;	/* Quite possible we are beyond the horizon */
8199 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], y_in[row_in], &x_proj, &y_proj);
8200 				}
8201 
8202 				/* Here, (x_proj, y_proj) is the projected grid point.  Now find nearest node on the output grid */
8203 
8204 				row_out = gmt_M_grd_y_to_row (GMT, y_proj, O->header);
8205 				if (row_out < 0 || row_out >= n_rows) continue;	/* Outside our grid region */
8206 				col_out = gmt_M_grd_x_to_col (GMT, x_proj, O->header);
8207 				if (col_out < 0 || col_out >= n_columns) continue;	/* Outside our grid region */
8208 
8209 				/* OK, this projected point falls inside the projected grid's rectangular domain */
8210 
8211 				ij_out = gmt_M_ijp (O->header, row_out, col_out);	/* The output node */
8212 				if (nz[ij_out] == 0) O->data[ij_out] = 0.0f;	/* First time, override the initial value */
8213 				if (nz[ij_out] < SHRT_MAX) {			/* Avoid overflow */
8214 					O->data[ij_out] += I->data[ij_in];	/* Add up the z-sum inside this rect... */
8215 					nz[ij_out]++;				/* ..and how many points there were */
8216 				}
8217 				if (gmt_M_is_fnan (O->data[ij_out])) continue;
8218 				if (O->data[ij_out] < O->header->z_min) O->header->z_min = O->data[ij_out];
8219 				else if (O->data[ij_out] > O->header->z_max) O->header->z_max = O->data[ij_out];
8220 
8221 			}
8222 		}
8223 #ifdef DEBUG
8224 		if (GMT->common.n.save_debug) {	/* Write nz as a debug grid to file */
8225 			uint64_t k;
8226 			struct GMT_GRID *G = NULL;
8227 			if ((G = GMT_Create_Data (GMT->parent, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, O->header->wesn, O->header->inc, \
8228 				O->header->registration, GMT_PAD_DEFAULT, NULL)) == NULL) goto bail_grd_dbg;
8229 			for (k = 0; k < G->header->size; k++) G->data[k] = (float)nz[k];
8230 			(void) GMT_Write_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, "nz_grd_counter.grd", G);
8231 			GMT_Report (GMT->parent, GMT_MSG_NOTICE, "gmt_grd_project: Antialias counter nz written to grid file nz_grd_counter.grd\n");
8232 			GMT_Destroy_Data (GMT->parent, &G);
8233 		}
8234 		bail_grd_dbg:
8235 		ij_out = 0;
8236 #endif
8237 	}
8238 
8239 /* PART 2: Create weighted average of interpolated and observed points */
8240 
8241 /* Open MP does not work yet */
8242 
8243 /* The OpenMP loop below fails and yields nodes still set to NaN.  I cannot see any errors but obviously
8244  * there is something that is not quite correct. */
8245 
8246 //#ifdef _OPENMP
8247 //#pragma omp parallel for private(row_out,y_proj,col_out,ij_out,x_proj,z_int,inv_nz) shared(O,GMT,y_out_proj,x_out_proj,inverse,x_out,y_out,I,nz)
8248 //#endif
8249 	for (row_out = 0; row_out < (int)O->header->n_rows; row_out++) {	/* Loop over the output grid row coordinates */
8250 		if (gmt_M_is_rect_graticule (GMT)) y_proj = y_out_proj[row_out];
8251 		gmt_M_col_loop (GMT, O, row_out, col_out, ij_out) {	/* Loop over the output grid col coordinates */
8252 			if (gmt_M_is_rect_graticule (GMT))
8253 				x_proj = x_out_proj[col_out];
8254 			else if (inverse)
8255 				gmt_geo_to_xy (GMT, x_out[col_out], y_out[row_out], &x_proj, &y_proj);
8256 			else {
8257 				gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_out[col_out], y_out[row_out]);
8258 				if (GMT->current.proj.projection_GMT == GMT_GENPER && GMT->current.proj.g_outside) continue;	/* We are beyond the horizon */
8259 
8260 				/* On 17-Sep-2007 the slack of GMT_CONV4_LIMIT was added to allow for round-off
8261 				   errors in the grid limits. */
8262 				if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_proj)) {
8263 					while (x_proj < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_proj += 360.0;
8264 					while (x_proj > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_proj -= 360.0;
8265 				}
8266 			}
8267 
8268 			/* Here, (x_proj, y_proj) is the inversely projected grid point.  Now the interpret the input grid at that projected output point */
8269 
8270 			z_int = gmt_bcr_get_z (GMT, I, x_proj, y_proj);
8271 
8272 			if (!GMT->common.n.antialias || nz[ij_out] < 2)	/* Just use the interpolated value */
8273 				O->data[ij_out] = (gmt_grdfloat)z_int;
8274 			else if (gmt_M_is_dnan (z_int)) {		/* Take the average of what we accumulated */
8275 				if (nz[ij_out]) O->data[ij_out] /= nz[ij_out];	/* Plain average */
8276 			}
8277 			else {					/* Weighted average between blockmean'ed and interpolated values */
8278 				inv_nz = 1.0 / nz[ij_out];
8279 				O->data[ij_out] = (gmt_grdfloat) ((O->data[ij_out] + z_int * inv_nz) / (nz[ij_out] + inv_nz));
8280 			}
8281 			if (O->data[ij_out] < O->header->z_min) O->header->z_min = O->data[ij_out];
8282 			if (O->data[ij_out] > O->header->z_max) O->header->z_max = O->data[ij_out];
8283 		}
8284 	}
8285 
8286 	if (O->header->z_min < I->header->z_min || O->header->z_max > I->header->z_max) {	/* Truncate output to input extrema */
8287 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: Output grid extrema [%g/%g] exceed extrema of input grid [%g/%g] due to resampling\n",
8288 			O->header->z_min, O->header->z_max, I->header->z_min, I->header->z_max);
8289 		if (GMT->common.n.truncate) {
8290 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: Output grid clipped to input grid extrema\n");
8291 			gmt_M_grd_loop (GMT, O, row_out, col_out, ij_out) {
8292 				if (O->data[ij_out] < I->header->z_min) O->data[ij_out] = (gmt_grdfloat)I->header->z_min;
8293 				else if (O->data[ij_out] > I->header->z_max) O->data[ij_out] = (gmt_grdfloat)I->header->z_max;
8294 			}
8295 			O->header->z_min = I->header->z_min;	O->header->z_max = I->header->z_max;
8296 		}
8297 		else
8298 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: See option -n+c to clip resampled output range to given input range\n");
8299 	}
8300 
8301 	/* Time to clean up our mess */
8302 
8303 	if (!in) {
8304 		gmt_M_free (GMT, x_in);
8305 		gmt_M_free (GMT, y_in);
8306 	}
8307 	if (!out) {
8308 		gmt_M_free (GMT, x_out);
8309 		gmt_M_free (GMT, y_out);
8310 	}
8311 	if (gmt_M_is_rect_graticule(GMT)) {
8312 		gmt_M_free (GMT, x_in_proj);
8313 		gmt_M_free (GMT, y_in_proj);
8314 		gmt_M_free (GMT, x_out_proj);
8315 		gmt_M_free (GMT, y_out_proj);
8316 	}
8317 	if (GMT->common.n.antialias) gmt_M_free (GMT, nz);
8318 
8319 	if (I2 && GMT_Destroy_Data (GMT->parent, &I2)) {
8320 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_grd_project: Unable to free padded grid\n");
8321 		return GMT_RUNTIME_ERROR;
8322 	}
8323 	return (GMT_NOERROR);
8324 }
8325 
8326 /*! . */
gmt_img_project(struct GMT_CTRL * GMT,struct GMT_IMAGE * I,struct GMT_IMAGE * O,bool inverse)8327 int gmt_img_project (struct GMT_CTRL *GMT, struct GMT_IMAGE *I, struct GMT_IMAGE *O, bool inverse) {
8328 	/* Generalized image projection that deals with both interpolation and averaging effects.
8329 	 * It requires the input image to have 2 boundary rows/cols so that the bcr
8330 	 * functions can be used.  The I struct represents the input image which is either in original
8331 	 * (i.e., lon/lat) coordinates or projected x/y (if inverse = true).
8332 	 *
8333 	 * I:	Image and header with input image on a padded image with 2 extra rows/columns
8334 	 * O:	Image and header for output image, no padding needed (but allowed)
8335 	 * edgeinfo:	Structure with information about boundary conditions on input image
8336 	 * inverse:	true if input is x/y and we want to invert for a lon/lat image
8337 	 *
8338 	 * In addition, these settings (via -n) control interpolation:
8339 	 * antialias:	true if we need to do the antialiasing STEP 1 (below)
8340 	 * interpolant:	0 = nearest neighbor, 1 = bilinear, 2 = B-spline, 3 = bicubic
8341 	 * threshold:	minimum weight to be used. If weight < threshold interpolation yields NaN.
8342 	 *
8343 	 * We initialize the O->data array to the NaN color.
8344 	 *
8345 	 * Changed 10-Sep-07 to include the argument "antialias" and "threshold" and
8346 	 * made "interpolant" an integer (was int bilinear).
8347 	 */
8348 
8349 	bool in = false, out = false;
8350 	int col_in, row_in, col_out, row_out, b, nb = I->header->n_bands;
8351  	uint64_t ij_in, ij_out;
8352 	short int *nz = NULL;
8353 	double x_proj = 0.0, y_proj = 0.0, inv_nz, rgb[4];
8354 	double *x_in = NULL, *x_out = NULL, *x_in_proj = NULL, *x_out_proj = NULL;
8355 	double *y_in = NULL, *y_out = NULL, *y_in_proj = NULL, *y_out_proj = NULL;
8356 	unsigned char z_int[4], z_int_bg[4];
8357 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
8358 
8359 	/* Only input image MUST have at least 2 rows/cols padding */
8360 	if (I->header->pad[XLO] < 2 || I->header->pad[XHI] < 2 || I->header->pad[YLO] < 2 || I->header->pad[YHI] < 2) {
8361 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_img_project: Input image does not have sufficient (2) padding\n");
8362 		return GMT_RUNTIME_ERROR;
8363 	}
8364 
8365 	/* Precalculate grid coordinates */
8366 
8367 	if (I->x && HH->reset_pad == 0) {	/* Reuse existing arrays if present, but not if grid has had its pad adjusted to make subsets of read-only memory grids */
8368 		x_in = I->x;	y_in = I->y;	in = true;
8369 	}
8370 	else {	/* Must allocate here */
8371 		x_in  = gmt_grd_coord (GMT, I->header, GMT_X);
8372 		y_in  = gmt_grd_coord (GMT, I->header, GMT_Y);
8373 	}
8374 	if (O->x) {	/* Reuse existing arrays */
8375 		x_out = O->x;	y_out = O->y;	out = true;
8376 	}
8377 	else {	/* Must allocate here */
8378 		x_out = gmt_grd_coord (GMT, O->header, GMT_X);
8379 		y_out = gmt_grd_coord (GMT, O->header, GMT_Y);
8380 	}
8381 
8382 	if (gmt_M_is_rect_graticule (GMT)) {	/* Since lon/lat parallels x/y it pays to precalculate projected grid coordinates up front */
8383 		x_in_proj  = gmt_M_memory (GMT, NULL, I->header->n_columns, double);
8384 		y_in_proj  = gmt_M_memory (GMT, NULL, I->header->n_rows, double);
8385 		x_out_proj = gmt_M_memory (GMT, NULL, O->header->n_columns, double);
8386 		y_out_proj = gmt_M_memory (GMT, NULL, O->header->n_rows, double);
8387 		if (inverse) {
8388 			gmt_M_row_loop  (GMT, I, row_in)  gmt_xy_to_geo (GMT, &x_proj, &y_in_proj[row_in], I->header->wesn[XLO], y_in[row_in]);
8389 			gmt_M_col_loop2 (GMT, I, col_in)  gmt_xy_to_geo (GMT, &x_in_proj[col_in], &y_proj, x_in[col_in], I->header->wesn[YLO]);
8390 			gmt_M_row_loop  (GMT, O, row_out) gmt_geo_to_xy (GMT, I->header->wesn[YLO], y_out[row_out], &x_proj, &y_out_proj[row_out]);
8391 			gmt_M_col_loop2 (GMT, O, col_out) gmt_geo_to_xy (GMT, x_out[col_out], I->header->wesn[YLO], &x_out_proj[col_out], &y_proj);
8392 		}
8393 		else {
8394 			gmt_M_row_loop  (GMT, I, row_in) gmt_geo_to_xy (GMT, I->header->wesn[XLO], y_in[row_in], &x_proj, &y_in_proj[row_in]);
8395 			gmt_M_col_loop2 (GMT, I, col_in) gmt_geo_to_xy (GMT, x_in[col_in], I->header->wesn[YLO], &x_in_proj[col_in], &y_proj);
8396 			gmt_M_row_loop  (GMT, O, row_out) gmt_xy_to_geo (GMT, &x_proj, &y_out_proj[row_out], I->header->wesn[YLO], y_out[row_out]);
8397 			gmt_M_col_loop2 (GMT, O, col_out) {	/* Here we must also align longitudes properly */
8398 				gmt_xy_to_geo (GMT, &x_out_proj[col_out], &y_proj, x_out[col_out], I->header->wesn[YLO]);
8399 				if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_out_proj[col_out])) {
8400 					while (x_out_proj[col_out] < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_out_proj[col_out] += 360.0;
8401 					while (x_out_proj[col_out] > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_out_proj[col_out] -= 360.0;
8402 				}
8403 			}
8404 		}
8405 	}
8406 
8407 #if 0
8408 	gmt_M_grd_loop (GMT, O, row_out, col_out, ij_out) 		/* So that nodes outside will have the NaN color */
8409 		for (b = 0; b < nb; b++) O->data[nb*ij_out+b] = gmt_M_u255 (GMT->current.setting.color_patch[GMT_NAN][b]);
8410 #endif
8411 	for (b = 0; b < 4; b++) z_int_bg[b] = gmt_M_u255 (GMT->current.setting.color_patch[GMT_NAN][b]);
8412 
8413 	/* PART 1: Project input image points and do a blockmean operation */
8414 
8415 	if (GMT->common.n.antialias) {	/* Blockaverage repeat pixels, at least the first ~32767 of them... */
8416 		int n_columns = O->header->n_columns, n_rows = O->header->n_rows;
8417 		nz = gmt_M_memory (GMT, NULL, O->header->size, short int);
8418 		/* Cannot do OPENMP yet here since it would require a reduction into an output array (nz) */
8419 
8420 		gmt_M_row_loop (GMT, I, row_in) {	/* Loop over the input grid row coordinates */
8421 			if (gmt_M_is_rect_graticule (GMT)) y_proj = y_in_proj[row_in];
8422 			gmt_M_col_loop (GMT, I, row_in, col_in, ij_in) {	/* Loop over the input grid col coordinates */
8423 				if (gmt_M_is_rect_graticule (GMT))
8424 					x_proj = x_in_proj[col_in];
8425 				else if (inverse)
8426 					gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_in[col_in], y_in[row_in]);
8427 				else {
8428 					if (GMT->current.map.outside (GMT, x_in[col_in], y_in[row_in])) continue;	/* Quite possible we are beyond the horizon */
8429 					gmt_geo_to_xy (GMT, x_in[col_in], y_in[row_in], &x_proj, &y_proj);
8430 				}
8431 
8432 				/* Here, (x_proj, y_proj) is the projected grid point.  Now find nearest node on the output grid */
8433 
8434 				row_out = (int)gmt_M_grd_y_to_row (GMT, y_proj, O->header);
8435 				if (row_out < 0 || row_out >= n_rows) continue;	/* Outside our grid region */
8436 				col_out = (int)gmt_M_grd_x_to_col (GMT, x_proj, O->header);
8437 				if (col_out < 0 || col_out >= n_columns) continue;	/* Outside our grid region */
8438 
8439 				/* OK, this projected point falls inside the projected grid's rectangular domain */
8440 
8441 				ij_out = gmt_M_ijp (O->header, row_out, col_out);	/* The output node */
8442 				if (nz[ij_out] == 0) for (b = 0; b < nb; b++) O->data[nb*ij_out+b] = 0;	/* First time, override the initial value */
8443 				if (nz[ij_out] < SHRT_MAX) {	/* Avoid overflow */
8444 					for (b = 0; b < nb; b++) {
8445 						rgb[b] = ((double)nz[ij_out] * O->data[nb*ij_out+b] + I->data[nb*ij_in+b])/(nz[ij_out] + 1.0);	/* Update the mean pix values inside this rect... */
8446 						O->data[nb*ij_out+b] = (unsigned char) lrint (gmt_M_0_255_truncate (rgb[b]));
8447 					}
8448 					nz[ij_out]++;		/* ..and how many points there were */
8449 				}
8450 			}
8451 		}
8452 #ifdef DEBUG
8453 		if (GMT->common.n.save_debug) {	/* Write nz as a debug grid to file */
8454 			uint64_t k;
8455 			struct GMT_GRID *G = NULL;
8456 			if ((G = GMT_Create_Data (GMT->parent, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, O->header->wesn, O->header->inc, \
8457 				O->header->registration, GMT_PAD_DEFAULT, NULL)) == NULL) goto bail_img_dbg;
8458 			for (k = 0; k < G->header->size; k++) G->data[k] = (float)nz[k];
8459 			(void) GMT_Write_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, "nz_img_counter.grd", G);
8460 			GMT_Report (GMT->parent, GMT_MSG_NOTICE, "gmt_grd_project: Antialias counter nz written to grid file nz_img_counter.grd\n");
8461 			GMT_Destroy_Data (GMT->parent, &G);
8462 		}
8463 		bail_img_dbg:
8464 		ij_out = 0;
8465 #endif
8466 	}
8467 /* PART 2: Create weighted average of interpolated and observed points */
8468 
8469 //#ifdef _OPENMP
8470 //#pragma omp parallel for private(row_out,y_proj,col_out,ij_out,x_proj,z_int,inv_nz,b) shared(O,GMT,y_out_proj,x_out_proj,inverse,x_out,y_out,I,nz,z_int_bg,nb)
8471 //#endif
8472 	for (row_out = 0; row_out < (int)O->header->n_rows; row_out++) {	/* Loop over the output grid row coordinates */
8473 		if (gmt_M_is_rect_graticule (GMT)) y_proj = y_out_proj[row_out];
8474 		gmt_M_col_loop (GMT, O, row_out, col_out, ij_out) {	/* Loop over the output grid col coordinates */
8475 			if (gmt_M_is_rect_graticule (GMT))
8476 				x_proj = x_out_proj[col_out];
8477 			else if (inverse)
8478 				gmt_geo_to_xy (GMT, x_out[col_out], y_out[row_out], &x_proj, &y_proj);
8479 			else {
8480 				gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_out[col_out], y_out[row_out]);
8481 				if (GMT->current.proj.projection_GMT == GMT_GENPER && GMT->current.proj.g_outside) continue;	/* We are beyond the horizon */
8482 
8483 				/* On 17-Sep-2007 the slack of GMT_CONV4_LIMIT was added to allow for round-off
8484 				   errors in the grid limits. */
8485 				if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_proj)) {
8486 					while (x_proj < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_proj += 360.0;
8487 					while (x_proj > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_proj -= 360.0;
8488 				}
8489 			}
8490 
8491 			/* Here, (x_proj, y_proj) is the inversely projected grid point.  Now find nearest node on the input grid */
8492 
8493 			if (gmtlib_bcr_get_img (GMT, I, x_proj, y_proj, z_int))		/* So that nodes outside will have the NaN color */
8494 				for (b = 0; b < 4; b++) z_int[b] = z_int_bg[b];
8495 
8496 			if (!GMT->common.n.antialias || nz[ij_out] < 2)	/* Just use the interpolated value */
8497 				for (b = 0; b < nb; b++) O->data[nb*ij_out+b] = z_int[b];
8498 			else {						/* Weighted average between blockmean'ed and interpolated values */
8499 				inv_nz = 1.0 / nz[ij_out];
8500 				for (b = 0; b < nb; b++) {
8501 					rgb[b] = ((double)nz[ij_out] * O->data[nb*ij_out+b] + z_int[b] * inv_nz) / (nz[ij_out] + inv_nz);
8502 					O->data[nb*ij_out+b] = (unsigned char) lrint (gmt_M_0_255_truncate (rgb[b]));
8503 				}
8504 			}
8505 		}
8506 	}
8507 
8508 	/* Time to clean up our mess */
8509 
8510 	if (!in) {
8511 		gmt_M_free (GMT, x_in);
8512 		gmt_M_free (GMT, y_in);
8513 	}
8514 	if (!out) {
8515 		gmt_M_free (GMT, x_out);
8516 		gmt_M_free (GMT, y_out);
8517 	}
8518 	if (gmt_M_is_rect_graticule(GMT)) {
8519 		gmt_M_free (GMT, x_in_proj);
8520 		gmt_M_free (GMT, y_in_proj);
8521 		gmt_M_free (GMT, x_out_proj);
8522 		gmt_M_free (GMT, y_out_proj);
8523 	}
8524 	if (GMT->common.n.antialias) gmt_M_free (GMT, nz);
8525 
8526 	return (GMT_NOERROR);
8527 }
8528 
8529 /*! . */
gmt_azim_to_angle(struct GMT_CTRL * GMT,double lon,double lat,double c,double azim)8530 double gmt_azim_to_angle (struct GMT_CTRL *GMT, double lon, double lat, double c, double azim) {
8531 	/* All variables in degrees */
8532 
8533 	double lon1, lat1, x0, x1, y0, y1, dx, width, sinaz, cosaz, angle;
8534 
8535 	if (gmt_M_is_linear (GMT)) {	/* Trivial case */
8536 		angle = 90.0 - azim;
8537 		if (GMT->current.proj.scale[GMT_X] != GMT->current.proj.scale[GMT_Y]) {	/* But allow for different x,y scaling */
8538 			sincosd (angle, &sinaz, &cosaz);
8539 			angle = d_atan2d (sinaz * GMT->current.proj.scale[GMT_Y], cosaz * GMT->current.proj.scale[GMT_X]);
8540 		}
8541 		return (angle);
8542 	}
8543 	else if (GMT->current.proj.projection_GMT == GMT_POLAR) {	/* r/theta */
8544 		return (azim);	/* Place holder - not correct yet but don't want to go into the below if r/theta */
8545 	}
8546 
8547 	/* Find second point c spherical degrees away in the azim direction */
8548 
8549 	gmtlib_get_point_from_r_az (GMT, lon, lat, c, azim, &lon1, &lat1);
8550 
8551 	/* Convert both points to x,y and get angle */
8552 
8553 	gmt_geo_to_xy (GMT, lon, lat, &x0, &y0);
8554 	gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
8555 
8556 	/* Check for wrap-around */
8557 
8558 	dx = x1 - x0;
8559 	if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]) && fabs (dx) > (width = gmt_half_map_width (GMT, y0))) {
8560 		width *= 2.0;
8561 		if (x1 < width)
8562 			x0 -= width;
8563 		else
8564 			x0 += width;
8565 	}
8566 	angle = d_atan2d (y1 - y0, x1 - x0);
8567 	return (angle);
8568 }
8569 
8570 /*! . */
gmt_map_clip_path(struct GMT_CTRL * GMT,double ** x,double ** y,bool * donut)8571 uint64_t gmt_map_clip_path (struct GMT_CTRL *GMT, double **x, double **y, bool *donut) {
8572 	/* This function returns a clip path corresponding to the
8573 	 * extent of the map.
8574 	 */
8575 
8576 	uint64_t i, j, np;
8577 	bool do_circle = false;
8578 	double *work_x = NULL, *work_y = NULL, da, r0, s, c, lon, lat;
8579 
8580 	*donut = false;
8581 
8582 	if (GMT->common.R.oblique)	/* Rectangular map boundary */
8583 		np = 4;
8584 	else {
8585 		switch (GMT->current.proj.projection_GMT) {
8586 			case GMT_LINEAR:
8587 			case GMT_MERCATOR:
8588 			case GMT_CYL_EQ:
8589 			case GMT_CYL_EQDIST:
8590 			case GMT_CYL_STEREO:
8591 			case GMT_MILLER:
8592 			case GMT_OBLIQUE_MERC:
8593 				np = 4;
8594 				break;
8595 			case GMT_POLAR:
8596 				if (GMT->current.proj.flip)
8597 					*donut = (GMT->common.R.wesn[YHI] < GMT->current.proj.flip_radius && GMT->current.map.is_world);
8598 				else
8599 					*donut = (GMT->common.R.wesn[YLO] > 0.0 && GMT->current.map.is_world);
8600 				np = GMT->current.map.n_lon_nodes + 1;
8601 				if ((GMT->current.proj.flip && GMT->common.R.wesn[YHI] < GMT->current.proj.flip_radius) || (!GMT->current.proj.flip && GMT->common.R.wesn[YLO] > 0.0))	/* Need inside circle segment */
8602 					np *= 2;
8603 				else if (!GMT->current.map.is_world)	/* Need to include origin */
8604 					np++;
8605 				break;
8606 			case GMT_GENPER:
8607 			case GMT_STEREO:
8608 			case GMT_LAMBERT:
8609 			case GMT_LAMB_AZ_EQ:
8610 			case GMT_ORTHO:
8611 			case GMT_GNOMONIC:
8612 			case GMT_AZ_EQDIST:
8613 			case GMT_ALBERS:
8614 			case GMT_ECONIC:
8615 			case GMT_VANGRINTEN:
8616 				np = (GMT->current.proj.polar && (GMT->common.R.wesn[YLO] <= -90.0 || GMT->common.R.wesn[YHI] >= 90.0)) ? GMT->current.map.n_lon_nodes + 2: 2 * (GMT->current.map.n_lon_nodes + 1);
8617 				break;
8618 			case GMT_MOLLWEIDE:
8619 			case GMT_SINUSOIDAL:
8620 			case GMT_ROBINSON:
8621 				np = 2 * GMT->current.map.n_lat_nodes + 2;
8622 				break;
8623 			case GMT_WINKEL:
8624 			case GMT_HAMMER:
8625 			case GMT_ECKERT4:
8626 			case GMT_ECKERT6:
8627 				np = 2 * GMT->current.map.n_lat_nodes + 2;
8628 				if (GMT->common.R.wesn[YLO] != -90.0) np += GMT->current.map.n_lon_nodes - 1;
8629 				if (GMT->common.R.wesn[YHI] != 90.0) np += GMT->current.map.n_lon_nodes - 1;
8630 				break;
8631 			case GMT_TM:
8632 			case GMT_UTM:
8633 			case GMT_CASSINI:
8634 			case GMT_POLYCONIC:
8635 				np = 2 * (GMT->current.map.n_lon_nodes + GMT->current.map.n_lat_nodes);
8636 				break;
8637 			default:
8638 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad case in gmt_map_clip_path (%d)\n", GMT->current.proj.projection_GMT);
8639 				np = 0;
8640 				break;
8641 		}
8642 	}
8643 
8644 	work_x = gmt_M_memory (GMT, NULL, np+1, double);	/* Add one for manual closure */
8645 	work_y = gmt_M_memory (GMT, NULL, np+1, double);
8646 
8647 	if (GMT->common.R.oblique) {
8648 		work_x[0] = work_x[3] = GMT->current.proj.rect[XLO];	work_y[0] = work_y[1] = GMT->current.proj.rect[YLO];
8649 		work_x[1] = work_x[2] = GMT->current.proj.rect[XHI];	work_y[2] = work_y[3] = GMT->current.proj.rect[YHI];
8650 	}
8651 	else {
8652 		if (gmt_M_is_azimuthal (GMT)) do_circle = GMT->current.map.is_world;
8653 		switch (GMT->current.proj.projection_GMT) {	/* Fill in clip path */
8654 			case GMT_LINEAR:
8655 			case GMT_MERCATOR:
8656 			case GMT_CYL_EQ:
8657 			case GMT_CYL_EQDIST:
8658 			case GMT_CYL_STEREO:
8659 			case GMT_MILLER:
8660 			case GMT_OBLIQUE_MERC:
8661 				work_x[0] = work_x[3] = GMT->current.proj.rect[XLO];	work_y[0] = work_y[1] = GMT->current.proj.rect[YLO];
8662 				work_x[1] = work_x[2] = GMT->current.proj.rect[XHI];	work_y[2] = work_y[3] = GMT->current.proj.rect[YHI];
8663 				break;
8664 			case GMT_LAMBERT:
8665 			case GMT_ALBERS:
8666 			case GMT_ECONIC:
8667 				for (i = j = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8668 					lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon;
8669 					gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8670 				}
8671 				for (i = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8672 					lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon;
8673 					gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8674 				}
8675 				break;
8676 			case GMT_TM:
8677 			case GMT_UTM:
8678 			case GMT_CASSINI:
8679 			case GMT_POLYCONIC:
8680 				for (i = j = 0; i < GMT->current.map.n_lon_nodes; i++, j++)	/* South */
8681 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8682 				for (i = 0; i < GMT->current.map.n_lat_nodes; i++, j++)	/* East */
8683 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, &work_x[j], &work_y[j]);
8684 				for (i = 0; i < GMT->current.map.n_lon_nodes; i++, j++)	/* North */
8685 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8686 				for (i = 0; i < GMT->current.map.n_lat_nodes; i++, j++)	/* West */
8687 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI] - i * GMT->current.map.dlat, &work_x[j], &work_y[j]);
8688 				break;
8689 			case GMT_POLAR:
8690 				r0 = GMT->current.proj.r * GMT->common.R.wesn[YLO] / GMT->common.R.wesn[YHI];
8691 				if (*donut) {
8692 					np /= 2;
8693 					da = TWO_PI / np;
8694 					for (i = 0, j = 2 * np - 1; i < np; i++, j--) {	/* Draw outer clippath */
8695 						sincos (i * da, &s, &c);
8696 						work_x[i] = GMT->current.proj.r * (1.0 + c);
8697 						work_y[i] = GMT->current.proj.r * (1.0 + s);
8698 						/* Do inner clippath and put it at end of array */
8699 						work_x[j] = GMT->current.proj.r + r0 * c;
8700 						work_y[j] = GMT->current.proj.r + r0 * s;
8701 					}
8702 				}
8703 				else {
8704 					da = fabs (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / GMT->current.map.n_lon_nodes;
8705 					if (GMT->current.proj.flip) {
8706 						for (i = j = 0; i <= GMT->current.map.n_lon_nodes; i++, j++)	/* Draw outer clippath */
8707 							gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * da, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8708 						if (GMT->common.R.wesn[YHI] < GMT->current.proj.flip_radius) {	/* Must do the inner path as well */
8709 							for (i = GMT->current.map.n_lon_nodes + 1; i > 0; i--, j++)	/* Draw inner clippath */
8710 								gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + (i-1) * da, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8711 						}
8712 						if (doubleAlmostEqual (GMT->common.R.wesn[YHI], 90.0) && !GMT->current.map.is_world)	/* Add origin */
8713 							gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8714 					}
8715 					else {
8716 						for (i = j = 0; i <= GMT->current.map.n_lon_nodes; i++, j++)	/* Draw outer clippath */
8717 							gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * da, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8718 						if (GMT->common.R.wesn[YLO] > 0.0) {	/* Must do the inner path as well */
8719 							for (i = GMT->current.map.n_lon_nodes + 1; i > 0; i--, j++)	/* Draw inner clippath */
8720 								gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + (i-1) * da, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8721 						}
8722 						if (gmt_M_is_zero (GMT->common.R.wesn[YLO]) && !GMT->current.map.is_world)	/* Add origin */
8723 							gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8724 					}
8725 				}
8726 				break;
8727 			case GMT_GENPER:
8728 				gmtlib_genper_map_clip_path (GMT, np, work_x, work_y);
8729 				break;
8730 			case GMT_VANGRINTEN:
8731 				do_circle = GMT->current.map.is_world;
8732 				/* Intentionally fall through */
8733 			case GMT_LAMB_AZ_EQ:
8734 			case GMT_AZ_EQDIST:
8735 			case GMT_ORTHO:
8736 			case GMT_GNOMONIC:
8737 			case GMT_STEREO:
8738 				if (GMT->current.proj.polar && !do_circle) {
8739 					j = 0;
8740 					if (GMT->common.R.wesn[YLO] > -90.0) {
8741 						for (i = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8742 							lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon;
8743 							gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8744 						}
8745 					}
8746 					else { /* Just add S pole */
8747 						gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], -90.0, &work_x[j], &work_y[j]);
8748 						j++;
8749 					}
8750 					if (GMT->common.R.wesn[YHI] < 90.0) {
8751 						for (i = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8752 							lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon;
8753 							gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8754 						}
8755 					}
8756 					else { /* Just add N pole */
8757 						gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], 90.0, &work_x[j], &work_y[j]);
8758 						j++;
8759 					}
8760 				}
8761 				else {	/* Just need to create a circular closed path */
8762 					da = TWO_PI / np;
8763 					for (i = 0; i < np; i++) {
8764 						sincos (i * da, &s, &c);
8765 						work_x[i] = GMT->current.proj.r * (1.0 + c);
8766 						work_y[i] = GMT->current.proj.r * (1.0 + s);
8767 					}
8768 				}
8769 				break;
8770 			case GMT_MOLLWEIDE:
8771 			case GMT_SINUSOIDAL:
8772 			case GMT_ROBINSON:
8773 				for (i = j = 0; i <= GMT->current.map.n_lat_nodes; i++, j++) {	/* Right */
8774 					lat = (i == GMT->current.map.n_lat_nodes) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat;
8775 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], lat, &work_x[j], &work_y[j]);
8776 				}
8777 				gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);	j++;
8778 				for (i = GMT->current.map.n_lat_nodes; i > 0; j++, i--)	{	/* Left */
8779 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + (i-1) * GMT->current.map.dlat, &work_x[j], &work_y[j]);
8780 				}
8781 				break;
8782 			case GMT_HAMMER:
8783 			case GMT_WINKEL:
8784 			case GMT_ECKERT4:
8785 			case GMT_ECKERT6:
8786 				for (i = j = 0; i <= GMT->current.map.n_lat_nodes; i++, j++) {	/* Right */
8787 					lat = (i == GMT->current.map.n_lat_nodes) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat;
8788 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], lat, &work_x[j], &work_y[j]);
8789 				}
8790 				for (i = 1; GMT->common.R.wesn[YHI] != 90.0 && i < GMT->current.map.n_lon_nodes; i++, j++)
8791 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8792 				gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);	j++;
8793 				for (i = GMT->current.map.n_lat_nodes; i > 0; j++, i--)	{	/* Left */
8794 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + (i-1)* GMT->current.map.dlat, &work_x[j], &work_y[j]);
8795 				}
8796 				for (i = 1; GMT->common.R.wesn[YLO] != -90.0 && i < GMT->current.map.n_lon_nodes; i++, j++)
8797 					gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8798 				break;
8799 		}
8800 	}
8801 
8802 	/* CLose the clipping polygon */
8803 	work_x[np] = work_x[0];
8804 	work_y[np] = work_y[0];
8805 	np++;
8806 	if (!(*donut)) np = gmt_compact_line (GMT, work_x, work_y, np, false, NULL);
8807 
8808 	*x = work_x;
8809 	*y = work_y;
8810 
8811 	return (np);
8812 }
8813 
8814 /*! . */
gmtmap_lat_swap_quick(struct GMT_CTRL * GMT,double lat,double c[])8815 double gmtmap_lat_swap_quick (struct GMT_CTRL *GMT, double lat, double c[]) {
8816 	/* Return latitude, in degrees, given latitude, in degrees, based on coefficients c */
8817 
8818 	double delta, cos2phi, sin2phi;
8819 	gmt_M_unused(GMT);
8820 
8821 	/* First deal with trivial cases */
8822 
8823 	if (lat >=  90.0) return ( 90.0);
8824 	if (lat <= -90.0) return (-90.0);
8825 	if (gmt_M_is_zero (lat)) return (0.0);
8826 
8827 	sincosd (2.0 * lat, &sin2phi, &cos2phi);
8828 
8829 	delta = sin2phi * (c[0] + cos2phi * (c[1] + cos2phi * (c[2] + cos2phi * c[3])));
8830 
8831 	return (lat + R2D * delta);
8832 }
8833 
8834 /*! . */
gmt_lat_swap(struct GMT_CTRL * GMT,double lat,int itype)8835 double gmt_lat_swap (struct GMT_CTRL *GMT, double lat, int itype) {
8836 	/* Return latitude, in degrees, given latitude, in degrees, based on itype */
8837 
8838 	double delta, cos2phi, sin2phi;
8839 
8840 	/* First deal with trivial cases */
8841 
8842 	if (lat >=  90.0) return ( 90.0);
8843 	if (lat <= -90.0) return (-90.0);
8844 	if (gmt_M_is_zero (lat)) return (0.0);
8845 
8846 	if (GMT->current.proj.lat_swap_vals.spherical) return (lat);
8847 
8848 	if (itype >= GMT_LATSWAP_N) {
8849 		/* This should never happen -?- or do we want to allow the
8850 			possibility of using itype = -1 to do nothing  */
8851 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_lat_swap(): Invalid choice, programming bug.\n");
8852 		return(lat);
8853 	}
8854 
8855 	sincosd (2.0 * lat, &sin2phi, &cos2phi);
8856 
8857 	delta = sin2phi * (GMT->current.proj.lat_swap_vals.c[itype][0]
8858 		+ cos2phi * (GMT->current.proj.lat_swap_vals.c[itype][1]
8859 		+ cos2phi * (GMT->current.proj.lat_swap_vals.c[itype][2]
8860 		+ cos2phi * GMT->current.proj.lat_swap_vals.c[itype][3])));
8861 
8862 	return (lat + R2D * delta);
8863 }
8864 
8865 /*! . */
gmtlib_scale_eqrad(struct GMT_CTRL * GMT)8866 void gmtlib_scale_eqrad (struct GMT_CTRL *GMT) {
8867 	/* Reinitialize GMT->current.proj.EQ_RAD to the appropriate value */
8868 
8869 	switch (GMT->current.proj.projection_GMT) {
8870 
8871 		/* Conformal projections */
8872 
8873 		case GMT_MERCATOR:
8874 		case GMT_TM:
8875 		case GMT_UTM:
8876 		case GMT_OBLIQUE_MERC:
8877 		case GMT_LAMBERT:
8878 		case GMT_STEREO:
8879 
8880 			GMT->current.proj.EQ_RAD = GMT->current.proj.lat_swap_vals.rm;
8881 			break;
8882 
8883 		/* Equal Area projections */
8884 
8885 		case GMT_LAMB_AZ_EQ:
8886 		case GMT_ALBERS:
8887 		case GMT_ECKERT4:
8888 		case GMT_ECKERT6:
8889 		case GMT_HAMMER:
8890 		case GMT_MOLLWEIDE:
8891 		case GMT_SINUSOIDAL:
8892 
8893 			GMT->current.proj.EQ_RAD = GMT->current.proj.lat_swap_vals.ra;
8894 			break;
8895 
8896 		default:	/* Keep EQ_RAD as is */
8897 			break;
8898 	}
8899 
8900 	/* Also reset dependencies of EQ_RAD */
8901 
8902 	GMT->current.proj.i_EQ_RAD = 1.0 / GMT->current.proj.EQ_RAD;
8903 	GMT->current.proj.M_PR_DEG = TWO_PI * GMT->current.proj.EQ_RAD / 360.0;
8904 	GMT->current.proj.KM_PR_DEG = GMT->current.proj.M_PR_DEG / METERS_IN_A_KM;
8905 }
8906 
8907 
8908 /*! . */
gmtlib_init_ellipsoid(struct GMT_CTRL * GMT)8909 void gmtlib_init_ellipsoid (struct GMT_CTRL *GMT) {
8910 	double f;
8911 
8912 	/* Set up ellipsoid parameters for the selected ellipsoid since gmt.conf could have changed them */
8913 
8914 	f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
8915 	GMT->current.proj.ECC2 = 2.0 * f - f * f;
8916 	GMT->current.proj.ECC4 = GMT->current.proj.ECC2 * GMT->current.proj.ECC2;
8917 	GMT->current.proj.ECC6 = GMT->current.proj.ECC2 * GMT->current.proj.ECC4;
8918 	GMT->current.proj.one_m_ECC2 = 1.0 - GMT->current.proj.ECC2;
8919 	GMT->current.proj.i_one_m_ECC2 = 1.0 / GMT->current.proj.one_m_ECC2;
8920 	GMT->current.proj.ECC = d_sqrt (GMT->current.proj.ECC2);
8921 	GMT->current.proj.half_ECC = 0.5 * GMT->current.proj.ECC;
8922 	if (GMT->current.proj.ECC != 0) { /* avoid division by 0 */
8923 		GMT->current.proj.i_half_ECC = 0.5 / GMT->current.proj.ECC;	/* Only used in inverse Alberts when e > 0 anyway */
8924 	}
8925 	GMT->current.proj.EQ_RAD = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius;
8926 	GMT->current.proj.i_EQ_RAD = 1.0 / GMT->current.proj.EQ_RAD;
8927 
8928 	/* Spherical degrees to m or km */
8929 	GMT->current.proj.mean_radius = gmtmap_mean_radius (GMT, GMT->current.proj.EQ_RAD, f);
8930 	GMT->current.proj.M_PR_DEG = TWO_PI * GMT->current.proj.mean_radius / 360.0;
8931 	GMT->current.proj.KM_PR_DEG = GMT->current.proj.M_PR_DEG / METERS_IN_A_KM;
8932 	GMT->current.proj.DIST_M_PR_DEG = GMT->current.proj.M_PR_DEG;
8933 	GMT->current.proj.DIST_KM_PR_DEG = GMT->current.proj.KM_PR_DEG;
8934 
8935 	/* Compute coefficients needed for auxiliary latitude conversions */
8936 	gmtmap_lat_swap_init (GMT);
8937 }
8938 
8939 /*! . */
gmtlib_init_geodesic(struct GMT_CTRL * GMT)8940 void gmtlib_init_geodesic (struct GMT_CTRL *GMT) {
8941 	switch (GMT->current.setting.proj_geodesic) {
8942 		case GMT_GEODESIC_VINCENTY:
8943 			GMT->current.map.geodesic_meter = gmtmap_vincenty_dist_meter;
8944 			GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_vincenty;
8945 			break;
8946 		case GMT_GEODESIC_ANDOYER:
8947 			GMT->current.map.geodesic_meter = gmtmap_andoyer_dist_meter;
8948 			GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_vincenty;	/* This may change later */
8949 			break;
8950 		case GMT_GEODESIC_RUDOE:
8951 			GMT->current.map.geodesic_meter = gmtmap_rudoe_dist_meter;
8952 			GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_rudoe;
8953 			break;
8954 		default:
8955 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "The PROJ_GEODESIC is not set! - use Vincenty\n");
8956 			GMT->current.setting.proj_geodesic = GMT_GEODESIC_VINCENTY;
8957 			GMT->current.map.geodesic_meter = gmtmap_vincenty_dist_meter;
8958 			GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_vincenty;
8959 			break;
8960 	}
8961 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "The PROJ_GEODESIC set to %s\n", GEOD_TEXT[GMT->current.setting.proj_geodesic]);
8962 }
8963 
8964 /* Datum conversion routines */
8965 
8966 /*! . */
gmt_datum_init(struct GMT_CTRL * GMT,struct GMT_DATUM * from,struct GMT_DATUM * to,bool heights)8967 void gmt_datum_init (struct GMT_CTRL *GMT, struct GMT_DATUM *from, struct GMT_DATUM *to, bool heights) {
8968 	/* Initialize datum conv structures based on the parsed values*/
8969 	unsigned int k;
8970 
8971 	GMT->current.proj.datum.h_given = heights;
8972 	gmt_M_memcpy (&GMT->current.proj.datum.from, from, 1, struct GMT_DATUM);
8973 	gmt_M_memcpy (&GMT->current.proj.datum.to,   to,   1, struct GMT_DATUM);
8974 
8975 	GMT->current.proj.datum.da = GMT->current.proj.datum.to.a - GMT->current.proj.datum.from.a;
8976 	GMT->current.proj.datum.df = GMT->current.proj.datum.to.f - GMT->current.proj.datum.from.f;
8977 	for (k = 0; k < 3; k++)
8978 		GMT->current.proj.datum.dxyz[k] = -(GMT->current.proj.datum.to.xyz[k] - GMT->current.proj.datum.from.xyz[k]);	/* Since the X, Y, Z are Deltas relative to WGS-84 */
8979 	GMT->current.proj.datum.one_minus_f = 1.0 - GMT->current.proj.datum.from.f;
8980 }
8981 
8982 /*! . */
gmt_ECEF_init(struct GMT_CTRL * GMT,struct GMT_DATUM * D)8983 void gmt_ECEF_init (struct GMT_CTRL *GMT, struct GMT_DATUM *D) {
8984 	/* Duplicate the parsed datum to the GMT from datum */
8985 	gmt_M_memcpy (&GMT->current.proj.datum.from, D, 1, struct GMT_DATUM);
8986 }
8987 
8988 /*! . */
gmt_set_datum(struct GMT_CTRL * GMT,char * text,struct GMT_DATUM * D)8989 int gmt_set_datum (struct GMT_CTRL *GMT, char *text, struct GMT_DATUM *D) {
8990 	int i;
8991 	double t;
8992 
8993 	if (text[0] == '\0' || text[0] == '-') {	/* Shortcut for WGS-84 */
8994 		if ((i = gmt_get_ellipsoid (GMT, "WGS-84")) >= 0) {	/* For Coverity's benefit */
8995 			gmt_M_memset (D->xyz, 3, double);
8996 			D->a = GMT->current.setting.ref_ellipsoid[i].eq_radius;
8997 			D->f = GMT->current.setting.ref_ellipsoid[i].flattening;
8998 			D->ellipsoid_id = i;
8999 		}
9000 	}
9001 	else if (strchr (text, ':')) {	/* Has colons, must get ellipsoid and dr separately */
9002 		char ellipsoid[GMT_LEN256] = {""}, dr[GMT_LEN256] = {""};
9003 		if (sscanf (text, "%[^:]:%s", ellipsoid, dr) != 2) {
9004 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed <ellipsoid>:<dr> argument!\n");
9005 			return (GMT_NOTSET);
9006 		}
9007 		if (sscanf (dr, "%lf,%lf,%lf", &D->xyz[GMT_X], &D->xyz[GMT_Y], &D->xyz[GMT_Z]) != 3) {
9008 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed <x>,<y>,<z> argument!\n");
9009 			return (GMT_NOTSET);
9010 		}
9011 		if ((i = gmt_get_ellipsoid (GMT, ellipsoid)) >= 0) {	/* This includes looking for format <a>,<1/f> */
9012 			D->a = GMT->current.setting.ref_ellipsoid[i].eq_radius;
9013 			D->f = GMT->current.setting.ref_ellipsoid[i].flattening;
9014 			D->ellipsoid_id = i;
9015 		}
9016 		else {
9017 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Ellipsoid %s not recognized!\n", ellipsoid);
9018 			return (GMT_NOTSET);
9019 		}
9020 	}
9021 	else {		/* Gave a Datum ID tag [ 0-(GMT_N_DATUMS-1)] */
9022 		int k;
9023 		if (sscanf (text, "%d", &i) != 1) {
9024 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed or unrecognized <datum> argument (%s)!\n", text);
9025 			return (GMT_NOTSET);
9026 		}
9027 		if (i < 0 || i >= GMT_N_DATUMS) {
9028 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Datum ID (%d) outside valid range (0-%d)!\n", i, GMT_N_DATUMS-1);
9029 			return (GMT_NOTSET);
9030 		}
9031 		if ((k = gmt_get_ellipsoid (GMT, GMT->current.setting.proj_datum[i].ellipsoid)) < 0) {	/* This should not happen... */
9032 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Ellipsoid %s not recognized!\n", GMT->current.setting.proj_datum[i].ellipsoid);
9033 			return (GMT_NOTSET);
9034 		}
9035 		D->a = GMT->current.setting.ref_ellipsoid[k].eq_radius;
9036 		D->f = GMT->current.setting.ref_ellipsoid[k].flattening;
9037 		D->ellipsoid_id = k;
9038 		for (k = 0; k < 3; k++) D->xyz[k] = GMT->current.setting.proj_datum[i].xyz[k];
9039 	}
9040 	D->b = D->a * (1 - D->f);
9041 	D->e_squared = 2 * D->f - D->f * D->f;
9042 	t = D->a /D->b;
9043 	D->ep_squared = t * t - 1.0;	/* (a^2 - b^2)/b^2 */
9044 	return 0;
9045 }
9046 
9047 /*! Compute the Abridged Molodensky transformation (3 parametrs). */
gmt_conv_datum(struct GMT_CTRL * GMT,double in[],double out[])9048 void gmt_conv_datum (struct GMT_CTRL *GMT, double in[], double out[]) {
9049 	/* Evaluate J^-1 and B on from ellipsoid */
9050 	/* Based on Standard Molodensky Datum Conversion, implemented from
9051 	 * http://www.colorado.edu/geography/gcraft/notes/datum/gif/molodens.gif */
9052 
9053 	double sin_lon, cos_lon, sin_lat, cos_lat, sin_lat2, M, N, h, tmp_1, tmp_2, tmp_3;
9054 	double delta_lat, delta_lon, delta_h, sc_lat;
9055 
9056 	h = (GMT->current.proj.datum.h_given) ? in[GMT_Z] : 0.0;
9057 	sincosd (in[GMT_X], &sin_lon, &cos_lon);
9058 	sincosd (in[GMT_Y], &sin_lat, &cos_lat);
9059 	sin_lat2 = sin_lat * sin_lat;
9060 	sc_lat = sin_lat * cos_lat;
9061 	M = GMT->current.proj.datum.from.a * (1.0 - GMT->current.proj.datum.from.e_squared) / pow (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat2, 1.5);
9062 	N = GMT->current.proj.datum.from.a / sqrt (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat2);
9063 
9064 	tmp_1 = -GMT->current.proj.datum.dxyz[GMT_X] * sin_lat * cos_lon - GMT->current.proj.datum.dxyz[GMT_Y] * sin_lat * sin_lon + GMT->current.proj.datum.dxyz[GMT_Z] * cos_lat;
9065 	tmp_2 = GMT->current.proj.datum.da * (N * GMT->current.proj.datum.from.e_squared * sc_lat) / GMT->current.proj.datum.from.a;
9066 	tmp_3 = GMT->current.proj.datum.df * (M / GMT->current.proj.datum.one_minus_f + N * GMT->current.proj.datum.one_minus_f) * sc_lat;
9067 	delta_lat = (tmp_1 + tmp_2 + tmp_3) / (M + h);
9068 
9069 	delta_lon = (-GMT->current.proj.datum.dxyz[GMT_X] * sin_lon + GMT->current.proj.datum.dxyz[GMT_Y] * cos_lon) / ((N + h) * cos_lat);
9070 
9071 	tmp_1 = GMT->current.proj.datum.dxyz[GMT_X] * cos_lat * cos_lon + GMT->current.proj.datum.dxyz[GMT_Y] * cos_lat * sin_lon + GMT->current.proj.datum.dxyz[GMT_Z] * sin_lat;
9072 	tmp_2 = -GMT->current.proj.datum.da * GMT->current.proj.datum.from.a / N;
9073 	tmp_3 = GMT->current.proj.datum.df * GMT->current.proj.datum.one_minus_f * N * sin_lat2;
9074 	delta_h = tmp_1 + tmp_2 + tmp_3;
9075 
9076 	out[GMT_X] = in[GMT_X] + delta_lon * R2D;
9077 	out[GMT_Y] = in[GMT_Y] + delta_lat * R2D;
9078 	if (GMT->current.proj.datum.h_given) out[GMT_Z] = in[GMT_Z] + delta_h;
9079 }
9080 
9081 /*! . */
gmt_ECEF_forward(struct GMT_CTRL * GMT,double in[],double out[])9082 void gmt_ECEF_forward (struct GMT_CTRL *GMT, double in[], double out[]) {
9083 	/* Convert geodetic lon, lat, height to ECEF coordinates given the datum parameters.
9084 	 * GMT->current.proj.datum.from is always the ellipsoid to use */
9085 
9086 	double sin_lon, cos_lon, sin_lat, cos_lat, N, tmp;
9087 
9088 	sincosd (in[GMT_X], &sin_lon, &cos_lon);
9089 	sincosd (in[GMT_Y], &sin_lat, &cos_lat);
9090 
9091 	N = GMT->current.proj.datum.from.a / d_sqrt (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat * sin_lat);
9092 	tmp = (N + in[GMT_Z]) * cos_lat;
9093 	out[GMT_X] = tmp * cos_lon + GMT->current.proj.datum.from.xyz[GMT_X];
9094 	out[GMT_Y] = tmp * sin_lon + GMT->current.proj.datum.from.xyz[GMT_Y];
9095 	out[GMT_Z] = (N * (1 - GMT->current.proj.datum.from.e_squared) + in[GMT_Z]) * sin_lat + GMT->current.proj.datum.from.xyz[GMT_Z];
9096 }
9097 
9098 /*! . */
gmt_ECEF_inverse(struct GMT_CTRL * GMT,double in[],double out[])9099 void gmt_ECEF_inverse (struct GMT_CTRL *GMT, double in[], double out[]) {
9100 	/* Convert ECEF coordinates to geodetic lon, lat, height given the datum parameters.
9101 	 * GMT->current.proj.datum.from is always the ellipsoid to use */
9102 
9103 	unsigned int i;
9104 	double in_p[3], sin_lat, cos_lat, N, p, theta, sin_theta, cos_theta;
9105 
9106 	/* First remove the xyz shifts, us in_p to avoid changing in */
9107 	for (i = 0; i < 3; i++) in_p[i] = in[i] - GMT->current.proj.datum.from.xyz[i];
9108 
9109 	p = hypot (in_p[GMT_X], in_p[GMT_Y]);
9110 	theta = atan (in_p[GMT_Z] * GMT->current.proj.datum.from.a / (p * GMT->current.proj.datum.from.b));
9111 	sincos (theta, &sin_theta, &cos_theta);
9112 	out[GMT_X] = d_atan2d (in_p[GMT_Y], in_p[GMT_X]);
9113 	out[GMT_Y] = atand ((in_p[GMT_Z] + GMT->current.proj.datum.from.ep_squared * GMT->current.proj.datum.from.b * pow (sin_theta, 3.0)) / (p - GMT->current.proj.datum.from.e_squared * GMT->current.proj.datum.from.a * pow (cos_theta, 3.0)));
9114 	sincosd (out[GMT_Y], &sin_lat, &cos_lat);
9115 	N = GMT->current.proj.datum.from.a / sqrt (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat * sin_lat);
9116 	out[GMT_Z] = (p / cos_lat) - N;
9117 }
9118 
9119 #if 0
9120 /*! Convert ECEF coordinates to geodetic lon, lat, height given the 'to' parameters. Used in 7 params Bursa-Wolf transform */
9121 void gmt_ECEF_inverse_dest_datum (struct GMT_CTRL *GMT, double in[], double out[]) {
9122 	double sin_lat, cos_lat, N, p, theta, sin_theta, cos_theta;
9123 
9124 	p = hypot (in[GMT_X], in[GMT_Y]);
9125 	theta = atan (in[GMT_Z] * GMT->current.proj.datum.to.a / (p * GMT->current.proj.datum.to.b));
9126 	sincos (theta, &sin_theta, &cos_theta);
9127 	out[GMT_X] = d_atan2d (in[GMT_Y], in[GMT_X]);
9128 	out[GMT_Y] = atand ((in[GMT_Z] + GMT->current.proj.datum.to.ep_squared * GMT->current.proj.datum.to.b * pow (sin_theta, 3.0)) / (p - GMT->current.proj.datum.to.e_squared * GMT->current.proj.datum.to.a * pow (cos_theta, 3.0)));
9129 	sincosd (out[GMT_Y], &sin_lat, &cos_lat);
9130 	N = GMT->current.proj.datum.to.a / sqrt (1.0 - GMT->current.proj.datum.to.e_squared * sin_lat * sin_lat);
9131 	out[GMT_Z] = (p / cos_lat) - N;
9132 }
9133 #endif
9134 
9135 /*! . */
gmt_line_length(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,bool project)9136 double gmt_line_length (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, bool project) {
9137 	/* Returns distance of line in units set by GMT_distaz. It bypassed points where x and/or y are NaN. */
9138 	uint64_t this_p, prev;
9139 	bool xy_not_NaN;
9140 	double cum_dist = 0.0, xp0 = 0, xp1, yp0 = 0, yp1;
9141 
9142 	if (n == 0) return 0.0;
9143 	if (project) gmt_geo_to_xy (GMT, x[0], y[0], &xp0, &yp0);
9144 	for (this_p = 1, prev = 0; this_p < n; this_p++) {
9145 		xy_not_NaN = !(gmt_M_is_dnan (x[this_p]) || gmt_M_is_dnan (y[this_p]));
9146 		if (xy_not_NaN)	{	/* safe to calculate inc */
9147 			if (project) {
9148 				gmt_geo_to_xy (GMT, x[this_p], y[this_p], &xp1, &yp1);
9149 				cum_dist += hypot (xp0 - xp1, yp0 - yp1);
9150 				xp0 = xp1;	yp0 = yp1;
9151 			}
9152 			else
9153 				cum_dist += gmt_distance (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9154 			prev = this_p;	/* This was a record with OK x,y; make it the previous point for distance calculations */
9155 		}
9156 	}
9157 	if (project) cum_dist *= GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
9158 
9159 	return (cum_dist);
9160 }
9161 
9162 /*! . */
gmt_dist_array(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,bool cumulative)9163 double *gmt_dist_array (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, bool cumulative) {
9164 	/* Returns distances in units set by GMT_distaz. It bypassed points where x and/or y are NaN.
9165 	 * If cumulative is false we just return the increments; otherwise we add up distances */
9166 	uint64_t this_p, prev;
9167 	bool xy_not_NaN;
9168 	double *d = NULL, cum_dist = 0.0, inc = 0.0;
9169 
9170 	if (n == 0) return (NULL);
9171 	d = gmt_M_memory (GMT, NULL, n, double);
9172 	if (gmt_M_is_dnan (x[0]) || gmt_M_is_dnan (y[0])) d[0] = GMT->session.d_NaN;
9173 	for (this_p = 1, prev = 0; this_p < n; this_p++) {
9174 		xy_not_NaN = !(gmt_M_is_dnan (x[this_p]) || gmt_M_is_dnan (y[this_p]));
9175 		if (xy_not_NaN) {	/* safe to calculate inc */
9176 			inc = gmt_distance (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9177 			if (cumulative) {
9178 				cum_dist += inc;
9179 				d[this_p] = cum_dist;
9180 			}
9181 			else
9182 				d[this_p] = inc;
9183 		}
9184 		else
9185 			d[this_p] = GMT->session.d_NaN;
9186 
9187 		if (xy_not_NaN) prev = this_p;	/* This was a record with OK x,y; make it the previous point for distance calculations */
9188 	}
9189 	return (d);
9190 }
9191 
9192 /*! . */
gmt_dist_array_2(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,double scale,int dist_flag)9193 double * gmt_dist_array_2 (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, double scale, int dist_flag) {
9194 	/* Returns distances in meter; use scale to get other units */
9195 	uint64_t this_p, prev;
9196 	bool cumulative = true, do_scale, xy_not_NaN;
9197 	double *d = NULL, cum_dist = 0.0, inc = 0.0;
9198 
9199 	if (dist_flag < 0) {	/* Want increments and not cumulative distances */
9200 		dist_flag = abs (dist_flag);
9201 		cumulative = false;
9202 	}
9203 
9204 	if (dist_flag < 0 || dist_flag > 3) return (NULL);
9205 
9206 	do_scale = (scale != 1.0);
9207 	d = gmt_M_memory (GMT, NULL, n, double);
9208 	if (gmt_M_is_dnan (x[0]) || gmt_M_is_dnan (y[0])) d[0] = GMT->session.d_NaN;
9209 	for (this_p = 1, prev = 0; this_p < n; this_p++) {
9210 		xy_not_NaN = !(gmt_M_is_dnan (x[this_p]) || gmt_M_is_dnan (y[this_p]));
9211 		if (xy_not_NaN) {	/* safe to calculate inc */
9212 			switch (dist_flag) {
9213 
9214 				case 0:	/* Cartesian distances */
9215 
9216 					inc = hypot (x[this_p] - x[prev], y[this_p] - y[prev]);
9217 					break;
9218 
9219 				case 1:	/* Flat earth distances in meter */
9220 
9221 					inc = gmtmap_flatearth_dist_meter (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9222 					break;
9223 
9224 				case 2:	/* Great circle distances in meter */
9225 
9226 					inc = gmt_great_circle_dist_meter (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9227 					break;
9228 
9229 				case 3:	/* Geodesic distances in meter */
9230 
9231 					inc = (*GMT->current.map.geodesic_meter) (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9232 					break;
9233 			}
9234 
9235 			if (do_scale) inc *= scale;
9236 			if (cumulative) cum_dist += inc;
9237 			d[this_p] = (cumulative) ? cum_dist : inc;
9238 		}
9239 		else
9240 			d[this_p] = GMT->session.d_NaN;
9241 
9242 		if (xy_not_NaN) prev = this_p;	/* This was a record with OK x,y; make it the previous point for distance calculations */
9243 	}
9244 	return (d);
9245 }
9246 
9247 /*! . */
gmtlib_map_latcross(struct GMT_CTRL * GMT,double lat,double west,double east,struct GMT_XINGS ** xings)9248 unsigned int gmtlib_map_latcross (struct GMT_CTRL *GMT, double lat, double west, double east, struct GMT_XINGS **xings) {
9249 	bool go = false;
9250 	unsigned int i, nx, nc = 0;
9251 	size_t n_alloc = GMT_SMALL_CHUNK;
9252 	double lon, lon_old, this_x, this_y, last_x, last_y, xlon[2], xlat[2], gap;
9253 	struct GMT_XINGS *X = NULL;
9254 
9255 	if (gmt_M_is_conical (GMT) && gmt_M_360_range (west, east)) {	/* Special case since 360 longitudes do not form a circle but a pacman shape */
9256 		X = gmt_M_memory (GMT, NULL, 1U, struct GMT_XINGS);
9257 		X[0].nx = 2;	/* Will cut both east and west repeated boundaries */
9258 		/* Do west boundary */
9259 		gmt_geo_to_xy (GMT, west, lat, &X[0].xx[0], &X[0].yy[0]);
9260 		X[0].angle[0] = gmtmap_get_angle (GMT, west, GMT->common.R.wesn[YLO], west, GMT->common.R.wesn[YHI]) + 90.0;	/* Get angle of west boundary and add 90 */
9261 		X[0].sides[0] = W_SIDE;
9262 		/* Do east boundary */
9263 		gmt_geo_to_xy (GMT, east, lat, &X[0].xx[1], &X[0].yy[1]);
9264 		X[0].angle[1] = gmtmap_get_angle (GMT, east, GMT->common.R.wesn[YLO], east, GMT->common.R.wesn[YHI]) - 90.0;	/* Get angle of east boundary and subtract 90 */
9265 		X[0].sides[1] = E_SIDE;
9266 		*xings = X;
9267 		return 1;	/* Done here, returning array with single GMT_XINGS structure holding two crossings */
9268 	}
9269 
9270 	/* Remaining (general) cases */
9271 
9272 	X = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_XINGS);
9273 
9274 	lon_old = west - 2.0 * GMT_CONV4_LIMIT;
9275 	gmt_map_outside (GMT, lon_old, lat);
9276 	gmt_geo_to_xy (GMT, lon_old, lat, &last_x, &last_y);
9277 	for (i = 1; i <= GMT->current.map.n_lon_nodes; i++) {
9278 		lon = (i == GMT->current.map.n_lon_nodes) ? east + 2.0 * GMT_CONV4_LIMIT : west + i * GMT->current.map.dlon;
9279 		gmt_map_outside (GMT, lon, lat);
9280 		gmt_geo_to_xy (GMT, lon, lat, &this_x, &this_y);
9281 		if ((nx = gmtmap_crossing (GMT, lon_old, lat, lon, lat, xlon, xlat, X[nc].xx, X[nc].yy, X[nc].sides))) {
9282 			X[nc].angle[0] = gmtmap_get_angle (GMT, lon_old, lat, lon, lat);	/* Get angle at first crossing */
9283 			if (nx == 2) X[nc].angle[1] = X[nc].angle[0] + 180.0;	/* If a 2nd crossing it must be really close so just add 180 */
9284 			if (GMT->current.map.corner > 0) {
9285 				X[nc].sides[0] = (GMT->current.map.corner%4 > 1) ? 1 : 3;
9286 				if (GMT->current.proj.got_azimuths) X[nc].sides[0] = (X[nc].sides[0] + 2) % 4;
9287 				GMT->current.map.corner = 0;
9288 			}
9289 			gmt_M_memcpy (X[nc].lon, xlon, 2U, double);
9290 			gmt_M_memcpy (X[nc].lat, xlat, 2U, double);
9291 		}
9292 		else if (GMT->current.map.is_world)	/* Deal with possibility of wrapping around 360 */
9293 			nx = (*GMT->current.map.wrap_around_check) (GMT, X[nc].angle, last_x, last_y, this_x, this_y, X[nc].xx, X[nc].yy, X[nc].sides);
9294 		if (nx == 2 && fabs (fabs (X[nc].xx[1] - X[nc].xx[0]) - GMT->current.map.width) < GMT_CONV4_LIMIT && !GMT->current.map.is_world)
9295 			/* I assume this means if we have a crossing and both left and right and not worldmap then skip as something went wrong? */
9296 			go = false;
9297 		else if (nx == 2 && (gap = fabs (X[nc].yy[1] - X[nc].yy[0])) > GMT_CONV4_LIMIT && fabs (gap - GMT->current.map.height) < GMT_CONV4_LIMIT && !GMT->current.map.is_world_tm)
9298 			/* I assume this means if we have a crossing and both top and bottom and it is not a global UTM map then skip as something went wrong? */
9299 			go = false;
9300 		else if (nx > 0)
9301 			go = true;
9302 		if (go) {
9303 			X[nc].nx = nx;
9304 			nc++;
9305 			if (nc == n_alloc) {
9306 				n_alloc <<= 1;
9307 				X = gmt_M_memory (GMT, X, n_alloc, struct GMT_XINGS);
9308 			}
9309 			go = false;
9310 		}
9311 		lon_old = lon;
9312 		last_x = this_x;	last_y = this_y;
9313 	}
9314 
9315 	if (nc > 0) {
9316 		X = gmt_M_memory (GMT, X, nc, struct GMT_XINGS);
9317 		*xings = X;
9318 	}
9319 	else
9320 		gmt_M_free (GMT, X);
9321 
9322 	return (nc);
9323 }
9324 
9325 /*! . */
gmtlib_map_loncross(struct GMT_CTRL * GMT,double lon,double south,double north,struct GMT_XINGS ** xings)9326 unsigned int gmtlib_map_loncross (struct GMT_CTRL *GMT, double lon, double south, double north, struct GMT_XINGS **xings) {
9327 	bool go = false;
9328 	unsigned int j, nx, nc = 0;
9329 	size_t n_alloc = GMT_SMALL_CHUNK;
9330 	double lat, lat_old, this_x, this_y, last_x, last_y, xlon[2], xlat[2], gap;
9331 	struct GMT_XINGS *X = NULL;
9332 
9333 	X = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_XINGS);
9334 
9335 	lat_old = ((south - (2.0 * GMT_CONV4_LIMIT)) >= -90.0) ? south - 2.0 * GMT_CONV4_LIMIT : south;	/* Outside */
9336 	if ((north + 2.0 * GMT_CONV4_LIMIT) <= 90.0) north += 2.0 * GMT_CONV4_LIMIT;
9337 	gmt_map_outside (GMT, lon, lat_old);
9338 	gmt_geo_to_xy (GMT, lon, lat_old, &last_x, &last_y);
9339 	for (j = 1; j <= GMT->current.map.n_lat_nodes; j++) {
9340 		lat = (j == GMT->current.map.n_lat_nodes) ? north: south + j * GMT->current.map.dlat;
9341 		gmt_map_outside (GMT, lon, lat);
9342 		gmt_geo_to_xy (GMT, lon, lat, &this_x, &this_y);
9343 		if ((nx = gmtmap_crossing (GMT, lon, lat_old, lon, lat, xlon, xlat, X[nc].xx, X[nc].yy, X[nc].sides))) {
9344 			X[nc].angle[0] = gmtmap_get_angle (GMT, lon, lat_old, lon, lat);	/* Get angle at first crossing */
9345 			if (nx == 2) X[nc].angle[1] = X[nc].angle[0] + 180.0;	/* If a 2nd crossing it must be really close so just add 180 */
9346 			if (GMT->current.map.corner > 0) {
9347 				X[nc].sides[0] = (GMT->current.map.corner < 3) ? 0 : 2;
9348 				GMT->current.map.corner = 0;
9349 			}
9350 			gmt_M_memcpy (X[nc].lon, xlon, 2U, double);
9351 			gmt_M_memcpy (X[nc].lat, xlat, 2U, double);
9352 		}
9353 		else if (GMT->current.map.is_world)	/* Deal with possibility of wrapping around 360 */
9354 			nx = (*GMT->current.map.wrap_around_check) (GMT, X[nc].angle, last_x, last_y, this_x, this_y, X[nc].xx, X[nc].yy, X[nc].sides);
9355 		if (nx == 2 && fabs (fabs (X[nc].xx[1] - X[nc].xx[0]) - GMT->current.map.width) < GMT_CONV4_LIMIT && !GMT->current.map.is_world)
9356 			/* I assume this means if we have a crossing and both left and right and not worldmap then skip as something went wrong? */
9357 			go = false;
9358 		else if (nx == 2 && (gap = fabs (X[nc].yy[1] - X[nc].yy[0])) > GMT_CONV4_LIMIT && fabs (gap - GMT->current.map.height) < GMT_CONV4_LIMIT && !GMT->current.map.is_world_tm)
9359 			/* I assume this means if we have a crossing and both top and bottom and it is not a global UTM map then skip as something went wrong? */
9360 			go = false;
9361 		else if (nx > 0)
9362 			go = true;
9363 		if (go) {
9364 			X[nc].nx = nx;
9365 			nc++;
9366 			if (nc == n_alloc) {
9367 				n_alloc <<= 1;
9368 				X = gmt_M_memory (GMT, X, n_alloc, struct GMT_XINGS);
9369 			}
9370 			go = false;
9371 		}
9372 		lat_old = lat;
9373 		last_x = this_x;	last_y = this_y;
9374 	}
9375 
9376 	if (nc > 0) {
9377 		X = gmt_M_memory (GMT, X, nc, struct GMT_XINGS);
9378 		*xings = X;
9379 	}
9380 	else
9381 		gmt_M_free (GMT, X);
9382 
9383 	return (nc);
9384 }
9385 
gmtmap_reset_oblique_settings(struct GMT_CTRL * GMT)9386 void gmtmap_reset_oblique_settings (struct GMT_CTRL *GMT) {
9387 	/* The modern mode default for MAP_ANNOT_OBLIQUE has settings that only makes sense for oblique plots with rectangular borders.
9388 	 * Thus, if the current projection is not like that then we reset the default to a more benign default setting.  However, if
9389 	 * the user has actively changed the MAP_ANNOT_OBLIQUE value then we do nothing. */
9390 
9391 	if (GMT->current.setting.map_annot_oblique_set) return;	/* User changed MAP_ANNOT_OBLIQUE so we cannot reset anything */
9392 	if (GMT->common.R.oblique) return;	/* These are the appropriate defaults for oblique projections with rectangular borders */
9393 
9394 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reset MAP_ANNOT_OBLIQUE to anywhere\n");
9395 
9396 	GMT->current.setting.map_annot_oblique = GMT_OBL_ANNOT_ANYWHERE;	/* Reset to non-oblique map defaults */
9397 }
9398 
gmt_proj_setup(struct GMT_CTRL * GMT,double wesn[])9399 int gmt_proj_setup (struct GMT_CTRL *GMT, double wesn[]) {
9400 	/* Core map setup function for the projection.  All program needing projection support
9401 	 * will call this either directly or indirectly.  Programs that _just_ need to project
9402 	 * to/from may call this function only, while all the plotting program need to have more
9403 	 * things initialized and they will call gmt_map_setup instead, which starts by calling
9404 	 * gmt_proj_setup first. */
9405 
9406 	bool search = false;
9407 	int error = GMT_NOERROR;
9408 
9409 	if (wesn[XHI] == wesn[XLO] && wesn[YHI] == wesn[YLO]) Error_and_return (GMT_MAP_NO_REGION, GMT_NOT_A_VALID_DOMAIN);	/* Since -R may not be involved if there are grids */
9410 
9411 	if (!GMT->common.J.active) {
9412 		char *def_args[2] = {"X15c", "Q15c"};
9413 		unsigned int geo;
9414 		if (GMT->current.setting.run_mode == GMT_CLASSIC)	/* This is a fatal error in classic mode */
9415 			Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR);
9416 		if (!GMT->current.ps.active)
9417 			Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR);	/* Only auto-setup a projection for mapping and plots */
9418 		/* Here we are in modern mode starting a new plot without a map projection.  The rules says use -JQ15c (geo) or -JX15c (Cartesian) */
9419 		geo = gmt_M_is_geographic (GMT, GMT_IN);
9420 		gmt_parse_common_options (GMT, "J", 'J', def_args[geo]);
9421 		GMT->common.J.active = true;
9422 	}
9423 
9424 	gmtlib_init_ellipsoid (GMT);	/* Set parameters depending on the ellipsoid since the latter could have been set explicitly */
9425 
9426 	if (gmt_M_x_is_lon (GMT, GMT_IN)) {
9427 		/* Limit east-west range to 360 and make sure east > -180 and west < 360 */
9428 		if (!GMT->common.R.oblique || gmt_M_is_rect_graticule (GMT)) {	/* Only makes sense if not corner coordinates */
9429 			if (wesn[XHI] < wesn[XLO]) wesn[XHI] += 360.0;
9430 			if ((fabs (wesn[XHI] - wesn[XLO]) - 360.0) > GMT_CONV4_LIMIT) {
9431 				Error_and_return (GMT_MAP_EXCEEDS_360, GMT_NOT_A_VALID_DOMAIN);
9432 			}
9433 		}
9434 		while (wesn[XHI] < -180.0) {
9435 			wesn[XLO] += 360.0;
9436 			wesn[XHI] += 360.0;
9437 		}
9438 		while (wesn[XLO] > 360.0) {
9439 			wesn[XLO] -= 360.0;
9440 			wesn[XHI] -= 360.0;
9441 		}
9442 	}
9443 	else if (gmt_M_y_is_lon (GMT, GMT_IN)) {
9444 		/* Limit east-west range to 360 and make sure east > -180 and west < 360 */
9445 		if (wesn[YHI] < wesn[YLO]) wesn[YHI] += 360.0;
9446 		if ((fabs (wesn[YHI] - wesn[YLO]) - 360.0) > GMT_CONV4_LIMIT) {
9447 			Error_and_return (GMT_MAP_EXCEEDS_360, GMT_NOT_A_VALID_DOMAIN);
9448 		}
9449 		while (wesn[YHI] < -180.0) {
9450 			wesn[YLO] += 360.0;
9451 			wesn[YHI] += 360.0;
9452 		}
9453 		while (wesn[YLO] > 360.0) {
9454 			wesn[YLO] -= 360.0;
9455 			wesn[YHI] -= 360.0;
9456 		}
9457 	}
9458 	if (GMT->current.proj.got_elevations) {
9459 		if (wesn[YLO] < 0.0 || wesn[YLO] >= 90.0) Error_and_return (GMT_MAP_BAD_ELEVATION_MIN, GMT_NOT_A_VALID_DOMAIN);
9460 		if (wesn[YHI] <= 0.0 || wesn[YHI] > 90.0) Error_and_return (GMT_MAP_BAD_ELEVATION_MAX, GMT_NOT_A_VALID_DOMAIN);
9461 	}
9462 	if (gmt_M_y_is_lat (GMT, GMT_IN)) {
9463 		if (wesn[YLO] < -90.0 || wesn[YLO] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MIN, GMT_NOT_A_VALID_DOMAIN);
9464 		if (wesn[YHI] < -90.0 || wesn[YHI] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MAX, GMT_NOT_A_VALID_DOMAIN);
9465 	}
9466 	else if (gmt_M_x_is_lat (GMT, GMT_IN)) {
9467 		if (wesn[XLO] < -90.0 || wesn[XLO] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MIN, GMT_NOT_A_VALID_DOMAIN);
9468 		if (wesn[XHI] < -90.0 || wesn[XHI] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MAX, GMT_NOT_A_VALID_DOMAIN);
9469 	}
9470 
9471 	if (GMT->common.R.wesn != wesn)		/* In many cases they are both copies of same pointer */
9472 		gmt_M_memcpy (GMT->common.R.wesn, wesn, 4, double);
9473 	GMT->current.proj.GMT_convert_latitudes = false;
9474 	if (GMT->current.proj.gave_map_width) GMT->current.proj.units_pr_degree = false;
9475 
9476 	GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 0;
9477 	GMT->current.map.n_lon_nodes = GMT->current.map.n_lat_nodes = 0;
9478 	GMT->current.map.wrap_around_check = &gmtmap_wrap_around_check_x;
9479 	GMT->current.map.jump = &gmtmap_jump_x;
9480 	GMT->current.map.will_it_wrap = &gmtmap_will_it_wrap_x;
9481 #if 0
9482 	GMT->current.map.this_point_wraps = &gmtmap_this_point_wraps_x;
9483 #endif
9484 	GMT->current.map.get_crossings = &gmtmap_get_crossings_x;
9485 
9486 	GMT->current.map.lon_wrap = true;
9487 	GMT->current.map.lat_wrap = false;
9488 	GMT->current.map.lon_wrap_range = 360.0;
9489 
9490 	gmtmap_reset_oblique_settings (GMT);	/* Maybe reset MAP_ANNOT_OBLIQUE defaults depending on situation */
9491 
9492 	switch (GMT->current.proj.projection) {
9493 
9494 		case GMT_LINEAR:		/* Linear transformations */
9495 			error = gmtmap_init_linear (GMT, &search);
9496 			break;
9497 
9498 		case GMT_POLAR:		/* Both lon/lat are actually theta, radius */
9499 			error = gmtmap_init_polar (GMT, &search);
9500 			break;
9501 
9502 		case GMT_MERCATOR:		/* Standard Mercator projection */
9503 			error = gmtmap_init_merc (GMT, &search);
9504 			break;
9505 
9506 		case GMT_STEREO:		/* Stereographic projection */
9507 			error = gmtmap_init_stereo (GMT, &search);
9508 			break;
9509 
9510 		case GMT_LAMBERT:		/* Lambert Conformal Conic */
9511 			error = gmtmap_init_lambert (GMT, &search);
9512 			break;
9513 
9514 		case GMT_OBLIQUE_MERC:	/* Oblique Mercator */
9515 			error = gmtmap_init_oblique (GMT, &search);
9516 			break;
9517 
9518 		case GMT_TM:		/* Transverse Mercator */
9519 			error = gmtmap_init_tm (GMT, &search);
9520 			break;
9521 
9522 		case GMT_UTM:		/* Universal Transverse Mercator */
9523 			error = gmtmap_init_utm (GMT, &search);
9524 			break;
9525 
9526 		case GMT_LAMB_AZ_EQ:	/* Lambert Azimuthal Equal-Area */
9527 			error = gmtmap_init_lambeq (GMT, &search);
9528 			break;
9529 
9530 		case GMT_ORTHO:		/* Orthographic Projection */
9531 			error = gmtmap_init_ortho (GMT, &search);
9532 			break;
9533 
9534 		case GMT_GENPER:		/* General Perspective Projection */
9535 			error = gmtmap_init_genper (GMT, &search);
9536 			break;
9537 
9538 		case GMT_AZ_EQDIST:		/* Azimuthal Equal-Distance Projection */
9539 			error = gmtmap_init_azeqdist (GMT, &search);
9540 			break;
9541 
9542 		case GMT_GNOMONIC:		/* Azimuthal Gnomonic Projection */
9543 			error = gmtmap_init_gnomonic (GMT, &search);
9544 			break;
9545 
9546 		case GMT_MOLLWEIDE:		/* Mollweide Equal-Area */
9547 			error = gmtmap_init_mollweide (GMT, &search);
9548 			break;
9549 
9550 		case GMT_HAMMER:		/* Hammer-Aitoff Equal-Area */
9551 			error = gmtmap_init_hammer (GMT, &search);
9552 			break;
9553 
9554 		case GMT_VANGRINTEN:		/* Van der Grinten */
9555 			error = gmtmap_init_grinten (GMT, &search);
9556 			break;
9557 
9558 		case GMT_WINKEL:		/* Winkel Tripel */
9559 			error = gmtmap_init_winkel (GMT, &search);
9560 			break;
9561 
9562 		case GMT_ECKERT4:		/* Eckert IV */
9563 			error = gmtmap_init_eckert4 (GMT, &search);
9564 			break;
9565 
9566 		case GMT_ECKERT6:		/* Eckert VI */
9567 			error = gmtmap_init_eckert6 (GMT, &search);
9568 			break;
9569 
9570 		case GMT_CYL_EQ:		/* Cylindrical Equal-Area */
9571 			error = gmtmap_init_cyleq (GMT, &search);
9572 			break;
9573 
9574 		case GMT_CYL_STEREO:			/* Cylindrical Stereographic */
9575 			error = gmtmap_init_cylstereo (GMT, &search);
9576 			break;
9577 
9578 		case GMT_MILLER:		/* Miller Cylindrical */
9579 			error = gmtmap_init_miller (GMT, &search);
9580 			break;
9581 
9582 		case GMT_CYL_EQDIST:	/* Cylindrical Equidistant */
9583 			error = gmtmap_init_cyleqdist (GMT, &search);
9584 			break;
9585 
9586 		case GMT_ROBINSON:		/* Robinson */
9587 			error = gmtmap_init_robinson (GMT, &search);
9588 			break;
9589 
9590 		case GMT_SINUSOIDAL:	/* Sinusoidal Equal-Area */
9591 			error = gmtmap_init_sinusoidal (GMT, &search);
9592 			break;
9593 
9594 		case GMT_CASSINI:		/* Cassini cylindrical */
9595 			error = gmtmap_init_cassini (GMT, &search);
9596 			break;
9597 
9598 		case GMT_ALBERS:		/* Albers Equal-Area Conic */
9599 			error = gmtmap_init_albers (GMT, &search);
9600 			break;
9601 
9602 		case GMT_ECONIC:		/* Equidistant Conic */
9603 			error = gmtmap_init_econic (GMT, &search);
9604 			break;
9605 
9606 		case GMT_POLYCONIC:		/* Polyconic */
9607 			error = gmtmap_init_polyconic (GMT, &search);
9608 			break;
9609 
9610 #ifdef HAVE_GDAL
9611 		case GMT_PROJ4_PROJS:	/* All proj.4 projections */
9612 			error = map_init_proj4 (GMT, &search);
9613 			break;
9614 #endif
9615 
9616 		default:	/* No projection selected, return to a horrible death */
9617 			Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR);
9618 	}
9619 	if (error) return (GMT_PROJECTION_ERROR);	/* Something went wrong in one of the map_init_* functions */
9620 
9621 	if (GMT->current.proj.fwd == NULL)	/* Some error in projection projection parameters, return to a horrible death */
9622 		Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR);
9623 
9624 	GMT->current.proj.search = search;
9625 
9626 	GMT->current.proj.i_scale[GMT_X] = (GMT->current.proj.scale[GMT_X] != 0.0) ? 1.0 / GMT->current.proj.scale[GMT_X] : 1.0;
9627 	GMT->current.proj.i_scale[GMT_Y] = (GMT->current.proj.scale[GMT_Y] != 0.0) ? 1.0 / GMT->current.proj.scale[GMT_Y] : 1.0;
9628 	GMT->current.proj.i_scale[GMT_Z] = (GMT->current.proj.scale[GMT_Z] != 0.0) ? 1.0 / GMT->current.proj.scale[GMT_Z] : 1.0;
9629 
9630 	GMT->current.map.width  = fabs (GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]);
9631 	GMT->current.map.height = fabs (GMT->current.proj.rect[YHI] - GMT->current.proj.rect[YLO]);
9632 	GMT->current.map.half_width  = 0.5 * GMT->current.map.width;
9633 	GMT->current.map.half_height = 0.5 * GMT->current.map.height;
9634 
9635 	if (gmt_M_x_is_lon (GMT, GMT_IN)) {	/* x is longitude */
9636 		if (GMT->current.proj.central_meridian < GMT->common.R.wesn[XLO] && (GMT->current.proj.central_meridian + 360.0) <= GMT->common.R.wesn[XHI]) GMT->current.proj.central_meridian += 360.0;
9637 		if (GMT->current.proj.central_meridian > GMT->common.R.wesn[XHI] && (GMT->current.proj.central_meridian - 360.0) >= GMT->common.R.wesn[XLO]) GMT->current.proj.central_meridian -= 360.0;
9638 	}
9639 
9640 	if (!GMT->current.map.n_lon_nodes) GMT->current.map.n_lon_nodes = urint (GMT->current.map.width / GMT->current.setting.map_line_step);
9641 	if (!GMT->current.map.n_lat_nodes) GMT->current.map.n_lat_nodes = urint (GMT->current.map.height / GMT->current.setting.map_line_step);
9642 
9643 	error = gmtmap_init_three_D (GMT);
9644 
9645 	return (error);
9646 }
9647 
gmt_map_perimeter_search(struct GMT_CTRL * GMT,double * wesn,bool add_pad)9648 int gmt_map_perimeter_search (struct GMT_CTRL *GMT, double *wesn, bool add_pad) {
9649 	/* Loop around rectangular perimeter and determine min/max lon/lat extent.
9650 	 * If add_pad is true then we extend by 0.1 degrees (as per GMT historical settings) */
9651 	int error = GMT_NOERROR;
9652 	if (GMT->current.proj.projection_GMT == GMT_GENPER)	/* Need special considerations for this projection */
9653 		gmtmap_genper_search (GMT, &GMT->common.R.wesn[XLO], &GMT->common.R.wesn[XHI], &GMT->common.R.wesn[YLO], &GMT->common.R.wesn[YHI], add_pad);
9654 	else	/* Search along the projected border */
9655 		gmt_wesn_search (GMT, GMT->current.proj.rect[XLO], GMT->current.proj.rect[XHI], GMT->current.proj.rect[YLO], GMT->current.proj.rect[YHI], &GMT->common.R.wesn[XLO], &GMT->common.R.wesn[XHI], &GMT->common.R.wesn[YLO], &GMT->common.R.wesn[YHI], add_pad);
9656 	GMT->current.map.dlon = (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / GMT->current.map.n_lon_nodes;
9657 	GMT->current.map.dlat = (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / GMT->current.map.n_lat_nodes;
9658 	if (gmt_M_is_azimuthal(GMT) && GMT->common.R.oblique) {
9659 		if ((error = gmtmap_horizon_search (GMT, wesn[XLO], wesn[XHI], wesn[YLO], wesn[YHI], GMT->current.proj.rect[XLO], GMT->current.proj.rect[XHI], GMT->current.proj.rect[YLO], GMT->current.proj.rect[YHI])))
9660 			return (error);
9661 	}
9662 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_map_setup perimeter search region: %.16lg/%.16lg/%.16lg/%.16lg.\n",
9663 		GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
9664 	return (GMT_NOERROR);
9665 }
9666 
9667 /*! . */
gmt_map_setup(struct GMT_CTRL * GMT,double wesn[])9668 int gmt_map_setup (struct GMT_CTRL *GMT, double wesn[]) {
9669 	unsigned int i;
9670 	int error;
9671 	bool search, double_auto[6];
9672 	bool test_pole[2] = {true, true}, separate_intervals = false;
9673 	double scale, i_scale;
9674 
9675 	if ((error = gmt_proj_setup (GMT, wesn)) != GMT_NOERROR) goto gmt_map_setup_end;
9676 
9677 	gmt_set_undefined_defaults (GMT, sqrt (GMT->current.map.width * GMT->current.map.height), false);	/* We must change any undefined defaults given max plot dimension */
9678 
9679 	search = GMT->current.proj.search;
9680 
9681 	/* If intervals are not set specifically, round them to some "nice" values
9682 	 * Remember whether frame items in both directions were automatically set */
9683 	for (i = 0; i < 6; i++)
9684 		double_auto[i] = gmt_M_is_geographic (GMT, GMT_IN) && GMT->current.map.frame.set_both &&
9685 		GMT->current.map.frame.axis[GMT_X].item[i].active && GMT->current.map.frame.axis[GMT_X].item[i].interval == 0.0 &&
9686 		GMT->current.map.frame.axis[GMT_Y].item[i].active && GMT->current.map.frame.axis[GMT_Y].item[i].interval == 0.0;
9687 
9688 	gmt_auto_frame_interval (GMT, GMT_X, GMT_ANNOT_UPPER);
9689 	gmt_auto_frame_interval (GMT, GMT_Y, GMT_ANNOT_UPPER);
9690 	gmt_auto_frame_interval (GMT, GMT_Z, GMT_ANNOT_UPPER);
9691 	gmt_auto_frame_interval (GMT, GMT_X, GMT_ANNOT_LOWER);
9692 	gmt_auto_frame_interval (GMT, GMT_Y, GMT_ANNOT_LOWER);
9693 	gmt_auto_frame_interval (GMT, GMT_Z, GMT_ANNOT_LOWER);
9694 
9695 	/* Then check if one or both poles are inside map.  If they are then the longitude range will be 360
9696 	 * and depending on the latitude extent it may not make sense to use the coarse longitude-spacing determined
9697 	 * also for the latitude spacing.  We check this and if a pole is inside and if the latitude range is < 1/4
9698 	 * of that of the longitude (e.g., 90 degrees for a 0-360 global map) we go separate. */
9699 
9700 	if (GMT->current.proj.projection_GMT == GMT_AZ_EQDIST) {	/* Must be careful since if a pole equals an antipode we get NaNs as coordinates */
9701 		double x, y;
9702 		gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, -90.0, &x, &y);
9703 		if (gmt_M_is_dnan (x) && gmt_M_is_dnan (y)) test_pole[0] = false;
9704 		gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, +90.0, &x, &y);
9705 		if (gmt_M_is_dnan (x) && gmt_M_is_dnan (y)) test_pole[1] = false;
9706 	}
9707 	if (test_pole[0] && !gmt_map_outside (GMT, GMT->current.proj.central_meridian, -90.0)) GMT->current.proj.pole_in_map[0] = separate_intervals = true;
9708 	if (test_pole[1] && !gmt_map_outside (GMT, GMT->current.proj.central_meridian, +90.0)) GMT->current.proj.pole_in_map[1] = separate_intervals = true;
9709 	if (separate_intervals && (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) > 0.25)
9710 		separate_intervals = false;	/* E.g, for a whole global hemisphere we keep them the same, else we adjust individually */
9711 
9712 	/* Now set the pairs of automatically set intervals to be the same in both x- and y-direction, except when a geographic pole is inside an oblique map frame */
9713 	for (i = 0; i < 6; i++) {
9714 		if (double_auto[i] && !separate_intervals) GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval =
9715 		MAX (GMT->current.map.frame.axis[GMT_X].item[i].interval, GMT->current.map.frame.axis[GMT_Y].item[i].interval);
9716 	}
9717 
9718 	if (!GMT->current.map.n_lon_nodes) GMT->current.map.n_lon_nodes = urint (GMT->current.map.width / GMT->current.setting.map_line_step);
9719 	if (!GMT->current.map.n_lat_nodes) GMT->current.map.n_lat_nodes = urint (GMT->current.map.height / GMT->current.setting.map_line_step);
9720 
9721 	GMT->current.map.dlon = (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / GMT->current.map.n_lon_nodes;
9722 	GMT->current.map.dlat = (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / GMT->current.map.n_lat_nodes;
9723 
9724 	if (GMT->current.map.width > 400.0 && gmt_M_is_grdmapproject (GMT)) {	/* ***project calling with true scale, probably  */
9725 		search = false;	/* Safe-guard that prevents region search below for (map|grd)project and others (400 inch = ~> 10 meters) */
9726 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_map_setup perimeter search skipped when using true scale with grdproject or mapproject.\n");
9727 	}
9728 
9729 	if (search) {	/* Loop around rectangular perimeter and determine min/max lon/lat extent */
9730 		if ((error = gmt_map_perimeter_search (GMT, wesn, true)))
9731 			goto gmt_map_setup_end;
9732 	}
9733 
9734 	/* Maximum step size (in degrees) used for interpolation of line segments along great circles (or meridians/parallels)  before they are plotted */
9735 	GMT->current.map.path_step = GMT->current.setting.map_line_step / GMT->current.proj.scale[GMT_X] / GMT->current.proj.M_PR_DEG;
9736 
9737 	i_scale = 1.0 / (0.0254 * GMT->current.proj.scale[GMT_X]);
9738 	scale = 0.001 / (GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit] * GMT->current.proj.scale[GMT_X]);
9739 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Map scale is %g km per %s or 1:%g.\n",
9740 		scale, GMT->session.unit_name[GMT->current.setting.proj_length_unit], i_scale);
9741 
9742 gmt_map_setup_end:
9743 
9744 	if (error) GMT_Report(GMT->parent, GMT_MSG_ERROR, "General map projection error\n");
9745 	return (error);
9746 }
9747 
9748 /*! . */
gmt_init_distaz(struct GMT_CTRL * GMT,char unit,unsigned int mode,unsigned int type)9749 unsigned int gmt_init_distaz (struct GMT_CTRL *GMT, char unit, unsigned int mode, unsigned int type) {
9750 	/* Initializes distance calculation given the selected values for:
9751 	 * Distance unit: must be on of the following:
9752 	 *  1) d|e|f|k|m|M|n|s
9753 	 *  2) GMT (Cartesian distance after projecting with -J) | X (Cartesian)
9754 	 *  3) S (cosine distance) | P (cosine after first inverse projecting with -J)
9755 	 * distance-calculation modifier mode flags are:
9756 	 *   0 (Cartesian), 1 (flat Earth), 2 (great-circle [Default]), 3 (geodesic), 4 (loxodrome)
9757 	 *   However, if -j<mode> is set it takes precedence.
9758 	 * type: 0 = map distances, 1 = contour distances, 2 = contour annotation distances
9759 	 * We set distance and azimuth functions and scales for this type.
9760 	 * At the moment there is only one azimuth function pointer for all.
9761 	 *
9762 	 * The input args for gmt_init_distaz normally comes from calling gmt_get_distance.
9763 	 */
9764 
9765 	unsigned int proj_type = GMT_GEOGRAPHIC;	/* Default is to just use the geographic coordinates as they are */
9766 	int err = GMT_NOERROR;
9767 
9768 	if (strchr (GMT_LEN_UNITS, unit) && gmt_M_is_cartesian (GMT, GMT_IN)) {	/* Want geographic distance units but -fg (or -J) not set */
9769 		gmt_parse_common_options (GMT, "f", 'f', "g");
9770 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your distance unit (%c) implies geographic data; -fg has been set.\n", unit);
9771 	}
9772 
9773 	/* Determine if -j selected a particular mode */
9774 	if (gmt_M_is_geographic (GMT, GMT_IN) && GMT->common.j.active) {	/* User specified a -j setting */
9775 		static char *kind[5] = {"Cartesian", "Flat Earth", "Great Circle", "Geodesic", "Loxodrome"};
9776 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Spherical distance calculation mode: %s.\n", kind[GMT->common.j.active]);
9777 		if (mode != GMT->common.j.mode)	/* We override a selection due to deprecated leading -|+ signs before increment or radius */
9778 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your distance mode (%s) differs from your -j option (%s) which takes precedence.\n", kind[mode], kind[GMT->common.j.mode]);
9779 		mode = GMT->common.j.mode;	/* Override with what -j said */
9780 	}
9781 
9782 	switch (unit) {
9783 			/* First the three arc angular distance units */
9784 
9785 		case 'd':	/* Arc degrees on spherical body using desired metric mode */
9786 			err = gmtmap_set_distaz (GMT, GMT_DIST_DEG + mode, type, "arc-degree");
9787 			GMT->current.map.dist[type].arc = true;	/* Angular measure */
9788 			break;
9789 		case 'm':	/* Arc minutes on spherical body using desired metric mode */
9790 			err = gmtmap_set_distaz (GMT, GMT_DIST_DEG + mode, type, "arc-minute");
9791 			GMT->current.map.dist[type].scale = GMT_DEG2MIN_F;
9792 			GMT->current.map.dist[type].arc = true;	/* Angular measure */
9793 			break;
9794 		case 's':	/* Arc seconds on spherical body using desired metric mode */
9795 			err = gmtmap_set_distaz (GMT, GMT_DIST_DEG + mode, type, "arc-second");
9796 			GMT->current.map.dist[type].scale = GMT_DEG2SEC_F;
9797 			GMT->current.map.dist[type].arc = true;	/* Angular measure */
9798 			break;
9799 
9800 			/* Various distance units on the planetary body */
9801 
9802 		case 'e':	/* Meters on spherical body using desired metric mode */
9803 			err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "meter");
9804 			break;
9805 		case 'f':	/* Feet on spherical body using desired metric mode */
9806 			err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "foot");
9807 			GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_FOOT;
9808 			break;
9809 		case 'k':	/* Kilometers on spherical body using desired metric mode */
9810 			err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "km");
9811 			GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_KM;
9812 			break;
9813 		case 'M':	/* Statute Miles on spherical body using desired metric mode  */
9814 			err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "mile");
9815 			GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_MILE;
9816 			break;
9817 		case 'n':	/* Nautical miles on spherical body using desired metric mode */
9818 			err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "nautical mile");
9819 			GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_NAUTICAL_MILE;
9820 			break;
9821 		case 'u':	/* Survey feet on spherical body using desired metric mode */
9822 			err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "survey feet");
9823 			GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_SURVEY_FOOT;
9824 			break;
9825 
9826 			/* Cartesian distances.  Note: The X|C|R|Z|S|P 'units' are only passed internally and are not available as user selections directly */
9827 
9828 		case 'X':	/* Cartesian distances in user units */
9829 			proj_type = GMT_CARTESIAN;
9830 			if (GMT->common.n.periodic[GMT_X] || GMT->common.n.periodic[GMT_Y])
9831 				err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST_PERIODIC, type, "");
9832 			else
9833 				err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST, type, "");
9834 			break;
9835 		case 'C':	/* Cartesian distances (in PROJ_LENGTH_UNIT) after first projecting input coordinates with -J is still just GMT_CARTESIAN_DIST */
9836 			err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST, type, "");
9837 			proj_type = GMT_GEO2CART;
9838 			break;
9839 
9840 		case 'R':	/* Cartesian distances squared in user units */
9841 			proj_type = GMT_CARTESIAN;
9842 			err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST2, type, "");
9843 			break;
9844 		case 'Z':	/* Cartesian distances squared (in PROJ_LENGTH_UNIT^2) after first projecting input coordinates with -J */
9845 			err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST_PROJ2, type, "");
9846 			proj_type = GMT_GEO2CART;
9847 			break;
9848 
9849 			/* Specialized cosine distances used internally only (e.g., greenspline) */
9850 
9851 		case 'S':	/* Spherical cosine distances (for various gridding functions) */
9852 			err = gmtmap_set_distaz (GMT, GMT_DIST_COS + mode, type, "");
9853 			break;
9854 		case 'P':	/* Spherical distances after first inversely projecting Cartesian coordinates with -J */
9855 			err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST_PROJ_INV, type, "");
9856 			proj_type = GMT_CART2GEO;
9857 			break;
9858 
9859 		default:
9860 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Distance units must be one of %s\n", GMT_LEN_UNITS_DISPLAY);
9861 			err =  GMT_NOT_A_VALID_TYPE;
9862 			break;
9863 	}
9864 	if (err) return (err);
9865 	GMT->current.map.dist[type].init = true;	/* OK, we have now initialized the info for this type */
9866 	return (proj_type);
9867 }
9868 
9869 /*! . */
gmt_get_smallcircle(struct GMT_CTRL * GMT,double plon,double plat,double colat,uint64_t m)9870 struct GMT_DATASEGMENT * gmt_get_smallcircle (struct GMT_CTRL *GMT, double plon, double plat, double colat, uint64_t m) {
9871 	/* Function to generate m equidistant coordinates for an oblique small circle about a given pole.
9872 	 * To avoid some downstream problems we make sure that if circle crosses Greenwhich or Dateline that
9873 	 * we insert a point at lon = 0 and/or lon = 180. I imagine that in the worst-case scenario we could
9874 	 * cross those lines twice each, hence I allocate 4 more spaces than needed. */
9875 	double P[3], X[3], N[3], R[3][3], xlat, dlon, xx, yy, x, y, last_x = 0.0, last_y = 0.0, dx;
9876 	uint64_t k, n;
9877 	struct GMT_DATASEGMENT *S = NULL;
9878 	if (m < 2) return NULL;
9879 
9880 	S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, m+4, 2, NULL, NULL);	/* The output segment - allocate array space (+ 4 extra) */
9881 	plat = gmt_lat_swap (GMT, plat, GMT_LATSWAP_G2O);	/* Convert to geocentric coordinates */
9882 	gmt_geo_to_cart (GMT, plat, plon, P, true);		/* Get pole vector P */
9883 	xlat = (plat > 0.0) ? plat - colat : plat + colat;	/* Starting point along meridian through P but colat degrees away */
9884 	xlat = gmt_lat_swap (GMT, xlat, GMT_LATSWAP_G2O);	/* Convert to geocentric */
9885 	gmt_geo_to_cart (GMT, xlat, plon, X, true);		/* Generating vector X we will rotate about P */
9886 	dlon = 360.0 / (m - 1);					/* Point spacing along the small circle in oblique longitude degrees */
9887 	for (k = n = 0; k < m; k++, n++) {	/* Given how dlon was set we end up closing the polygon exactly */
9888 		gmt_make_rot_matrix2 (GMT, P, k * dlon, R);	/* Rotation matrix about P for this increment in oblique longitude */
9889 		gmt_matrix_vect_mult (GMT, 3U, R, X, N);	/* Rotate the X-vector to N */
9890 		gmt_cart_to_geo (GMT, &y, &x, N, true);		/* Recover lon,lat of rotated point */
9891 		y = gmt_lat_swap (GMT, y, GMT_LATSWAP_O2G);	/* Convert back to geodetic coordinates */
9892 		if (k) {
9893 			dx = x - last_x;
9894 			if (fabs (dx) > 180.0) {	/* Jump in longitudes */
9895 				dx = copysign (360.0 - fabs (dx), -dx);
9896 				xx = (fabs (last_x) > 270 || fabs(last_x) < 90.0) ? 0.0 : copysign (180.0, last_x);
9897 				yy = last_y + (y - last_y) * (xx - last_x) / dx;
9898 				S->data[GMT_X][n] = xx;	S->data[GMT_Y][n++] = yy;	/* Assign the extra element */
9899 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_get_smallcircle: Added extra point at %g/%g\n", xx, yy);
9900 			}
9901 			else if ((x > 0.0 && last_x < 0.0) || (x < 0.0 && last_x > 0.0)) {	/* Crossing Greenwhich */
9902 				xx = 0.0;
9903 				yy = last_y + (y - last_y) * (xx - last_x) / dx;
9904 				S->data[GMT_X][n] = xx;	S->data[GMT_Y][n++] = yy;	/* Assign the extra element */
9905 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_get_smallcircle: Added extra point at %g/%g\n", xx, yy);
9906 			}
9907 		}
9908 		S->data[GMT_X][n] = x;	S->data[GMT_Y][n] = y;	/* Assign the array elements */
9909 		last_x = x;	last_y = y;
9910 	}
9911 	S->data[GMT_X][n - 1] = S->data[GMT_X][0];		/* Make really sure the polygon is closed */
9912 	S->data[GMT_Y][n - 1] = S->data[GMT_Y][0];
9913 	S->n_rows = n;
9914 	gmt_set_seg_minmax (GMT, GMT_IS_POLY, 2, S);
9915 	return (S);	/* Pass out the results */
9916 }
9917 
gmtmap_ellipse_point(struct GMT_CTRL * GMT,double lon,double lat,double center,double sinp,double cosp,double major,double minor,double cos_azimuth,double sin_azimuth,double angle,double * plon,double * plat)9918 GMT_LOCAL void gmtmap_ellipse_point (struct GMT_CTRL *GMT, double lon, double lat, double center, double sinp, double cosp, double major, double minor, double cos_azimuth, double sin_azimuth, double angle, double *plon, double *plat) {
9919 	/* Lon, lat is center of ellipse, our point is making an angle with the major axis. */
9920 	double x, y, x_prime, y_prime, rho, c, sin_c, cos_c;
9921 	sincos (angle, &y, &x);
9922 	x *= major;	y *= minor;
9923 	/* Get rotated coordinates in m */
9924 	x_prime = x * cos_azimuth - y * sin_azimuth;
9925 	y_prime = x * sin_azimuth + y * cos_azimuth;
9926 	/* Convert m back to lon lat */
9927 	rho = hypot (x_prime, y_prime);
9928 	c = rho / GMT->current.proj.EQ_RAD;
9929 	sincos (c, &sin_c, &cos_c);
9930 	*plat = d_asind (cos_c * sinp + (y_prime * sin_c * cosp / rho));
9931 	if ((lat - 90.0) > -GMT_CONV8_LIMIT)	/* origin in Northern hemisphere */
9932 		*plon = lon + d_atan2d (x_prime, -y_prime);
9933 	else if ((lat + 90.0) < GMT_CONV8_LIMIT)	/* origin in Southern hemisphere */
9934 		*plon = lon + d_atan2d (x_prime, y_prime);
9935 	else
9936 		*plon = lon + d_atan2d (x_prime * sin_c, (rho * cosp * cos_c - y_prime * sinp * sin_c));
9937 	while ((*plon - center) < -180.0) *plon += 360.0;
9938 	while ((*plon - center) > +180.0) *plon -= 360.0;
9939 }
9940 
9941 #define GMT_ELLIPSE_APPROX 72
9942 
gmt_get_geo_ellipse(struct GMT_CTRL * GMT,double lon,double lat,double major_km,double minor_km,double azimuth,uint64_t m)9943 struct GMT_DATASEGMENT * gmt_get_geo_ellipse (struct GMT_CTRL *GMT, double lon, double lat, double major_km, double minor_km, double azimuth, uint64_t m) {
9944 	/* gmt_get_geo_ellipse takes the location, axes (in km), and azimuth of the ellipse's major axis
9945 	   and computes coordinates for an approximate ellipse using N-sided polygon.
9946 	   If m > 0 then we l\set N = m and use that many points.  Otherwise (m == 0), we will
9947 	   determine N by looking at the spacing between successive points for a trial N and
9948 	   then scale N up or down to satisfy the minimum point spacing criteria. */
9949 
9950 	uint64_t i, N;
9951 	double delta_azimuth, sin_azimuth, cos_azimuth, sinp, cosp, ax, ay, axx, ayy, bx, by, bxx, byy, L;
9952 	double major, minor, center, *px = NULL, *py = NULL;
9953 	char header[GMT_LEN256] = {""};
9954 	struct GMT_DATASEGMENT *S = NULL;
9955 
9956 	major = major_km * 500.0, minor = minor_km * 500.0;	/* Convert to meters (x1000) of semi-major (/2) and semi-minor axes */
9957 	/* Set up local azimuthal equidistant projection */
9958 	sincosd (90.0 - azimuth, &sin_azimuth, &cos_azimuth);
9959 	sincosd (lat, &sinp, &cosp);
9960 
9961 	center = (GMT->current.proj.central_meridian < GMT->common.R.wesn[XLO] || GMT->current.proj.central_meridian > GMT->common.R.wesn[XHI]) ? 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]) : GMT->current.proj.central_meridian;
9962 
9963 	if (m == 0) {	/* Determine N */
9964 		delta_azimuth = 2.0 * M_PI / GMT_ELLIPSE_APPROX;	/* Initial guess of angular spacing */
9965 		/* Compute distance between first two points and compare to map_line_step to determine angular spacing */
9966 		gmtmap_ellipse_point (GMT, lon, lat, center, sinp, cosp, major, minor, cos_azimuth, sin_azimuth, 0.0, &ax, &ay);
9967 		gmtmap_ellipse_point (GMT, lon, lat, center, sinp, cosp, major, minor, cos_azimuth, sin_azimuth, delta_azimuth, &bx, &by);
9968 		gmt_geo_to_xy (GMT, ax, ay, &axx, &ayy);
9969 		gmt_geo_to_xy (GMT, bx, by, &bxx, &byy);
9970 		L = hypot (axx - bxx, ayy - byy);
9971 		N = (uint64_t) irint (GMT_ELLIPSE_APPROX * L / GMT->current.setting.map_line_step);
9972 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Ellipse will be approximated by %d-sided polygon\n", N);
9973 		/* Approximate ellipse by a N-sided polygon */
9974 	}
9975 	else	/* Approximate ellipse by a m-sided polygon */
9976 		N = m;
9977 	delta_azimuth = 2.0 * M_PI / N;
9978 	/* Allocate datasetgment of the right size */
9979 	S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, N+1, 2, NULL, NULL);
9980 	px = S->data[GMT_X];	py = S->data[GMT_Y];
9981 
9982 	for (i = 0; i < N; i++)
9983 		gmtmap_ellipse_point (GMT, lon, lat, center, sinp, cosp, major, minor, cos_azimuth, sin_azimuth, i * delta_azimuth, &px[i], &py[i]);
9984 
9985 	/* Explicitly close the polygon */
9986 	px[N] = px[0], py[N] = py[0];
9987 	if (doubleAlmostEqual (major_km, minor_km))
9988 		snprintf (header, GMT_LEN256, "Circle around %g/%g with diameter %g km approximated by %" PRIu64 " points", lon, lat, major_km, N);
9989 	else
9990 		snprintf (header, GMT_LEN256, "Ellipse around %g/%g with major/minor axes %g/%g km and major axis azimuth %g approximated by %" PRIu64 " points", lon, lat, major_km, minor_km, azimuth, N);
9991 	S->header = strdup (header);
9992 	return (S);
9993 }
9994 
gmt_segment_BB_outside_map_BB(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)9995 bool gmt_segment_BB_outside_map_BB (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
9996 	/* Determine if the bounding box for this segment is completely outside the current map w/e/s/n region.
9997 	 * Note: If an oblique projection is selected then the gmt_wesn_search has updated the GMT->common.R.wesn
9998 	 * to be the bounding box of that oblique region. */
9999 
10000 	/* First check latitude or y-coordinates since they are straightforward to check */
10001 	if (S->min[GMT_Y] > GMT->common.R.wesn[YHI]) return true;	/* BB is above of our max region */
10002 	if (S->max[GMT_Y] < GMT->common.R.wesn[YLO]) return true;	/* BB is below of our max region */
10003 	if (gmt_M_x_is_lon (GMT, GMT_IN)) {	/* Must take periodicity of longitudes into account when checking if we are outside */
10004 		if ((S->min[GMT_X] > GMT->common.R.wesn[XHI]) && ((S->max[GMT_X] - 360.0) < GMT->common.R.wesn[XLO])) return true;	/* BB is to the right of our max region */
10005 		if ((S->max[GMT_X] < GMT->common.R.wesn[XLO]) && ((S->min[GMT_X] + 360.0) > GMT->common.R.wesn[XHI])) return true;	/* BB is to the left of our max region */
10006 	}
10007 	else {	/* Cartesian x is easy to check */
10008 		if (S->min[GMT_X] > GMT->common.R.wesn[XHI]) return true;	/* BB is to the right of our max region */
10009 		if (S->max[GMT_X] < GMT->common.R.wesn[XLO]) return true;	/* BB is to the left of our max region */
10010 
10011 	}
10012 	return false;
10013 }
10014 
gmt_circle_to_region(struct GMT_CTRL * GMT,double lon,double lat,double radius,double * wesn)10015 int gmt_circle_to_region (struct GMT_CTRL *GMT, double lon, double lat, double radius, double *wesn) {
10016 	/* Given a (lon,lat) point and a radius in spherical degrees, return w/e/s/n for a rectangular geographic region */
10017 	int code = 0;	/* Will return 1 if w/e extends the full 360, else we return 0 */
10018 	gmt_M_unused (GMT);
10019 	/* Set w/e to center point */
10020 	wesn[XLO] = wesn[XHI] = lon;
10021 	wesn[YLO] = wesn[YHI] = lat;
10022 	/* First adjust the S and N boundaries */
10023 	wesn[YLO] -= radius;	/* Approximate south limit in degrees */
10024 	if (wesn[YLO] < -90.0)	/* Too far south, reset to S pole */
10025 		wesn[YLO] = -90.0;
10026 	wesn[YHI] += radius;	/* Approximate north limit in degrees */
10027 	if (wesn[YHI] > 90.0)	/* Way north, reset to N pole */
10028 		wesn[YHI] = 90.0;
10029 	radius /= cosd (lat);	/* Approximate e-w half-width in degrees longitude at the latitude of center point */
10030 	if (doubleAlmostEqual (wesn[YLO], -90.0) || doubleAlmostEqual (wesn[YHI], 90.0) || radius >= 180.0) {	/* Need all longitudes when a pole is inside the radius or the circle is really large */
10031 		wesn[XLO] = lon - 180.0;
10032 		wesn[XHI] = lon + 180.0;
10033 		code = 1;
10034 	}
10035 	else {	/* Determine longitude limits */
10036 		wesn[XLO] -= radius;	/* Approximate west limit in degrees */
10037 		wesn[XHI] += radius;	/* Approximate east limit in degrees */
10038 		if ((wesn[XHI] - wesn[XLO]) >= 360.0) {	/* Global reach after all */
10039 			wesn[XLO] = lon - 180.0;
10040 			wesn[XHI] = lon + 180.0;
10041 			code = 1;
10042 		}
10043 		if (wesn[XHI] > 360.0) wesn[XLO] -= 360.0, wesn[XHI] -= 360.0;
10044 		if (wesn[XHI] < 0.0)   wesn[XLO] += 360.0, wesn[XHI] += 360.0;
10045 	}
10046 	return (code);
10047 }
10048