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  *
19  *			G M T _ S U P P O R T . C
20  *
21  *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
22  * GMT_support.c contains code used by most GMT programs
23  *
24  * Author:  Paul Wessel
25  * Date:    1-JAN-2010
26  * Version: 5
27  *
28  * Modules in this file:
29  *
30  *  gmtlib_akima               Akima's 1-D spline
31  *  gmt_BC_init             Initialize BCs for a grid or image
32  *  gmt_grd_BC_set          Set two rows of padding according to bound cond for grid
33  *  gmtlib_image_BC_set        Set two rows of padding according to bound cond for image
34  *  gmtsupport_check_rgb           Check rgb for valid range
35  *  gmtsupport_cmyk_to_rgb         Corvert CMYK to RGB
36  *  gmtsupport_comp_double_asc     Used when sorting doubles into ascending order [checks for NaN]
37  *  gmtsupport_comp_float_asc      Used when sorting floats into ascending order [checks for NaN]
38  *  gmtsupport_comp_int_asc        Used when sorting ints into ascending order
39  *  gmt_contours            Subroutine for contouring
40  *  gmtlib_cspline             Natural cubic 1-D spline solver
41  *  gmtsupport_csplint             Natural cubic 1-D spline evaluator
42  *  gmt_delaunay            Performs a Delaunay triangulation
43  *  gmtlib_get_annot_label     Construct degree/minute label
44  *  gmt_get_index           Return color table entry for given z
45  *  gmt_get_fill_from_z     Return fill type for given z
46  *  gmt_get_format          Find # of decimals and create format string
47  *  gmt_get_rgb_from_z      Return rgb for given z
48  *  gmt_get_plot_array      Allocate memory for plotting arrays
49  *  gmt_getfill             Decipher and check fill argument
50  *  gmt_getinc              Decipher and check increment argument
51  *  gmt_getpen              Decipher and check pen argument
52  *  gmt_getrgb              Decipher and check color argument
53  *  gmt_hsv_to_rgb          Convert HSV to RGB
54  *  gmt_init_fill           Initialize fill attributes
55  *  gmt_init_pen            Initialize pen attributes
56  *  gmt_illuminate          Add illumination effects to rgb
57  *  gmt_intpol              1-D interpolation
58  *  gmt_lab_to_rgb          Corvert CIELAB LAB to RGB
59  *  gmt_lab_to_xyz          Convert CIELAB LAB to XYZ
60  *  gmt_non_zero_winding    Finds if a point is inside/outside a polygon
61  *  gmt_putpen              Encode pen argument into textstring
62  *  gmtlib_read_cpt            Read color palette file
63  *  gmtsupport_rgb_to_cmyk         Convert RGB to CMYK
64  *  gmt_rgb_to_hsv          Convert RGB to HSV
65  *  gmt_rgb_to_lab          Convert RGB to CMYK
66  *  gmt_rgb_to_xyz          Convert RGB to CIELAB XYZ
67  *  gmt_sample_cpt          Resamples the current CPT based on new z-array
68  *  gmt_invert_cpt          Flips the current CPT upside down
69  *  gmtsupport_smooth_contour      Use Akima's spline to smooth contour
70  *  GMT_shift_refpoint      Adjust reference point based on size and justification of plotted item
71  *  gmt_sprintf_float       Make formatted string from float, while checking for %-apostrophe
72  *  gmtsupport_trace_contour       Function that trace the contours in gmt_contours
73  *  gmtsupport_polar_adjust        Adjust label justification for polar projection
74  *  gmt_xyz_to_rgb          Convert CIELAB XYZ to RGB
75  *  gmt_xyz_to_lab          Convert CIELAB XYZ to LAB
76  */
77 
78 /*!
79  * \file gmt_support.c
80  * \brief GMT_support.c contains code used by most GMT programs.
81  */
82 
83 #include "gmt_dev.h"
84 #include "gmt_internals.h"
85 #include <locale.h>
86 #ifndef WIN32
87 #include <glob.h>
88 #else
89 #include <Windows.h>
90 #include <tchar.h>
91 #endif
92 
93 /*! . */
94 enum GMT_profmode {
95 	GMT_GOT_AZIM	= 1,
96 	GMT_GOT_ORIENT	= 2,
97 	GMT_GOT_LENGTH	= 4,
98 	GMT_GOT_NP	= 8,
99 	GMT_GOT_INC	= 16,
100 	GMT_GOT_RADIUS	= 32,
101 };
102 
103 /*! . */
104 enum gmt_ends {
105 	BEG = 0,
106 	END = 1
107 };
108 
109 /*! Internal struct used in the processing of CPT z-scaling and truncation */
110 struct CPT_Z_SCALE {
111 	unsigned int z_adjust;	/* 1 if +u<unit> was parsed and scale set, 3 if z has been adjusted, 0 otherwise */
112 	unsigned int z_mode;	/* 1 if +U<unit> was parsed, 0 otherwise */
113 	unsigned int z_unit;	/* Unit enum specified via +u<unit> */
114 	double z_unit_to_meter;	/* Scale, given z_unit, to convert z from <unit> to meters */
115 };
116 
117 static char *GMT_just_code[12] = {"--", "LB", "CB", "RB", "--", "LM", "CM", "RM", "--", "LT", "CT", "RT"};
118 
119 #define gmt_M_uneven_interval(unit) ((unit == 'o' || unit == 'O' || unit == 'k' || unit == 'K' || unit == 'R' || unit == 'r' || unit == 'D' || unit == 'd') ? true : false)	/* true for uneven units */
120 
121 /** @brief XYZ color of the D65 white point */
122 #define WHITEPOINT_X	0.950456
123 #define WHITEPOINT_Y	1.0
124 #define WHITEPOINT_Z	1.088754
125 
126 /**
127  * @brief sRGB gamma correction, transforms R to R'
128  * http://en.wikipedia.org/wiki/SRGB
129  */
130 #define GAMMACORRECTION(t) (((t) <= 0.0031306684425005883) ? (12.92*(t)) : (1.055*pow((t), 0.416666666666666667) - 0.055))
131 
132 /**
133  * @brief Inverse sRGB gamma correction, transforms R' to R
134  */
135 #define INVGAMMACORRECTION(t) (((t) <= 0.0404482362771076) ? ((t)/12.92) : pow(((t) + 0.055)/1.055, 2.4))
136 
137 /**
138  * @brief CIE L*a*b* f function (used to convert XYZ to L*a*b*)
139  * http://en.wikipedia.org/wiki/Lab_color_space
140  */
141 #define LABF(t)	 ((t >= 8.85645167903563082e-3) ? pow(t,0.333333333333333) : (841.0/108.0)*(t) + (4.0/29.0))
142 #define LABINVF(t) ((t >= 0.206896551724137931) ? ((t)*(t)*(t)) : (108.0/841.0)*((t) - (4.0/29.0)))
143 
144 static unsigned char gmt_M_color_rgb[GMT_N_COLOR_NAMES][3] = {	/* r/g/b of X11 colors */
145 #include "gmt_color_rgb.h"
146 };
147 
148 /*! Names of pens and their thicknesses */
149 struct GMT_PEN_NAME {
150 	char name[16];
151 	double width;
152 };
153 
154 /*! . */
155 static struct GMT_PEN_NAME GMT_penname[GMT_N_PEN_NAMES] = {		/* Names and widths of pens */
156 #include "gmt_pennames.h"
157 };
158 
159 /*! List of GMT color tables available in makecpt and grd2cpt
160  *  Some are based on MATLAB and Matplotlib color schemes.
161  *  To add more tables, place the master CPT file in share/cpt and
162  *  add a one-line entry with name and explanation in gmt_cpt_masters.h. */
163 
164 static char *GMT_CPT_master[GMT_N_CPT_MASTERS] = {
165 #include "gmt_cpt_masters.h"
166 };
167 
168 /* Local functions needed for public functions below */
169 
gmtsupport_parse_pattern_new(struct GMT_CTRL * GMT,char * line,struct GMT_FILL * fill)170 GMT_LOCAL int gmtsupport_parse_pattern_new (struct GMT_CTRL *GMT, char *line, struct GMT_FILL *fill) {
171 	/* Parse the fill pattern syntax: p|P<pattern>[+r<dpi>][+b<color>|-][+f<color>|-] */
172 	char *c = NULL;
173 	unsigned int first = 1;
174 	double fb_rgb[4];
175 
176 	fill->dpi = irint (PSL_DOTS_PER_INCH_PATTERN);
177 	if ((c = strchr (line, '+'))) {	/* Got modifiers */
178 		unsigned int pos = 0, uerr = 0;
179 		char p[GMT_BUFSIZ] = {""};
180 		while (gmt_getmodopt (GMT, 0, c, "bfr", &pos, p, &uerr) && uerr == 0) {	/* Looking for +b, +f, +r */
181 			switch (p[0]) {
182 				case 'b':	/* Background color. Giving no argument means transparent [also checking for obsolete -] */
183 					if (p[1] == '\0' || p[1] == '-') {	/* Transparent */
184 						fill->b_rgb[0] = fill->b_rgb[1] = fill->b_rgb[2] = -1,	fill->b_rgb[3] = 0;
185 						GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Background pixels set to transparent!\n");
186 					}
187 					else {
188 						if (gmt_getrgb (GMT, &p[1], fill->b_rgb)) {
189 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Background colorizing value %s not recognized!\n", &p[1]);
190 							return 2;
191 						}
192 						GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Background pixels set to colors %s\n", gmt_putrgb (GMT, fill->b_rgb));
193 					}
194 					break;
195 				case 'f':	/* Foreground color. Giving no argument means transparent [also checking for obsolete -] */
196 					if (p[1] == '\0' || p[1] == '-') {	/* Transparent */
197 						fill->f_rgb[0] = fill->f_rgb[1] = fill->f_rgb[2] = -1,	fill->f_rgb[3] = 0;
198 						GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Foreground pixels set to transparent!\n");
199 					}
200 					else {
201 						if (gmt_getrgb (GMT, &p[1], fill->f_rgb)) {
202 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Foreground colorizing value %s not recognized!\n", &p[1]);
203 							return 2;
204 						}
205 						GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Foreground pixels set to colors %s\n", gmt_putrgb (GMT, fill->f_rgb));
206 					}
207 					break;
208 				case 'r':	/* Dots-per-inch resolution */
209 					if (p[1] == '-') {
210 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Pattern dpi %s is negative!\n", &p[1]);
211 						return 4;
212 					}
213 					fill->dpi = atoi (&p[1]);
214 					GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Pattern dpi set to %d\n", fill->dpi);
215 					break;
216 				default: break;
217 			}
218 		}
219 		if (uerr) return (GMT_PARSE_ERROR);
220 	}
221 
222 	/* Copy name (or number) of pattern */
223 	if (c) c[0] = '\0';	/* Chop off the modifiers */
224 	if (!gmt_M_file_is_memory (&line[1]) && line[1] == '@') {	/* Must be a cache file */
225 		first = gmt_download_file_if_not_found (GMT, &line[1], 0) + 1;	/* Add one since we started at 1 */
226 	}
227 	strncpy (fill->pattern, &line[first], PATH_MAX-1);
228 	/* Attempt to convert to integer - will be 0 if not an integer and then we set it to -1 for a filename */
229 	fill->pattern_no = atoi (fill->pattern);
230 	if (fill->pattern_no == 0) {
231 		bool R_save = GMT->common.R.active[RSET];
232 		fill->pattern_no = -1;
233 		gmt_set_pad (GMT, 0); /* No padding */
234 		GMT->common.R.active[RSET] = false;
235 
236 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Pattern image is in file %s\n", fill->pattern);
237 		if ((fill->I = GMT_Read_Data (GMT->parent, GMT_IS_IMAGE, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, fill->pattern, NULL)) == NULL) {
238 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to read image %s, no pattern set\n", fill->pattern);
239 			return (GMT_RUNTIME_ERROR);
240 		}
241 		GMT->common.R.active[RSET] = R_save;
242 		gmt_set_pad (GMT, GMT->parent->pad); /* Restore to GMT Defaults */
243 		fill->dim[0] = fill->I->header->n_columns;
244 		fill->dim[1] = fill->I->header->n_rows;
245 		if (fill->I->colormap) {	/* Got indexed color image, must build rgb stream instead */
246 			/* Convert colormap from integer to unsigned char and count colors */
247 			unsigned char *colormap = gmt_M_memory (GMT, NULL, 4*256, unsigned char);
248 			int64_t n, j, k;
249 			n = gmt_unpack_rgbcolors (GMT, fill->I, colormap);	/* colormap will be RGBARGBA... */
250 			/* Expand 8-bit indexed image to 24-bit image */
251 			fill->I->data = gmt_M_memory (GMT, fill->I->data, 3 * fill->I->header->nm, unsigned char);
252 			n = 3 * fill->I->header->nm - 1;
253 			for (j = (int64_t)fill->I->header->nm - 1; j >= 0; j--) {
254 				k = 4 * fill->I->data[j] + 3;
255 				fill->I->data[n--] = colormap[--k], fill->I->data[n--] = colormap[--k], fill->I->data[n--] = colormap[--k];
256 			}
257 			gmt_M_free (GMT, colormap);
258 			fill->I->header->n_bands = 3;
259 			fill->I->header->size *= 3;
260 		}
261 		else if (fill->I->header->n_bands == 4) { /* RGBA image, with a color map */
262 			uint64_t n4, j4;
263 			for (j4 = n4 = 0; j4 < 4 * fill->I->header->nm; j4++) /* Reduce image from 32- to 24-bit */
264 				fill->I->data[n4++] = fill->I->data[j4++], fill->I->data[n4++] = fill->I->data[j4++], fill->I->data[n4++] = fill->I->data[j4++];
265 			fill->I->header->n_bands = 3;
266 		}
267 		fill->image = fill->I->data;
268 		fill->dim[2] = (fill->I->header->n_bands == 3) ? 24 : 8;
269 	}
270 	else
271 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Pattern number %d selected\n", fill->pattern_no);
272 
273 	/* If inverse, simply flip the colors around */
274 	if (line[0] == 'p') {
275 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Pattern will be inverted\n");
276 		gmt_M_rgb_copy (fb_rgb, fill->f_rgb);
277 		gmt_M_rgb_copy (fill->f_rgb, fill->b_rgb);
278 		gmt_M_rgb_copy (fill->b_rgb, fb_rgb);
279 	}
280 	if (c) c[0] = '+';	/* Undo previous damage */
281 	return (0);
282 }
283 
gmtsupport_parse_pattern_old(struct GMT_CTRL * GMT,char * line,struct GMT_FILL * fill)284 GMT_LOCAL int gmtsupport_parse_pattern_old (struct GMT_CTRL *GMT, char *line, struct GMT_FILL *fill) {
285 	/* Parse the old-style pattern syntax */
286 	int n, i, len, pos, end;
287 	unsigned int first = 1;
288 	char f, word[GMT_LEN256] = {""};
289 	double fb_rgb[4];
290 
291 	/* We only get here if there are no +r,+f,+b strings or : in line, and we know line[1] is integer.
292 	 * However, if user gave -Gp1image.jpg then we will fail trying to parse as old.  So we do a check
293 	 * here if the argument can be accessed as a file and if so we call the new parser and return */
294 
295 	if (!gmt_M_file_is_memory (&line[1]) && line[1] == '@') {	/* Must be a cache file */
296 		first = gmt_download_file_if_not_found (GMT, &line[1], 0) + 1;	/* Add one since we started at 1 */
297 	}
298 	if (!gmt_access (GMT, &line[first], F_OK))
299 		return (gmtsupport_parse_pattern_new (GMT, line, fill));
300 
301 	n = sscanf (&line[1], "%d/%s", &fill->dpi, fill->pattern);
302 	if (n != 2) return (1);
303 	if (!gmt_M_file_is_memory (fill->pattern) && fill->pattern[0] == '@') {	/* Must be a cache file */
304 		first = gmt_download_file_if_not_found (GMT, fill->pattern, 0);
305 		/* Shuffle pattern name to skip the leading @ */
306 		for (i = 1; fill->pattern[i]; i++) fill->pattern[i-1] = fill->pattern[i];
307 		fill->pattern[i-1] = '\0';
308 	}
309 	/* Determine if there are colorizing options applied, i.e. [:F<rgb>B<rgb>] */
310 	len = (int)MIN(strlen (fill->pattern),PATH_MAX) - 1;
311 	for (i = 0, pos = GMT_NOTSET; i < len && fill->pattern[i] && pos == GMT_NOTSET; i++)
312 		if (i < len && fill->pattern[i] == ':' && (fill->pattern[i+1] == 'B' || fill->pattern[i+1] == 'F')) pos = i;	/* THe extra i < len is needed to defeat cppcheck confusion */
313 	if (pos != GMT_NOTSET) fill->pattern[pos] = '\0';
314 	fill->pattern_no = atoi (fill->pattern);
315 	if (fill->pattern_no == 0) fill->pattern_no = GMT_NOTSET;
316 
317 	/* See if fore- and background colors are given */
318 
319 	len = (int)strlen (line);
320 	for (i = 0, pos = GMT_NOTSET; line[i] && pos == GMT_NOTSET; i++) if (line[i] == ':' && i < len && (line[i+1] == 'B' || line[i+1] == 'F')) pos = i;
321 	pos++;
322 
323 	if (pos > 0 && line[pos]) {	/* Gave colors */
324 		while (line[pos]) {
325 			f = line[pos++];
326 			if (line[pos] == '-')	/* Signal for transparency masking */
327 				fb_rgb[0] = fb_rgb[1] = fb_rgb[2] = -1,	fb_rgb[3] = 0;
328 			else {
329 				end = pos;
330 				while (line[end] && !(line[end] == 'F' || line[end] == 'B')) end++;
331 				strncpy (word, &line[pos], (size_t)(end - pos));
332 				word[end - pos] = '\0';
333 				if (gmt_getrgb (GMT, word, fb_rgb)) {
334 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Colorizing value %s not recognized!\n", word);
335 					return 2;
336 				}
337 			}
338 			if (f == 'f' || f == 'F')
339 				gmt_M_rgb_copy (fill->f_rgb, fb_rgb);
340 			else if (f == 'b' || f == 'B')
341 				gmt_M_rgb_copy (fill->b_rgb, fb_rgb);
342 			else {
343 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Colorizing argument %c not recognized!\n", f);
344 				return 2;
345 			}
346 			while (line[pos] && !(line[pos] == 'F' || line[pos] == 'B')) pos++;
347 		}
348 	}
349 
350 	/* If inverse, simply flip the colors around */
351 	if (line[0] == 'p') {
352 		gmt_M_rgb_copy (fb_rgb, fill->f_rgb);
353 		gmt_M_rgb_copy (fill->f_rgb, fill->b_rgb);
354 		gmt_M_rgb_copy (fill->b_rgb, fb_rgb);
355 	}
356 	return 0;
357 }
358 
359 /*! . */
gmtsupport_parse_pattern(struct GMT_CTRL * GMT,char * line,struct GMT_FILL * fill)360 GMT_LOCAL int gmtsupport_parse_pattern (struct GMT_CTRL *GMT, char *line, struct GMT_FILL *fill) {
361 	int err;
362 	/* New syntax may have a modifier */
363 	if (gmt_found_modifier (GMT, line, "bfr") || !strchr(line, '/'))	/* Clearly new syntax */
364 		err = gmtsupport_parse_pattern_new (GMT, line, fill);
365 	else
366 		err = gmtsupport_parse_pattern_old (GMT, line, fill);
367 	fill->use_pattern = true;
368 	return (err);
369 }
370 
371 /*! . */
gmtsupport_get_userimagename(struct GMT_CTRL * GMT,char * line,char * cpt_path)372 GMT_LOCAL char *gmtsupport_get_userimagename (struct GMT_CTRL *GMT, char *line, char *cpt_path) {
373 	/* When a cpt is not in the current directory but given by relative or absolute path
374 	 * AND that cpt refers to a user pattern file (which may be a relative or absolute path)
375 	 * then unless that pattern file can be found we will try to prepend the path to the cpt
376 	 * file and see if the pattern can be found that way.
377 	 */
378 
379 	int j, err;
380 	char *name = NULL, path[PATH_MAX+GMT_LEN256] = {""};
381 	struct GMT_FILL fill;
382 	if (!gmt_M_is_pattern (line)) return NULL;	/* Not an image specification */
383 	err = gmtsupport_parse_pattern (GMT, line, &fill);	/* See if this returns an error or not */
384 	if (err) return NULL;	/* Not a valid image specification */
385 	if (fill.pattern_no > 0) return NULL;	/* Not a user image */
386 
387 	/* Here we do have a pattern specification */
388 	/* Try the user's default directories */
389 	if (gmtlib_getuserpath (GMT, fill.pattern, path))
390 		return NULL;	/* Yes, found so no problems */
391 
392 	/* Now must put our faith in the cpt path and hope it has a path that can help us */
393 	if (cpt_path == NULL || cpt_path[0] == '<') {	/* Without an actual file path we must warn and bail */
394 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Not enough information to determine location of user pattern %s\n", fill.pattern);
395 		return NULL;
396 	}
397 	j = (int)strlen (cpt_path);
398 	while (j > 0 && cpt_path[j] != '/') j--;	/* Find last slash */
399 	if (j > 0) {	/* OK, got the cpt directory */
400 		cpt_path[j] = '\0';	/* Temporarily chop off the slash */
401 		sprintf (path, "%s/%s", cpt_path, fill.pattern);
402 		cpt_path[j] = '/';	/* Restore the slash */
403 		if (access (path, R_OK)) {
404 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Not enough information to determine location of user pattern %s\n", fill.pattern);
405 			return NULL;
406 		}
407 		name = strdup (path);	/* Must use this image name instead as it contains the full working path */
408 	}
409 	return (name);
410 }
411 
412 /*! . */
gmt_rgb_to_hsv(double rgb[],double hsv[])413 void gmt_rgb_to_hsv (double rgb[], double hsv[]) {
414 	double diff;
415 	unsigned int i, imax = 0, imin = 0;
416 
417 	/* This had checks using rgb value in doubles (e.g. (max_v == xr)), which failed always on some compilers.
418 	   Changed to integer logic: 2009-02-05 by RS.
419 	*/
420 	hsv[3] = rgb[3];	/* Pass transparency unchanged */
421 	for (i = 1; i < 3; i++) {
422 		if (rgb[i] > rgb[imax]) imax = i;
423 		if (rgb[i] < rgb[imin]) imin = i;
424 	}
425 	diff = rgb[imax] - rgb[imin];
426 	hsv[0] = 0.0;
427 	hsv[1] = (rgb[imax] == 0.0) ? 0.0 : diff / rgb[imax];
428 	hsv[2] = rgb[imax];
429 	if (hsv[1] == 0.0) return;	/* Hue is undefined */
430 	hsv[0] = 120.0 * imax + 60.0 * (rgb[(imax + 1) % 3] - rgb[(imax + 2) % 3]) / diff;
431 	if (hsv[0] < 0.0) hsv[0] += 360.0;
432 	if (hsv[0] > 360.0) hsv[0] -= 360.0;
433 }
434 
435 /*! . */
gmt_hsv_to_rgb(double rgb[],double hsv[])436 void gmt_hsv_to_rgb (double rgb[], double hsv[]) {
437 	int i;
438 	double h, f, p, q, t, rr, gg, bb;
439 
440 	rgb[3] = hsv[3];	/* Pass transparency unchanged */
441 	if (hsv[1] == 0.0)
442 		rgb[0] = rgb[1] = rgb[2] = hsv[2];
443 	else {
444 		h = hsv[0];
445 		while (h >= 360.0) h -= 360.0;
446 		while (h < 0.0) h += 360.0;
447 		h /= 60.0;
448 		i = irint (floor (h));
449 		f = h - i;
450 		p = hsv[2] * (1.0 - hsv[1]);
451 		q = hsv[2] * (1.0 - (hsv[1] * f));
452 		t = hsv[2] * (1.0 - (hsv[1] * (1.0 - f)));
453 		switch (i) {
454 			case 0:
455 				rr = hsv[2]; gg = t; bb = p;
456 				break;
457 			case 1:
458 				rr = q; gg = hsv[2]; bb = p;
459 				break;
460 			case 2:
461 				rr = p; gg = hsv[2]; bb = t;
462 				break;
463 			case 3:
464 				rr = p; gg = q; bb = hsv[2];
465 				break;
466 			case 4:
467 				rr = t; gg = p; bb = hsv[2];
468 				break;
469 			default:
470 				rr = hsv[2]; gg = p; bb = q;
471 				break;
472 		}
473 
474 		rgb[0] = (rr < 0.0) ? 0.0 : rr;
475 		rgb[1] = (gg < 0.0) ? 0.0 : gg;
476 		rgb[2] = (bb < 0.0) ? 0.0 : bb;
477 	}
478 }
479 
480 /*! . */
gmtsupport_rgb_to_cmyk(double rgb[],double cmyk[])481 GMT_LOCAL void gmtsupport_rgb_to_cmyk (double rgb[], double cmyk[]) {
482 	/* Plain conversion; with default undercolor removal or blackgeneration */
483 	/* RGB is in 0-1, CMYK will be in 0-1 range */
484 
485 	int i;
486 
487 	cmyk[4] = rgb[3];	/* Pass transparency unchanged */
488 	for (i = 0; i < 3; i++) cmyk[i] = 1.0 - rgb[i];
489 	cmyk[3] = MIN (cmyk[0], MIN (cmyk[1], cmyk[2]));	/* Default Black generation */
490 	if (cmyk[3] < GMT_CONV8_LIMIT) cmyk[3] = 0.0;
491 
492 	/* To implement device-specific blackgeneration, supply lookup table K = BG[cmyk[3]] */
493 
494 	for (i = 0; i < 3; i++) {
495 		cmyk[i] -= cmyk[3];		/* Default undercolor removal */
496 		if (cmyk[i] < GMT_CONV8_LIMIT) cmyk[i] = 0.0;
497 	}
498 
499 	/* To implement device-specific undercolor removal, supply lookup table u = UR[cmyk[3]] */
500 }
501 
502 /*! . */
gmtsupport_cmyk_to_rgb(double rgb[],double cmyk[])503 GMT_LOCAL void gmtsupport_cmyk_to_rgb (double rgb[], double cmyk[]) {
504 	/* Plain conversion; no undercolor removal or blackgeneration */
505 	/* CMYK is in 0-1, RGB will be in 0-1 range */
506 
507 	int i;
508 
509 	rgb[3] = cmyk[4];	/* Pass transparency unchanged */
510 	for (i = 0; i < 3; i++) rgb[i] = 1.0 - cmyk[i] - cmyk[3];
511 }
512 
513 /*! . */
gmtsupport_cmyk_to_hsv(double hsv[],double cmyk[])514 GMT_LOCAL void gmtsupport_cmyk_to_hsv (double hsv[], double cmyk[]) {
515 	/* Plain conversion; no undercolor removal or blackgeneration */
516 	/* CMYK is in 0-1, RGB will be in 0-1 range */
517 
518 	double rgb[4];
519 
520 	gmtsupport_cmyk_to_rgb (rgb, cmyk);
521 	gmt_rgb_to_hsv (rgb, hsv);
522 }
523 
524 /*!
525  * Transform sRGB to CIE XYZ with the D65 white point
526  *
527  * Poynton, "Frequently Asked Questions About Color," page 10
528  * Wikipedia: http://en.wikipedia.org/wiki/SRGB
529  * Wikipedia: http://en.wikipedia.org/wiki/CIE_1931_color_space
530  */
gmt_rgb_to_xyz(double rgb[],double xyz[])531 void gmt_rgb_to_xyz (double rgb[], double xyz[]) {
532 	double R, G, B;
533 	R = INVGAMMACORRECTION(rgb[0]);
534 	G = INVGAMMACORRECTION(rgb[1]);
535 	B = INVGAMMACORRECTION(rgb[2]);
536 	xyz[0] = 0.4123955889674142161*R  + 0.3575834307637148171*G + 0.1804926473817015735*B;
537 	xyz[1] = 0.2125862307855955516*R  + 0.7151703037034108499*G + 0.07220049864333622685*B;
538 	xyz[2] = 0.01929721549174694484*R + 0.1191838645808485318*G + 0.9504971251315797660*B;
539 }
540 
541 /*! . */
gmt_xyz_to_rgb(double rgb[],double xyz[])542 void gmt_xyz_to_rgb (double rgb[], double xyz[]) {
543 	double R, G, B, min;
544 	R = ( 3.2406 * xyz[0] - 1.5372 * xyz[1] - 0.4986 * xyz[2]);
545 	G = (-0.9689 * xyz[0] + 1.8758 * xyz[1] + 0.0415 * xyz[2]);
546 	B = ( 0.0557 * xyz[0] - 0.2040 * xyz[1] + 1.0570 * xyz[2]);
547 
548 	min = MIN(MIN(R, G), B);
549 
550 	/* Force nonnegative values so that gamma correction is well-defined. */
551 	if (min < 0) {
552 		R -= min;
553 		G -= min;
554 		B -= min;
555 	}
556 
557 	/* Transform from RGB to R'G'B' */
558 	rgb[0] = GAMMACORRECTION(R);
559 	rgb[1] = GAMMACORRECTION(G);
560 	rgb[2] = GAMMACORRECTION(B);
561 }
562 
563 /*!
564  * Convert CIE XYZ to CIE L*a*b* (CIELAB) with the D65 white point
565  *
566  * Wikipedia: http://en.wikipedia.org/wiki/Lab_color_space
567  */
568 
gmt_xyz_to_lab(double xyz[],double lab[])569 void gmt_xyz_to_lab (double xyz[], double lab[]) {
570 	double X, Y, Z;
571 	X = LABF( xyz[0] / WHITEPOINT_X );
572 	Y = LABF( xyz[1] / WHITEPOINT_Y );
573 	Z = LABF( xyz[2] / WHITEPOINT_Z );
574 	lab[0] = 116 * Y - 16;
575 	lab[1] = 500 * (X - Y);
576 	lab[2] = 200 * (Y - Z);
577 }
578 
579 /*! . */
gmt_lab_to_xyz(double xyz[],double lab[])580 void gmt_lab_to_xyz (double xyz[], double lab[]) {
581 	xyz[0] = WHITEPOINT_X * LABINVF( (lab[0] + 16)/116 + lab[1]/500 );
582 	xyz[1] = WHITEPOINT_Y * LABINVF( (lab[0] + 16)/116 );
583 	xyz[2] = WHITEPOINT_Z * LABINVF( (lab[0] + 16)/116 - lab[2]/200 );
584 }
585 
586 /*! . */
gmt_rgb_to_lab(double rgb[],double lab[])587 void gmt_rgb_to_lab (double rgb[], double lab[]) {
588 	/* RGB is in 0-1, LAB will be in ??? range */
589 
590 	double xyz[3];
591 
592 	gmt_rgb_to_xyz (rgb, xyz);
593 	gmt_xyz_to_lab (xyz, lab);
594 }
595 
596 /*! . */
gmt_lab_to_rgb(double rgb[],double lab[])597 void gmt_lab_to_rgb (double rgb[], double lab[]) {
598 	double xyz[3];
599 	gmt_lab_to_xyz (xyz, lab);
600 	gmt_xyz_to_rgb (rgb, xyz);
601 }
602 
603 /*! . */
gmtsupport_comp_double_asc(const void * p_1,const void * p_2)604 GMT_LOCAL int gmtsupport_comp_double_asc (const void *p_1, const void *p_2) {
605 	/* Returns -1 if point_1 is < that point_2,
606 	   +1 if point_2 > point_1, and 0 if they are equal
607 	*/
608 	bool bad_1, bad_2;
609 	const double *point_1 = p_1, *point_2 = p_2;
610 
611 	bad_1 = gmt_M_is_dnan ((*point_1));
612 	bad_2 = gmt_M_is_dnan ((*point_2));
613 
614 	if (bad_1 && bad_2) return (0);
615 	if (bad_1) return (+1);
616 	if (bad_2) return (-1);
617 
618 	if ((*point_1) < (*point_2)) return (-1);
619 	if ((*point_1) > (*point_2)) return (+1);
620 	return (0);
621 }
622 
623 /*! . */
gmtsupport_comp_float_asc(const void * p_1,const void * p_2)624 GMT_LOCAL int gmtsupport_comp_float_asc (const void *p_1, const void *p_2) {
625 	/* Returns -1 if point_1 is < that point_2,
626 	   +1 if point_2 > point_1, and 0 if they are equal
627 	*/
628 	bool bad_1, bad_2;
629 	const float *point_1 = p_1, *point_2 = p_2;
630 
631 	bad_1 = gmt_M_is_fnan ((*point_1));
632 	bad_2 = gmt_M_is_fnan ((*point_2));
633 
634 	if (bad_1 && bad_2) return (0);
635 	if (bad_1) return (+1);
636 	if (bad_2) return (-1);
637 
638 	if ((*point_1) < (*point_2)) return (-1);
639 	if ((*point_1) > (*point_2)) return (+1);
640 	return (0);
641 }
642 
643 /*! . */
gmtsupport_comp_ulong_asc(const void * p_1,const void * p_2)644 GMT_LOCAL int gmtsupport_comp_ulong_asc (const void *p_1, const void *p_2) {
645 	/* Returns -1 if point_1 is < that point_2,
646 	   +1 if point_2 > point_1, and 0 if they are equal
647 	*/
648 	const uint64_t *point_1 = p_1, *point_2 = p_2;
649 
650 	if ((*point_1) < (*point_2)) return (-1);
651 	if ((*point_1) > (*point_2)) return (+1);
652 	return (0);
653 }
654 
655 /*! . */
gmtsupport_comp_long_asc(const void * p_1,const void * p_2)656 GMT_LOCAL int gmtsupport_comp_long_asc (const void *p_1, const void *p_2) {
657 	/* Returns -1 if point_1 is < that point_2,
658 	   +1 if point_2 > point_1, and 0 if they are equal
659 	*/
660 	const int64_t *point_1 = p_1, *point_2 = p_2;
661 
662 	if ((*point_1) < (*point_2)) return (-1);
663 	if ((*point_1) > (*point_2)) return (+1);
664 	return (0);
665 }
666 
667 /*! . */
gmtsupport_comp_uint_asc(const void * p_1,const void * p_2)668 GMT_LOCAL int gmtsupport_comp_uint_asc (const void *p_1, const void *p_2) {
669 	/* Returns -1 if point_1 is < that point_2,
670 	   +1 if point_2 > point_1, and 0 if they are equal
671 	*/
672 	const unsigned int *point_1 = p_1, *point_2 = p_2;
673 
674 	if ((*point_1) < (*point_2)) return (-1);
675 	if ((*point_1) > (*point_2)) return (+1);
676 	return (0);
677 }
678 
679 /*! . */
gmtsupport_comp_int_asc(const void * p_1,const void * p_2)680 GMT_LOCAL int gmtsupport_comp_int_asc (const void *p_1, const void *p_2) {
681 	/* Returns -1 if point_1 is < that point_2,
682 	   +1 if point_2 > point_1, and 0 if they are equal
683 	*/
684 	const int *point_1 = p_1, *point_2 = p_2;
685 
686 	if ((*point_1) < (*point_2)) return (-1);
687 	if ((*point_1) > (*point_2)) return (+1);
688 	return (0);
689 }
690 
691 /*! . */
gmtsupport_comp_ushort_asc(const void * p_1,const void * p_2)692 GMT_LOCAL int gmtsupport_comp_ushort_asc (const void *p_1, const void *p_2) {
693 	/* Returns -1 if point_1 is < that point_2,
694 	   +1 if point_2 > point_1, and 0 if they are equal
695 	*/
696 	const unsigned short int *point_1 = p_1, *point_2 = p_2;
697 
698 	if ((*point_1) < (*point_2)) return (-1);
699 	if ((*point_1) > (*point_2)) return (+1);
700 	return (0);
701 }
702 
703 /*! . */
gmtsupport_comp_short_asc(const void * p_1,const void * p_2)704 GMT_LOCAL int gmtsupport_comp_short_asc (const void *p_1, const void *p_2) {
705 	/* Returns -1 if point_1 is < that point_2,
706 	   +1 if point_2 > point_1, and 0 if they are equal
707 	*/
708 	const short int *point_1 = p_1, *point_2 = p_2;
709 
710 	if ((*point_1) < (*point_2)) return (-1);
711 	if ((*point_1) > (*point_2)) return (+1);
712 	return (0);
713 }
714 
715 /*! . */
gmtsupport_comp_uchar_asc(const void * p_1,const void * p_2)716 GMT_LOCAL int gmtsupport_comp_uchar_asc (const void *p_1, const void *p_2) {
717 	/* Returns -1 if point_1 is < that point_2,
718 	   +1 if point_2 > point_1, and 0 if they are equal
719 	*/
720 	const unsigned char *point_1 = p_1, *point_2 = p_2;
721 
722 	if ((*point_1) < (*point_2)) return (-1);
723 	if ((*point_1) > (*point_2)) return (+1);
724 	return (0);
725 }
726 
727 /*! . */
gmtsupport_comp_char_asc(const void * p_1,const void * p_2)728 GMT_LOCAL int gmtsupport_comp_char_asc (const void *p_1, const void *p_2) {
729 	/* Returns -1 if point_1 is < that point_2,
730 	   +1 if point_2 > point_1, and 0 if they are equal
731 	*/
732 	const char *point_1 = p_1, *point_2 = p_2;
733 
734 	if ((*point_1) < (*point_2)) return (-1);
735 	if ((*point_1) > (*point_2)) return (+1);
736 	return (0);
737 }
738 
739 /*! . */
gmtsupport_check_irgb(int irgb[],double rgb[])740 GMT_LOCAL bool gmtsupport_check_irgb (int irgb[], double rgb[]) {
741 	if ((irgb[0] < 0 || irgb[0] > 255) || (irgb[1] < 0 || irgb[1] > 255) || (irgb[2] < 0 || irgb[2] > 255)) return (true);
742 	rgb[0] = gmt_M_is255 (irgb[0]);
743 	rgb[1] = gmt_M_is255 (irgb[1]);
744 	rgb[2] = gmt_M_is255 (irgb[2]);
745 	return (false);
746 }
747 
748 /*! . */
gmtsupport_check_rgb(double rgb[])749 GMT_LOCAL bool gmtsupport_check_rgb (double rgb[]) {
750 	return ((rgb[0] < 0.0 || rgb[0] > 1.0) || (rgb[1] < 0.0 || rgb[1] > 1.0) || (rgb[2] < 0.0 || rgb[2] > 1.0));
751 }
752 
753 /*! . */
gmtsupport_check_hsv(double hsv[])754 GMT_LOCAL bool gmtsupport_check_hsv (double hsv[]) {
755 	return ((hsv[0] < 0.0 || hsv[0] > 360.0) || (hsv[1] < 0.0 || hsv[1] > 1.0) || (hsv[2] < 0.0 || hsv[2] > 1.0));
756 }
757 
758 /*! . */
gmtsupport_check_cmyk(double cmyk[])759 GMT_LOCAL bool gmtsupport_check_cmyk (double cmyk[]) {
760 	unsigned int i;
761 	for (i = 0; i < 4; i++) cmyk[i] *= 0.01;
762 	for (i = 0; i < 4; i++) if (cmyk[i] < 0.0 || cmyk[i] > 1.0) return (true);
763 	return (false);
764 }
765 
766 /*! . */
gmt_char_count(char * txt,char c)767 unsigned int gmt_char_count (char *txt, char c) {
768 	unsigned int i = 0, n = 0;
769 	while (txt[i]) if (txt[i++] == c) n++;
770 	return (n);
771 }
772 
773 /*! . */
gmtsupport_gethsv(struct GMT_CTRL * GMT,char * line,double hsv[])774 GMT_LOCAL bool gmtsupport_gethsv (struct GMT_CTRL *GMT, char *line, double hsv[]) {
775 	int n, i, count, irgb[3], c = 0;
776 	double rgb[4], cmyk[5];
777 	char buffer[GMT_LEN64] = {""}, *t = NULL;
778 
779 	if (!line) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "No argument given to gmtsupport_gethsv\n"); GMT->parent->error = GMT_PARSE_ERROR; return false; }
780 	if (!line[0]) return (false);	/* Nothing to do - accept default action */
781 
782 	rgb[3] = hsv[3] = cmyk[4] = 0.0;	/* Default is no transparency */
783 	if (line[0] == '-') {
784 		hsv[0] = -1.0; hsv[1] = -1.0; hsv[2] = -1.0;
785 		return (false);
786 	}
787 
788 	strncpy (buffer, line, GMT_LEN64-1);	/* Make local copy */
789 	if ((t = strstr (buffer, "@")) && strlen (t) > 1) {	/* User requested transparency via @<transparency> */
790 		double transparency = atof (&t[1]);
791 		if (transparency < 0.0 || transparency > 100.0)
792 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of transparency (%s) not recognized. Using default [0 or opaque].\n", t);
793 		else
794 			rgb[3] = hsv[3] = cmyk[4] = transparency / 100.0;	/* Transparency is in 0-1 range */
795 		t[0] = '\0';	/* Chop off transparency for the rest of this function */
796 	}
797 
798 	if (buffer[0] == '#') {	/* #rrggbb */
799 		n = sscanf (buffer, "#%2x%2x%2x", (unsigned int *)&irgb[0], (unsigned int *)&irgb[1], (unsigned int *)&irgb[2]);
800 		if (n != 3 || gmtsupport_check_irgb (irgb, rgb)) return (true);
801 		gmt_rgb_to_hsv (rgb, hsv);
802 		return (false);
803 	}
804 
805 	/* If it starts with a letter, then it could be a name */
806 
807 	if (isalpha ((unsigned char) buffer[0])) {
808 		if ((n = (int)gmt_colorname2index (GMT, buffer)) < 0) {
809 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Colorname %s not recognized!\n", buffer);
810 			return (true);
811 		}
812 		for (i = 0; i < 3; i++) rgb[i] = gmt_M_is255 (gmt_M_color_rgb[n][i]);
813 		gmt_rgb_to_hsv (rgb, hsv);
814 		return (false);
815 	}
816 
817 	/* Definitely wrong, at this point, is something that does not end in a number */
818 
819 	if (strlen(buffer) < 1) return (true);	/* Nothing, which is bad */
820 	c = buffer[strlen(buffer)-1];
821 	if (!(isdigit (c) || c == '.')) return (true);
822 
823 	count = (int)gmt_char_count (buffer, '/');
824 
825 	if (count == 3) {	/* c/m/y/k */
826 		n = sscanf (buffer, "%lf/%lf/%lf/%lf", &cmyk[0], &cmyk[1], &cmyk[2], &cmyk[3]);
827 		if (n != 4 || gmtsupport_check_cmyk (cmyk)) return (true);
828 		gmtsupport_cmyk_to_hsv (hsv, cmyk);
829 		return (false);
830 	}
831 
832 	if (count == 2) {	/* r/g/b */
833 		n = sscanf (buffer, "%d/%d/%d", &irgb[0], &irgb[1], &irgb[2]);
834 		if (n != 3 || gmtsupport_check_irgb (irgb, rgb)) return (true);
835 		gmt_rgb_to_hsv (rgb, hsv);
836 		return (false);
837 	}
838 
839 	if (gmt_char_count (buffer, '-')  == 2) {		/* h-s-v */
840 		n = sscanf (buffer, "%lf-%lf-%lf", &hsv[0], &hsv[1], &hsv[2]);
841 		return (n != 3 || gmtsupport_check_hsv (hsv));
842 	}
843 
844 	if (count == 0) {	/* gray */
845 		n = sscanf (buffer, "%d", &irgb[0]);
846 		irgb[1] = irgb[2] = irgb[0];
847 		if (n != 1 || gmtsupport_check_irgb (irgb, rgb)) return (true);
848 		gmt_rgb_to_hsv (rgb, hsv);
849 		return (false);
850 	}
851 
852 	/* Get here if there is a problem */
853 
854 	return (true);
855 }
856 
857 /*! . */
gmtsupport_is_pattern_old(char * word)858 GMT_LOCAL bool gmtsupport_is_pattern_old (char *word) {
859 	/* Returns true if the word is a pattern specification
860 	 * Old style:   P|p<dpi>/<pattern>[:B<color>[F<color>]]
861 	 */
862 
863 	if (strchr (word, ':')) return (true);			/* Only patterns may have a colon */
864 	if (!(word[0] == 'P' || word[0] == 'p')) return false;	/* Patterns must start with P or p */
865 	if (!strchr (word, '/')) return (false);		/* Patterns separate dpi and pattern with a slash */
866 	/* Here we know we start with P|p and there is a slash - this can only be a pattern specification */
867 	return (true);
868 }
869 
870 /*! . */
gmtsupport_is_pattern_new(struct GMT_CTRL * GMT,char * word)871 GMT_LOCAL bool gmtsupport_is_pattern_new (struct GMT_CTRL *GMT, char *word) {
872 	/* Returns true if the word is a pattern specification
873 	 * New style:   P|p<pattern>[+b<color>][+f<color>][+r<dpi>]
874 	 */
875 	char *c = NULL;
876 	int n;
877 	if ((c = strchr (word, '+')) && strchr ("bfr", c[1])) return (true);		/* Found +b, +f or +r */
878 	/* Here we have no modifiers at all */
879 	if (!(word[0] == 'P' || word[0] == 'p')) return false;	/* Patterns must start with P or p */
880 	/* Pattern without modifier must be an integer OR a valid file */
881 	if ((n = atoi (&word[1])) > 0 && n < PSL_N_PATTERNS) return (true);	/* Got a valid integer */
882 	/* Se if we can access the file */
883 	if (!gmt_access (GMT, &word[1], F_OK)) return (true);	/* Got a file that exists */
884 	return (false);	/* Not a pattern */
885 }
886 
887 /*! . */
gmtsupport_is_pattern(struct GMT_CTRL * GMT,char * word)888 GMT_LOCAL bool gmtsupport_is_pattern (struct GMT_CTRL *GMT, char *word) {
889 	/* Returns true if the word is a pattern specification
890 	 * Old style:   P|p<dpi>/<pattern>[:B<color>[F<color>]]
891 	 * New style:   P|p<pattern>[+b<color>][+f<color>][+r<dpi>]
892 	 */
893 
894 	bool val;
895 	/* New syntax may have a modifier or no slash AND no colon */
896 	if ((gmt_found_modifier (GMT, word, "bfr") || !strchr(word, '/')) && !strchr (word,':'))
897 		val = gmtsupport_is_pattern_new (GMT, word);
898 	else
899 		val = gmtsupport_is_pattern_old (word);
900 	return (val);
901 }
902 
903 /*! . */
gmtlib_is_color(struct GMT_CTRL * GMT,char * word)904 bool gmtlib_is_color (struct GMT_CTRL *GMT, char *word) {
905 	int i, k, n, n_hyphen = 0, n_slashes = 0;
906 
907 	/* Returns true if we are sure the word is a color string - else false.
908 	 * color syntax is <r/g/b>|<h-s-v>|<c/m/y/k>|<colorname>[@transparency].
909 	 * NOTES: 1) <gray> is excluded since this function is called in places where
910 	 *  a single integer may be used for font size or pen width...
911 	 *        2) We are not checking if the values are kosher; just that they follow the pattern  */
912 
913 	n = (int)strlen (word);
914 	if (n == 0) return (false);
915 	if (strchr (word, '@')) return (true);	/* Transparency means we have a color */
916 
917 	if (word[0] == '#') return (true);		/* Probably #rrggbb */
918 	if (gmt_colorname2index (GMT, word) >= 0) return (true);	/* Valid color name */
919 	/* Skip dimension specifications with units c|i|m|p or a colon */
920 	if (strchr(word,'t')) return (false);		/* Got a t somewhere */
921 	if (strchr(word,':')) return (false);		/* Got a : somewhere */
922 	if (strchr(word,'c')) return (false);		/* Got a c somewhere */
923 	if (strchr(word,'i')) return (false);		/* Got an i somewhere */
924 	if (strchr(word,'m')) return (false);		/* Got a m somewhere */
925 	if (strchr(word,'p')) return (false);		/* Got a p somewhere */
926 	for (i = k = 0; word[i]; i++) if (word[i] == '/') n_slashes++;	/* Count slashes */
927 	if (n_slashes == 1 || n_slashes > 3) return (false);	/* No color spec takes only 1 slash or more than 3 */
928 	n--;
929 	/* Check for h-s-v as well.  Must find 2 hyphens */
930 	while (n >= 0 && (strchr ("/-.", word[n]) || isdigit ((int)word[n]))) {
931 		if (word[n] == '-') n_hyphen++;
932 		n--;	/* Wind down as long as we find -,.,/ or digits */
933 	}
934 	return ((n == -1 && (n_slashes || n_hyphen == 2)));	/* Hence <gray> will fail the slash test */
935 }
936 
937 /*! . */
gmt_getfonttype(struct GMT_CTRL * GMT,char * name)938 int gmt_getfonttype (struct GMT_CTRL *GMT, char *name) {
939 	unsigned int i;
940 
941 	if (!name[0]) return (-1);
942 	if (!isdigit ((unsigned char) name[0])) {	/* Does not start with number. Try font name */
943 		int ret = GMT_NOTSET;
944 		for (i = 0; i < GMT->session.n_fonts && strcmp (name, GMT->session.font[i].name); i++);
945 		if (i < GMT->session.n_fonts) ret = i;
946 		return (ret);
947 	}
948 	if (!isdigit ((unsigned char) name[strlen(name)-1])) return (GMT_NOTSET);	/* Starts with digit, ends with something else: cannot be */
949 	return (atoi (name));
950 }
951 
952 /*! . */
gmtsupport_is_fontname(struct GMT_CTRL * GMT,char * word)953 GMT_LOCAL bool gmtsupport_is_fontname (struct GMT_CTRL *GMT, char *word) {
954 	/* Returns true if the word is one of the named fonts */
955 	unsigned int i;
956 
957 	if (!word[0]) return (false);
958 	for (i = 0; i < GMT->session.n_fonts && strcmp (word, GMT->session.font[i].name); i++);
959 	if (i == GMT->session.n_fonts) return (false);
960 	return (true);
961 }
962 
963 /*! . */
gmtsupport_name2pen(char * name)964 GMT_LOCAL int gmtsupport_name2pen (char *name) {
965 	/* Return index into structure with pennames and width, for given name */
966 
967 	int i, k;
968 	char Lname[GMT_LEN64] = {""};
969 
970 	strncpy (Lname, name, GMT_LEN64-1);
971 	gmt_str_tolower (Lname);
972 	for (i = 0, k = GMT_NOTSET; k < 0 && i < GMT_N_PEN_NAMES; i++) if (!strcmp (Lname, GMT_penname[i].name)) k = i;
973 	/* Backwards compatibility for old, inappropriate input pen name "obese" */
974 	if (k == GMT_NOTSET && !strcmp (Lname, "obese")) k = GMT_N_PEN_NAMES - 1;
975 
976 	return (k);
977 }
978 
979 /*! . */
gmtsupport_pen2name(double width)980 GMT_LOCAL int gmtsupport_pen2name (double width) {
981 	/* Return index into structure with pennames and width, for given width */
982 
983 	int i, k;
984 
985 	if (gmt_M_is_dnan (width)) return -2;	/* Pen width undefined */
986 
987 	for (i = 0, k = GMT_NOTSET; k < 0 && i < GMT_N_PEN_NAMES; i++) if (gmt_M_eq (width, GMT_penname[i].width)) k = i;
988 
989 	return (k);
990 }
991 
992 /*! . */
gmtsupport_getpenwidth(struct GMT_CTRL * GMT,char * line,struct GMT_PEN * P)993 GMT_LOCAL int gmtsupport_getpenwidth (struct GMT_CTRL *GMT, char *line, struct GMT_PEN *P) {
994 	int n;
995 
996 	/* SYNTAX for pen width:  <floatingpoint>[p|i|c|m] or <name> [fat, thin, etc] */
997 
998 	if (!line || !line[0]) return (GMT_NOERROR);	/* Nothing given, return */
999 	if ((line[0] == '.' && (line[1] >= '0' && line[1] <= '9')) || (line[0] >= '0' && line[0] <= '9')) {
1000 		/* Pen thickness with optional unit at end */
1001 		P->width = gmt_convert_units (GMT, line, GMT_PT, GMT_PT);
1002 	}
1003 	else if (!strcmp (line, "auto"))
1004 		P->width = GMT->session.d_NaN;
1005 	else {	/* Pen name was given - these refer to fixed widths in points */
1006 		if ((n = gmtsupport_name2pen (line)) < 0) {
1007 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Pen name %s not recognized!\n", line);
1008 			return GMT_PARSE_ERROR;
1009 		}
1010 		P->width = GMT_penname[n].width;
1011 	}
1012 	return (GMT_NOERROR);
1013 }
1014 
1015 /*! . */
gmtlib_getpenstyle(struct GMT_CTRL * GMT,char * line,struct GMT_PEN * P)1016 int gmtlib_getpenstyle (struct GMT_CTRL *GMT, char *line, struct GMT_PEN *P) {
1017 	unsigned int i, n, pos, unit = GMT_PT, n_dash = 0;
1018 	double width;
1019 	char tmp[GMT_PEN_LEN] = {""}, string[GMT_BUFSIZ] = {""}, ptr[GMT_BUFSIZ] = {""};
1020 
1021 	if (!line || !line[0]) return (GMT_NOERROR);	/* Nothing to do as no style given.  Note we do not reset any existing style here; use solid to do so */
1022 	if (!strcmp (line, "solid")) {	/* Used to override any other default style */
1023 		P->offset = 0.0;
1024 		P->style[0] = '\0';
1025 		 return (GMT_NOERROR);
1026 	}
1027 	if (gmt_M_is_dnan (P->width))  {	/* Must save style as given since we do not know the width yet */
1028 		strncpy (P->style, line, GMT_PEN_LEN);	/* We will update style in gmt_set_undefined_defaults. */
1029 		return (GMT_NOERROR);
1030 	}
1031 	if (!strncmp (line, "dashdot", 7U)) strcpy (line, "-.");	/* Accept "dashdot*" to mean -. */
1032 	if (!strncmp (line, "dotdash", 7U)) strcpy (line, ".-");	/* Accept "dotdash*" to mean .- */
1033 	if (!strncmp (line, "dash", 4U)) strcpy (line, "-");		/* Accept "dash*" to mean - */
1034 	if (!strncmp (line, "dot", 3U)) strcpy (line, ".");		/* Accept "dot*" to mean . */
1035 	if (!strcmp (line, "a")) {	/* Old GMT4 "a" style */
1036 		if (gmt_M_compat_check (GMT, 4))
1037 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Pen-style \"a\" is deprecated, use \"dashed\" or \"-\" instead\n");
1038 		else {
1039 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad pen-style %s\n", line);
1040 			return GMT_PARSE_ERROR;
1041 		}
1042 		strcpy (line, "-");	/* Accepted GMT4 style "a" to mean - */
1043 	}
1044 	else if (!strcmp (line, "o")) {	/* Old GMT4 "o" style */
1045 		if (gmt_M_compat_check (GMT, 4))
1046 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Pen-style \"o\" is deprecated, use \"dotted\" or \".\" instead\n");
1047 		else {
1048 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad pen-style %s\n", line);
1049 			return GMT_PARSE_ERROR;
1050 		}
1051 		strcpy (line, ".");	/* Accepted GMT4 style "a" to mean - */
1052 	}
1053 	n = (int)strlen (line) - 1;
1054 	if (strchr (GMT_DIM_UNITS, line[n]))	/* Separate unit given to style string */
1055 		unit = gmtlib_unit_lookup (GMT, line[n], GMT->current.setting.proj_length_unit);
1056 
1057 	width = (P->width < GMT_CONV4_LIMIT) ? GMT_PENWIDTH : P->width;
1058 
1059 	if (isdigit ((int)line[0])) {	/* Specified numeric pattern will start with an integer */
1060 		unsigned int c_pos;
1061 
1062 		for (i = 1, c_pos = 0; line[i] && c_pos == 0; i++) if (line[i] == ':') c_pos = i;
1063 		if (c_pos == 0) {
1064 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Pen style %s does not contain :<phase>. <phase> set to 0\n", line);
1065 			P->offset = 0.0;
1066 			sscanf (line, "%s", P->style);
1067 		}
1068 		else {
1069 			line[c_pos] = ' ';
1070 			sscanf (line, "%s %lf", P->style, &P->offset);
1071 			line[c_pos] = ':';
1072 		}
1073 		for (i = 0; P->style[i]; i++) if (P->style[i] == '_') P->style[i] = ' ';
1074 
1075 		/* Must convert given units to points */
1076 
1077 		pos = 0;
1078 		while ((gmt_strtok (P->style, " ", &pos, ptr))) {
1079 			snprintf (tmp, GMT_PEN_LEN, "%g ", (atof (ptr) * GMT->session.u2u[unit][GMT_PT]));
1080 			strcat (string, tmp);
1081 			n_dash++;
1082 		}
1083 		string[strlen (string) - 1] = 0;
1084 		if (strlen (string) >= GMT_PEN_LEN) {
1085 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Pen attributes too long!\n");
1086 			return GMT_PARSE_ERROR;
1087 		}
1088 		strncpy (P->style, string, GMT_PEN_LEN-1);
1089 		P->offset *= GMT->session.u2u[unit][GMT_PT];
1090 	}
1091 	else  {	/* New way of building it up with - and . */
1092 		P->style[0] = '\0';
1093 		P->offset = 0.0;
1094 		for (i = 0; line[i]; i++) {
1095 			if (line[i] == '-') { /* Dash */
1096 				snprintf (tmp, GMT_PEN_LEN, "%g %g ", 8.0 * width, 4.0 * width);
1097 				strcat (P->style, tmp);
1098 				n_dash += 2;
1099 			}
1100 			else if (line[i] == '.') { /* Dot */
1101 				snprintf (tmp, GMT_PEN_LEN, "%g %g ", width, 4.0 * width);
1102 				strcat (P->style, tmp);
1103 				n_dash += 2;
1104 			}
1105 			else if (line[i] == ':') { 	/* :<phase> setting given at the end */
1106 				i++;	/* Advance past the colon */
1107 				P->offset = atof (line) * GMT->session.u2u[unit][GMT_PT];
1108 				i = strlen (line) - 1;	/* So that when i++ happens at the end of the for loop we will exit */
1109 			}
1110 			else {
1111 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Pen attributes not using just - and . for dashes and dots. Offending character --> %c\n", line[i]);
1112 				return GMT_PARSE_ERROR;
1113 			}
1114 		}
1115 		P->style[strlen(P->style)-1] = '\0';	/* Chop off trailing space */
1116 	}
1117 	if (n_dash >= 11) {
1118 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Pen attributes contain more than 11 items (limit for PostScript setdash operator)!\n");
1119 		return GMT_PARSE_ERROR;
1120 	}
1121 	return (GMT_NOERROR);
1122 }
1123 
1124 /*! . */
gmtsupport_is_penstyle(char * word)1125 GMT_LOCAL bool gmtsupport_is_penstyle (char *word) {
1126 	int n;
1127 
1128 	/* Returns true if we are sure the word is a style string - else false.
1129 	 * style syntax is a|o|<pattern>[:<phase>]|<string made up of -|. only>
1130 	 * Also recognized "dashed" for -, "dotted" for . as well as "solid" */
1131 
1132 	n = (int)strlen (word);
1133 	if (n == 0) return (false);
1134 
1135 	if (!strncmp (word, "dotdash", 7U) || !strncmp (word, "dashdot", 7U) \
1136 	    || !strncmp (word, "dash", 4U) || !strncmp (word, "dot", 3U) \
1137 	    || !strncmp (word, "solid", 5U)) return (true);
1138 
1139 	n--;
1140 	if (strchr (GMT_DIM_UNITS, word[n])) n--;	/* Reduce length by 1; the unit character */
1141 	if (n < 0) return (false);		/* word only contained a unit character? */
1142 	if (n == 0) {
1143 		if (word[0] == '-' || word[0] == 'a' || word[0] == '.' || word[0] == 'o') return (true);
1144 		return (false);	/* No other 1-char style patterns possible */
1145 	}
1146 	if (strchr(word,'t')) return (false);	/* Got a t somewhere */
1147 	if (strchr(word,':')) return (true);	/* Got <pattern>:<phase> */
1148 	if (strchr(word,'_')) {	/* Got <pattern> without optional :<phase>, add :0 */
1149 		strcat (word, ":0");
1150 		return (true);
1151 	}
1152 	while (n >= 0 && (word[n] == '-' || word[n] == '.')) n--;	/* Wind down as long as we find - or . */
1153 	return ((n == -1));	/* true if we only found -/., false otherwise */
1154 }
1155 
1156 /*! . */
gmtsupport_free_range(struct GMT_CTRL * GMT,struct GMT_PALETTE_HIDDEN * H,struct GMT_LUT * S)1157 GMT_LOCAL void gmtsupport_free_range  (struct GMT_CTRL *GMT, struct GMT_PALETTE_HIDDEN *H, struct GMT_LUT *S) {
1158 	if (H->alloc_mode_text[GMT_CPT_INDEX_LBL]) gmt_M_str_free (S->label);
1159 	if (H->alloc_mode_text[GMT_CPT_INDEX_KEY]) gmt_M_str_free (S->key);
1160 	gmt_M_free (GMT, S->fill);
1161 }
1162 
1163 /*! . */
gmtsupport_copy_palette_hdrs(struct GMT_CTRL * GMT,struct GMT_PALETTE * P_to,struct GMT_PALETTE * P_from)1164 GMT_LOCAL void gmtsupport_copy_palette_hdrs (struct GMT_CTRL *GMT, struct GMT_PALETTE *P_to, struct GMT_PALETTE *P_from) {
1165 	unsigned int hdr;
1166 	P_to->header = NULL;
1167 	if (P_from->n_headers == 0) return;	/* Nothing to do */
1168 	/* Must duplicate the header records */
1169 	P_to->n_headers = P_from->n_headers;
1170 	if (P_to->n_headers) P_to->header = gmt_M_memory (GMT, NULL, P_from->n_headers, char *);
1171 	for (hdr = 0; hdr < P_from->n_headers; hdr++) P_to->header[hdr] = strdup (P_from->header[hdr]);
1172 }
1173 
1174 /*! Decode the optional +u|U<unit> and determine scales and +h[<z>] for hinge control */
gmtsupport_cpt_parse(struct GMT_CTRL * GMT,char * file,unsigned int direction,unsigned int * hinge_mode,double * z_hinge)1175 GMT_LOCAL struct CPT_Z_SCALE *gmtsupport_cpt_parse (struct GMT_CTRL *GMT, char *file, unsigned int direction, unsigned int *hinge_mode, double *z_hinge) {
1176 	/* CPT file arg is <file>[+h[<hinge>]][+u|U<unit>]
1177 	 * The +h modifier is used to turn a soft hinge in a CPT to a hard hinge at the user-selected z-value [0]
1178 	 * or to adjust the location of the hinge for an hard hinge CPT.
1179 	 * Note: The +i<inc> modifier is dealt with and removed in those modules that support it, hence not checked here. */
1180 	enum gmt_enum_units u_number;
1181 	unsigned int mode = 0;
1182 	char *c = NULL, *f = NULL, *m = NULL;
1183 	struct CPT_Z_SCALE *Z = NULL;
1184 	gmt_M_unused(direction);
1185 
1186 	if ((f = gmt_strrstr (file, GMT_CPT_EXTENSION)))	/* Got a file with CPT extension, look from there on out */
1187 		m = gmtlib_last_valid_file_modifier (GMT->parent, f, GMT_CPTFILE_MODIFIERS);
1188 	else	/* Must use the full file name */
1189 		m = gmtlib_last_valid_file_modifier (GMT->parent, file, GMT_CPTFILE_MODIFIERS);
1190 	*hinge_mode = 0;	/* Default is no hinge modifier */
1191 	if (m && (c = strstr (m, "+h"))) {	/* Gave hinge modifier, examine and set parameters */
1192 		if (c[2]) {	/* Gave hinge value for soft hinge */
1193 			if (gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_Z), gmt_scanf (GMT, &c[2], gmt_M_type (GMT, GMT_IN, GMT_Z), z_hinge), &c[2])) {
1194 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmtsupport_cpt_parse: CPT hinge modifier %s was not successfully parsed and is ignored.\n", c);
1195 			}
1196 			else {	/* Parsed successfully, this turns a soft CPT hinge to a hard user hinge */
1197 				c[0] = '\0';	/* Chop off the hinge specification from the file name */
1198 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtsupport_cpt_parse: CPT hard hinge was added at z = %s for file %s\n", &c[2], file);
1199 				*hinge_mode = 1;
1200 			}
1201 		}
1202 		else {	/* Accept zero as hard hinge value */
1203 			*z_hinge = 0.0;
1204 			*hinge_mode = 1;
1205 			c[0] = '\0';	/* Chop off the hinge specification from the file name */
1206 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtsupport_cpt_parse: CPT CPT hard hinge was added at z = 0 for file %s\n", file);
1207 		}
1208 	}
1209 
1210 	if (m == NULL || (c = gmtlib_cptfile_unitscale (GMT->parent, m)) == NULL) return NULL;	/* Did not find any +u|U modifiers */
1211 	/* Here, c is assigned */
1212 	mode = (c[1] == 'u') ? 0 : 1;
1213 	u_number = gmtlib_get_unit_number (GMT, c[2]);		/* Convert char unit to enumeration constant for this unit */
1214 	if (u_number == GMT_IS_NOUNIT) {
1215 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "CPT z unit specification %s was unrecognized (part of file name?) and is ignored.\n", c);
1216 		return NULL;
1217 	}
1218 	/* Got a valid unit */
1219 	Z = gmt_M_memory (GMT, NULL, 1, struct CPT_Z_SCALE);
1220 	Z->z_unit_to_meter = GMT->current.proj.m_per_unit[u_number];	/* Converts unit to meters */
1221 	if (mode) Z->z_unit_to_meter = 1.0 / Z->z_unit_to_meter;	/* Wanted the inverse */
1222 	Z->z_unit = u_number;	/* Unit ID */
1223 	Z->z_adjust |= 1;		/* Says we have successfully parsed and readied the x/y scaling */
1224 	Z->z_mode = mode;
1225 	c[0] = '\0';	/* Chop off the unit specification from the file name */
1226 	return (Z);
1227 }
1228 
1229 /*! . */
gmtsupport_find_cpt_hinge(struct GMT_CTRL * GMT,struct GMT_PALETTE * P)1230 GMT_LOCAL int gmtsupport_find_cpt_hinge (struct GMT_CTRL *GMT, struct GMT_PALETTE *P) {
1231 	/* Return the slice number where z_low' = 0 for a CPT with hinge and normalized z' = -1 to +1 */
1232 	unsigned int k;
1233 	if (!P->has_hinge) return GMT_NOTSET;	/* Does not have any hinge */
1234 	for (k = 0; k < P->n_colors; k++) if (doubleAlmostEqualZero (0.0, P->data[k].z_low)) {
1235 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found CPT hinge at z' = 0 for slice k = %u!\n", k);
1236 		return (int)k;
1237 	}
1238 	return GMT_NOTSET;
1239 }
1240 
1241 /*! . */
gmtsupport_cpt_z_scale(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,struct CPT_Z_SCALE * Z,unsigned int direction)1242 GMT_LOCAL void gmtsupport_cpt_z_scale (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, struct CPT_Z_SCALE *Z, unsigned int direction) {
1243 	unsigned int k;
1244 	double scale = 1.0;
1245 	struct GMT_PALETTE_HIDDEN *PH = gmt_get_C_hidden (P);
1246 	/* Apply the scaling of z as given by the header's z_* settings.
1247 	 * After reading a cpt it will have z in meters.
1248 	 * Before writing a cpt, it may have units changed back to original units
1249 	 * or scaled to another set of units */
1250 
1251 	if (direction == GMT_IN) {
1252 		if (Z) {
1253 			PH->z_adjust[GMT_IN] = Z->z_adjust; PH->z_unit[GMT_IN] = Z->z_unit;
1254 			PH->z_mode[GMT_IN] = Z->z_mode; PH->z_unit_to_meter[GMT_IN] = Z->z_unit_to_meter;
1255 		}
1256 		if (PH->z_adjust[GMT_IN] == 0) return;	/* Nothing to do */
1257 		if (PH->z_adjust[GMT_IN] & 2)  return;	/* Already scaled them */
1258 		scale = PH->z_unit_to_meter[GMT_IN];	/* To multiply all z-related entries in the CPT */
1259 		PH->z_adjust[GMT_IN] = 2;	/* Now the cpt is ready for use and in meters */
1260 		if (PH->z_mode[GMT_IN])
1261 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Input CPT z unit was converted from meters to %s after reading.\n", GMT->current.proj.unit_name[PH->z_unit[GMT_IN]]);
1262 		else
1263 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Input CPT z unit was converted from %s to meters after reading.\n", GMT->current.proj.unit_name[PH->z_unit[GMT_IN]]);
1264 	}
1265 	else if (direction == GMT_OUT) {	/* grid x/y are assumed to be in meters */
1266 		if (Z) {PH->z_adjust[GMT_OUT] = Z->z_adjust; PH->z_unit[GMT_OUT] = Z->z_unit; PH->z_mode[GMT_OUT] = Z->z_mode; PH->z_unit_to_meter[GMT_OUT] = Z->z_unit_to_meter; }
1267 		if (PH->z_adjust[GMT_OUT] & 1) {	/* Was given a new unit for output */
1268 			scale = 1.0 / PH->z_unit_to_meter[GMT_OUT];
1269 			PH->z_adjust[GMT_OUT] = 2;	/* Now we are ready for writing */
1270 			if (PH->z_mode[GMT_OUT])
1271 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output CPT z unit was converted from %s to meters before writing.\n", GMT->current.proj.unit_name[PH->z_unit[GMT_OUT]]);
1272 			else
1273 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output CPT z unit was converted from meters to %s before writing.\n", GMT->current.proj.unit_name[PH->z_unit[GMT_OUT]]);
1274 		}
1275 		else if (PH->z_adjust[GMT_IN] & 2) {	/* Just undo old scaling */
1276 			scale = 1.0 / PH->z_unit_to_meter[GMT_IN];
1277 			PH->z_adjust[GMT_IN] -= 2;	/* Now it is back to where we started */
1278 			if (PH->z_mode[GMT_OUT])
1279 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output CPT z unit was reverted back to %s from meters before writing.\n", GMT->current.proj.unit_name[PH->z_unit[GMT_IN]]);
1280 			else
1281 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output CPT z unit was reverted back from meters to %s before writing.\n", GMT->current.proj.unit_name[PH->z_unit[GMT_IN]]);
1282 		}
1283 	}
1284 	/* If we got here we must scale the CPT's z-values */
1285 	for (k = 0; k < P->n_colors; k++) {
1286 		P->data[k].z_high *= scale;
1287 		P->data[k].z_low  *= scale;
1288 		P->data[k].i_dz   /= scale;
1289 	}
1290 }
1291 
1292 /*! . */
gmtsupport_truncate_cpt_slice(struct GMT_LUT * S,bool do_hsv,double z_cut,int side)1293 GMT_LOCAL void gmtsupport_truncate_cpt_slice (struct GMT_LUT *S, bool do_hsv, double z_cut, int side) {
1294 	/* Interpolate this slice to the new low or high value, depending on side */
1295 	double f = (z_cut - S->z_low) * S->i_dz, hsv[4], rgb[4];
1296 	unsigned int k;
1297 	if (do_hsv) {	/* Interpolation in HSV space */
1298 		for (k = 0; k < 4; k++) hsv[k] = S->hsv_low[k] + S->hsv_diff[k] * f;
1299 		gmt_hsv_to_rgb (rgb, hsv);
1300 	}
1301 	else {	/* Interpolation in RGB space */
1302 		for (k = 0; k < 4; k++) rgb[k] = S->rgb_low[k] + S->rgb_diff[k] * f;
1303 		gmt_rgb_to_hsv (rgb, hsv);
1304 	}
1305 	if (side == -1) {
1306 		gmt_M_memcpy (S->hsv_low, hsv, 4, double);
1307 		gmt_M_memcpy (S->rgb_low, rgb, 4, double);
1308 		S->z_low = z_cut;
1309 	}
1310 	else {	/* Last slice */
1311 		gmt_M_memcpy (S->hsv_high, hsv, 4, double);
1312 		gmt_M_memcpy (S->rgb_high, rgb, 4, double);
1313 		S->z_high = z_cut;
1314 	}
1315 	/* Recompute differences used in gmt_get_rgb_from_z */
1316 	for (k = 0; k < 4; k++) S->rgb_diff[k] = S->rgb_high[k] - S->rgb_low[k];
1317 	for (k = 0; k < 4; k++) S->hsv_diff[k] = S->hsv_high[k] - S->hsv_low[k];
1318 	/* Recompute inverse stepsize */
1319 	S->i_dz = 1.0 / (S->z_high - S->z_low);
1320 }
1321 
gmt_consider_current_cpt(struct GMTAPI_CTRL * API,bool * active,char ** arg)1322 bool gmt_consider_current_cpt (struct GMTAPI_CTRL *API, bool *active, char **arg) {
1323 	/* Modern mode only: Detect if no CPT is given but -C was set.
1324 	 * If -C[+u|U<arg>] is given (i.e., no cpt) then we take that to mean
1325 	 * we want to use the current CPT.  If no current CPT exist then we do nothing
1326 	 * and the module with fail or create a CPT as needed. */
1327 	char *cpt = NULL;
1328 	bool ret = true;
1329 
1330 	if (API->GMT->current.setting.run_mode == GMT_CLASSIC) return false;	/* No such thing in classic mode */
1331 	if (!(*active)) return false;		/* Did not give that option so nothing to do */
1332 	if (arg == NULL) return false;	/* No text pointer to work with */
1333 
1334 	if (gmt_M_cpt_mod (*arg)) {	/* Gave modifiers for a unit change) */
1335 		char string[PATH_MAX] = {""};
1336 		if ((cpt = gmt_get_current_item (API->GMT, "cpt", false)) == NULL) return false;	/* No current CPT */
1337 		snprintf (string, PATH_MAX, "%s%s", cpt, *arg);	/* Append the modifiers to the current CPT name */
1338 		gmt_M_str_free (cpt);
1339 		gmt_M_str_free (*arg);
1340 		*arg = strdup (string);		/* Pass back the name of the current CPT with modifiers */
1341 	}
1342 	else if (*arg == NULL) {	/* Noting given */
1343 		if ((cpt = gmt_get_current_item (API->GMT, "cpt", false)) == NULL) return false;	/* No current CPT */
1344 		*arg = cpt;		/* Pass back the name of the current CPT */
1345 	}
1346 	else /* Got something already */
1347 		ret = false;
1348 	return ret;
1349 }
1350 
gmt_set_interpolate_mode(struct GMT_CTRL * GMT,unsigned int mode,unsigned int type)1351 unsigned int gmt_set_interpolate_mode (struct GMT_CTRL *GMT, unsigned int mode, unsigned int type) {
1352 	/* Convenience function that hides away the embedding of mode and type via the GMT_SPLINE_SLOPE factor */
1353 	gmt_M_unused (GMT);
1354 	return (mode + GMT_SPLINE_SLOPE * type);
1355 }
1356 
1357 /*! . */
gmtsupport_csplint(struct GMT_CTRL * GMT,double * x,double * y,double * c,double xp,uint64_t klo)1358 GMT_LOCAL double gmtsupport_csplint (struct GMT_CTRL *GMT, double *x, double *y, double *c, double xp, uint64_t klo) {
1359 	uint64_t khi;
1360 	double h, ih, b, a, yp;
1361 	gmt_M_unused(GMT);
1362 
1363 	khi = klo + 1;
1364 	h = x[khi] - x[klo];
1365 	ih = 1.0 / h;
1366 	a = (x[khi] - xp) * ih;
1367 	b = (xp - x[klo]) * ih;
1368 	yp = a * y[klo] + b * y[khi] + ((a*a*a - a) * c[klo] + (b*b*b - b) * c[khi]) * (h*h) / 6.0;
1369 
1370 	return (yp);
1371 }
1372 
1373 /*! . */
gmtsupport_csplintslp(struct GMT_CTRL * GMT,double * x,double * y,double * c,double xp,uint64_t klo)1374 GMT_LOCAL double gmtsupport_csplintslp (struct GMT_CTRL *GMT, double *x, double *y, double *c, double xp, uint64_t klo) {
1375 	/* As gmtsupport_csplint but returns the first derivative instead */
1376 	uint64_t khi;
1377 	double h, ih, b, a, dypdx;
1378 	gmt_M_unused(GMT);
1379 
1380 	khi = klo + 1;
1381 	h = x[khi] - x[klo];
1382 	ih = 1.0 / h;
1383 	a = (x[khi] - xp) * ih;
1384 	b = (xp - x[klo]) * ih;
1385 	dypdx = ih * (y[khi] - y[klo]) + ((3.0*b*b - 1.0) * c[khi] - (3.0*a*a - 1.0) * c[klo]) * h / 6.0;
1386 
1387 	return (dypdx);
1388 }
1389 
1390 /*! . */
gmtsupport_csplintcurv(struct GMT_CTRL * GMT,double * x,double * c,double xp,uint64_t klo)1391 GMT_LOCAL double gmtsupport_csplintcurv (struct GMT_CTRL *GMT, double *x, double *c, double xp, uint64_t klo) {
1392 	/* As gmtsupport_csplint but returns the 2nd derivative instead */
1393 	uint64_t khi;
1394 	double h, ih, b, a, d2ypdx2;
1395 	gmt_M_unused(GMT);
1396 
1397 	khi = klo + 1;
1398 	h = x[khi] - x[klo];
1399 	ih = 1.0 / h;
1400 	a = (x[khi] - xp) * ih;
1401 	b = (xp - x[klo]) * ih;
1402 	d2ypdx2 = -(b * c[khi] + a * c[klo]);
1403 
1404 	return (d2ypdx2);
1405 }
1406 
1407 /*! . */
gmtlib_smooth_spline(struct GMT_CTRL * GMT,double * x,double * y,double * w,double p,uint64_t n,int deriv,double * a)1408 int gmtlib_smooth_spline (struct GMT_CTRL *GMT, double *x, double *y, double *w, double p, uint64_t n, int deriv, double *a) {
1409 	/* Implements a smoothing splines as per Lancaster & Salkauskas [1980].
1410 	 * w is optional weights or NULL, p is fit parameter. The x values are monotonic */
1411 	int error;
1412 	uint64_t n3 = n - 3, n2 = n - 2, n1 = n - 1, k, kk, kkt;
1413 	double ip = 1.0 / p, sum_w = 0;
1414 	double *D = NULL, *Dt = NULL, *B = NULL, *K = NULL, *Q = NULL;
1415 	double *lambda = NULL, *dtau = NULL, *inv_dtau = NULL, *c = NULL, *s = NULL;
1416 
1417 	/* Create the inverse lambda diagonal vector (assume w[k] = sigma_k) */
1418 	lambda = gmt_M_memory (GMT, NULL, n, double);
1419 	for (k = 0; k < n; k++) {
1420 		lambda[k] = (w == NULL) ? 1.0 : w[k] * w[k];
1421 		sum_w += lambda[k];
1422 	}
1423 	for (k = 0; k < n; k++) {
1424 		lambda[k] /= sum_w;	/* Normalize weights to sum to 1 */
1425 		lambda[k] *= ip;
1426 	}
1427 	/* Create dtau and inv_dtau vectors */
1428 	dtau = gmt_M_memory (GMT, NULL, n1, double);
1429 	inv_dtau = gmt_M_memory (GMT, NULL, n1, double);
1430 	for (k = 0; k < n1; k++) {
1431 		dtau[k] = x[k+1] - x[k];
1432 		inv_dtau[k] = 1.0 / dtau[k];
1433 	}
1434 	/* Create B matrix */
1435 	B = gmt_M_memory (GMT, NULL, n2*n2, double);
1436 	for (k = 0; k < n2; k++) {	/* For rows 0 to n-3, a total of n-2 rows in B */
1437 		kk = k * n2 + k;	/* Diagonal index */
1438 		B[kk] = (dtau[k] + dtau[k+1]) / 3.0;
1439 		if (k > 0) B[kk-1] = dtau[k] / 6.0;	/* All but first row has entry to left of diagonal */
1440 		if (k < n3) B[kk+1] = dtau[k+1] / 6.0;	/* All but last row has entry to right of diagonal */
1441 	}
1442 
1443 	/* Create D and Dt matrix and temp matrix K = inv_lambd * Dt */
1444 	D  = gmt_M_memory (GMT, NULL, n2*n, double);
1445 	Dt = gmt_M_memory (GMT, NULL, n*n2, double);
1446 	K  = gmt_M_memory (GMT, NULL, n2*n, double);
1447 	for (k = 0; k < n2; k++) {	/* For rows 0 to n-3, a total of n-2 rows in D */
1448 		kk = k * n + k;	/* Diagonal index in D */
1449 		kkt = k * n2 + k;	/* Diagonal index in transpose */
1450 		D[kk] = Dt[kkt] = inv_dtau[k];
1451 		D[kk+1] = Dt[kkt+n2] = -(inv_dtau[k] + inv_dtau[k+1]);
1452 		D[kk+2] = Dt[kkt+2*n2] = inv_dtau[k+1];
1453 		K[kkt] = Dt[kkt] * lambda[k];
1454 		K[kkt+n2] = Dt[kkt+n2] * lambda[k+1];
1455 		K[kkt+2*n2] = Dt[kkt+2*n2] * lambda[k+2];
1456 	}
1457 	/* Need to compute Q = D * K */
1458 	Q = gmt_M_memory (GMT, NULL, n2*n2, double);
1459 	gmt_matrix_matrix_mult (GMT, D, K, n2, n, n2, Q);
1460 	/* Now add B to Q */
1461 	gmt_matrix_matrix_add (GMT, Q, B, n2, n2, Q);
1462 	/* Set c = D * y */
1463 	c = gmt_M_memory (GMT, NULL, n, double);
1464 	gmt_matrix_matrix_mult (GMT, D, y, n2, n, 1U, c);
1465 	/* Solve Q*x = c, with x returned in c; these are the curvatures (s" in the notes) */
1466 	error = gmt_gauss (GMT, Q, c, n2, n2, true);
1467 	if (error) {
1468 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Gauss returns error code %d\n", error);
1469 		error = GMT_RUNTIME_ERROR;
1470 		goto END_IT;
1471 	}
1472 	/* Set Q = K * c  which is a vector of length n */
1473 	gmt_matrix_matrix_mult (GMT, K, c, n, n2, 1, Q);
1474 	/* Solution for s = y - Q as in the notes */
1475 	s = gmt_M_memory (GMT, NULL, n, double);
1476 	for (k = 0; k < n; k++) s[k] = y[k] - Q[k];
1477 	/* Adjust c (n-2 items) by adding the first and last c = conditions */
1478 	for (k = n2; k > 1; k--) c[k] = c[k-1];
1479 	c[0] = c[n-1] = 0.0;	/* THe natural BCs */
1480 	/* Now build the coefficient matrix a (5.33) with y = s and c = s" */
1481 	for (k = kk = 0; k < n1; k++) {	/* The internal segments */
1482 		a[kk++] = c[k] * inv_dtau[k] / 6.0;
1483 		a[kk++] = c[k+1] * inv_dtau[k] / 6.0;
1484 		a[kk++] = s[k] * inv_dtau[k] - c[k] * dtau[k] / 6.0;
1485 		a[kk++] = s[k+1] * inv_dtau[k] - c[k+1] * dtau[k] / 6.0;
1486 	}
1487 	if (deriv == 1) {	/* Take first derivative and update coefficients */
1488 		for (k = kk = 0; k < n1; k++) {	/* The internal segments */
1489 			a[kk++] *= -3;
1490 			a[kk++] *= +3;
1491 			a[kk] = a[kk+1] - a[kk];
1492 			kk++;
1493 			a[kk++] = 0.0;
1494 		}
1495 	}
1496 	else if (deriv == 2) {	/* Take second derivative and update coefficients */
1497 		for (k = kk = 0; k < n1; k++) {	/* The internal segments */
1498 			a[kk++] *= 6;
1499 			a[kk++] *= 6;
1500 			a[kk++] = 0.0;
1501 			a[kk++] = 0.0;
1502 		}
1503 	}
1504 END_IT:
1505 	gmt_M_free (GMT, c);
1506 	gmt_M_free (GMT, s);
1507 	gmt_M_free (GMT, lambda);
1508 	gmt_M_free (GMT, dtau);
1509 	gmt_M_free (GMT, inv_dtau);
1510 	gmt_M_free (GMT, Q);
1511 	gmt_M_free (GMT, K);
1512 	gmt_M_free (GMT, D);
1513 	gmt_M_free (GMT, Dt);
1514 	gmt_M_free (GMT, B);
1515 
1516 	return (error);
1517 }
1518 
1519 /*! . */
gmtsupport_intpol_sub(struct GMT_CTRL * GMT,double * x,double * y,double * w,uint64_t n,uint64_t m,double * u,double * v,double p,int mode)1520 GMT_LOCAL int gmtsupport_intpol_sub (struct GMT_CTRL *GMT, double *x, double *y, double *w, uint64_t n, uint64_t m, double *u, double *v, double p, int mode) {
1521 	/* Does the main work of interpolating a section that has no NaNs */
1522 	uint64_t i, j;
1523 	int err_flag = 0, spline_mode = mode % GMT_SPLINE_SLOPE, deriv = mode / GMT_SPLINE_SLOPE;
1524 	double dx, dx1, dx2, x_min, x_max, *c = NULL;
1525 
1526 	/* Set minimum and maximum */
1527 
1528 	if (spline_mode == GMT_SPLINE_NN) {
1529 		x_min = (3*x[0] - x[1]) / 2;
1530 		x_max = (3*x[n-1] - x[n-2]) / 2;
1531 	}
1532 	else {
1533 		x_min = x[0]; x_max = x[n-1];
1534 	}
1535 
1536 	/* Allocate memory for spline factors */
1537 
1538 	if (spline_mode == GMT_SPLINE_AKIMA) {	/* Akima's spline */
1539 		c = gmt_M_memory (GMT, NULL, 3*n, double);
1540 		err_flag = gmtlib_akima (GMT, x, y, n, c);
1541 	}
1542 	else if (spline_mode == GMT_SPLINE_CUBIC) {	/* Natural cubic spline */
1543 		c = gmt_M_memory (GMT, NULL, 3*n, double);
1544 		err_flag = gmtlib_cspline (GMT, x, y, n, c);
1545 	}
1546 	else if (spline_mode == GMT_SPLINE_SMOOTH) {	/* Smoothing spline */
1547 		c = gmt_M_memory (GMT, NULL, 4*n, double);
1548 		err_flag = gmtlib_smooth_spline (GMT, x, y, w, p, n, deriv, c);
1549 	}
1550 	if (err_flag != 0) {
1551 		gmt_M_free (GMT, c);
1552 		return (err_flag);
1553 	}
1554 
1555 	/* Compute the interpolated values from the coefficients */
1556 
1557 	j = 0;
1558 	for (i = 0; i < m; i++) {
1559 		if (u[i] < x_min || u[i] > x_max) {	/* Desired point outside data range */
1560 			if (GMT->current.setting.extrapolate_val[0] == GMT_EXTRAPOLATE_NONE) {
1561 				v[i] = GMT->session.d_NaN;
1562 				continue;
1563 			}
1564 			else if (GMT->current.setting.extrapolate_val[0] == GMT_EXTRAPOLATE_CONSTANT) {
1565 				v[i] = GMT->current.setting.extrapolate_val[1];
1566 				continue;
1567 			}
1568 		}
1569 		while (j > 0 && x[j] >  u[i]) j--;	/* In case u is not sorted */
1570 		while (j < n && x[j] <= u[i]) j++;
1571 		if (j == n) j--;
1572 		if (j > 0) j--;
1573 
1574 		switch (mode) {
1575 			case GMT_SPLINE_LINEAR:	/* Linear spline v(u) */
1576 				dx = u[i] - x[j];
1577 				v[i] = (y[j+1]-y[j])*dx/(x[j+1]-x[j]) + y[j];
1578 				break;
1579 			case GMT_SPLINE_AKIMA:	/* Akime's spline u(v) */
1580 				dx = u[i] - x[j];
1581 				v[i] = ((c[3*j+2]*dx + c[3*j+1])*dx + c[3*j])*dx + y[j];
1582 				break;
1583 			case GMT_SPLINE_CUBIC:	/* Natural cubic spline u(v) */
1584 				v[i] = gmtsupport_csplint (GMT, x, y, c, u[i], j);
1585 				break;
1586 			case GMT_SPLINE_NN:	/* Nearest neighbor value */
1587 				v[i] = ((u[i] - x[j]) < (x[j+1] - u[i])) ? y[j] : y[j+1];
1588 				break;
1589 			case GMT_SPLINE_LINEAR+GMT_SPLINE_SLOPE:	/* Linear spline v'(u) */
1590 				v[i] = (y[j+1]-y[j])/(x[j+1]-x[j]);
1591 				break;
1592 			case GMT_SPLINE_AKIMA+GMT_SPLINE_SLOPE:	/* Akime's spline u'(v) */
1593 				dx = u[i] - x[j];
1594 				v[i] = (3.0*c[3*j+2]*dx + 2.0*c[3*j+1])*dx + c[3*j];
1595 				break;
1596 			case GMT_SPLINE_CUBIC+GMT_SPLINE_SLOPE:	/* Natural cubic spline u'(v) */
1597 				v[i] = gmtsupport_csplintslp (GMT, x, y, c, u[i], j);
1598 				break;
1599 			case GMT_SPLINE_NN+GMT_SPLINE_SLOPE:	/* Nearest neighbor slopes are zero */
1600 				v[i] = 0.0;
1601 				break;
1602 			case GMT_SPLINE_LINEAR+GMT_SPLINE_CURVATURE:	/* Linear spline v"(u) */
1603 				v[i] = 0.0;
1604 				break;
1605 			case GMT_SPLINE_AKIMA+GMT_SPLINE_CURVATURE:	/* Akime's spline u"(v) */
1606 				dx = u[i] - x[j];
1607 				v[i] = 6.0*c[3*j+2]*dx + 2.0*c[3*j+1];
1608 				break;
1609 			case GMT_SPLINE_CUBIC+GMT_SPLINE_CURVATURE:	/* Natural cubic spline u"(v) */
1610 				v[i] = gmtsupport_csplintcurv (GMT, x, c, u[i], j);
1611 				break;
1612 			case GMT_SPLINE_NN+GMT_SPLINE_CURVATURE:	/* Nearest neighbor curvatures are zero  */
1613 				v[i] = 0.0;
1614 				break;
1615 			case GMT_SPLINE_SMOOTH:	/* Smoothing spline */
1616 				dx1 = x[j+1] - u[i];
1617 				dx2 = u[i] - x[j];
1618 				v[i] = c[4*j] * pow (dx1, 3.0) + c[4*j+1] * pow (dx2, 3.0) + c[4*j+2] * dx1 + c[4*j+3] * dx2;
1619 				break;
1620 			case GMT_SPLINE_SMOOTH+GMT_SPLINE_SLOPE:	/* Derivative of smoothing spline */
1621 				dx1 = x[j+1] - u[i];
1622 				dx2 = u[i] - x[j];
1623 				v[i] = c[4*j] * pow (dx1, 2.0) + c[4*j+1] * pow (dx2, 2.0) + c[4*j+2];
1624 				break;
1625 			case GMT_SPLINE_SMOOTH+GMT_SPLINE_CURVATURE:	/* Curvature of smoothing spline */
1626 				dx1 = x[j+1] - u[i];
1627 				dx2 = u[i] - x[j];
1628 				v[i] = c[4*j] * dx1 + c[4*j+1] * dx2;
1629 				break;
1630 		}
1631 	}
1632 	gmt_M_free (GMT, c);
1633 
1634 	return (GMT_NOERROR);
1635 }
1636 
1637 /*! . */
gmtsupport_intpol_reverse(double * x,double * u,uint64_t n,uint64_t m)1638 GMT_LOCAL void gmtsupport_intpol_reverse (double *x, double *u, uint64_t n, uint64_t m) {
1639 	/* Changes sign on x and u */
1640 	uint64_t i;
1641 	for (i = 0; i < n; i++) x[i] = -x[i];
1642 	for (i = 0; i < m; i++) u[i] = -u[i];
1643 }
1644 
1645 /*! Non-square matrix transpose of matrix of size r x c and base address A */
gmtsupport_is_set(unsigned int * mark,uint64_t k,unsigned int * bits)1646 GMT_LOCAL unsigned int gmtsupport_is_set (unsigned int *mark, uint64_t k, unsigned int *bits) {
1647 	/* Return nonzero if this bit is set */
1648 	uint64_t w = k / 32ULL;
1649 	unsigned int b = (unsigned int)(k % 32ULL);
1650 	return (mark[w] & bits[b]);
1651 }
1652 
1653 /*! . */
gmtsupport_set_bit(unsigned int * mark,uint64_t k,unsigned int * bits)1654 GMT_LOCAL void gmtsupport_set_bit (unsigned int *mark, uint64_t k, unsigned int *bits) {
1655 	/* Set this bit */
1656  	uint64_t w = k / 32ULL;
1657 	unsigned int b = (unsigned int)(k % 32ULL);
1658  	mark[w] |= bits[b];
1659 }
1660 
1661 /*! . */
gmtsupport_decorate_free(struct GMT_CTRL * GMT,struct GMT_DECORATE * G)1662 GMT_LOCAL void gmtsupport_decorate_free (struct GMT_CTRL *GMT, struct GMT_DECORATE *G) {
1663 	/* Free memory used by decorate */
1664 
1665 	GMT_Destroy_Data (GMT->parent, &(G->X));
1666 	if (G->f_n) {	/* Array for fixed points */
1667 		gmt_M_free (GMT, G->f_xy[GMT_X]);
1668 		gmt_M_free (GMT, G->f_xy[GMT_Y]);
1669 	}
1670 }
1671 
1672 /*! . */
gmtsupport_code_to_lonlat(struct GMT_CTRL * GMT,char * code,double * lon,double * lat)1673 GMT_LOCAL int gmtsupport_code_to_lonlat (struct GMT_CTRL *GMT, char *code, double *lon, double *lat) {
1674 	int i, n, error = 0;
1675 	bool z_OK = false;
1676 
1677 	n = (int)strlen (code);
1678 	if (n != 2) return (1);
1679 
1680 	for (i = 0; i < 2; i++) {
1681 		switch (code[i]) {
1682 			case 'l':
1683 			case 'L':	/* Left */
1684 				*lon = GMT->common.R.wesn[XLO];
1685 				break;
1686 			case 'c':
1687 			case 'C':	/* center */
1688 				*lon = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
1689 				break;
1690 			case 'r':
1691 			case 'R':	/* right */
1692 				*lon = GMT->common.R.wesn[XHI];
1693 				break;
1694 			case 'b':
1695 			case 'B':	/* bottom */
1696 				*lat = GMT->common.R.wesn[YLO];
1697 				break;
1698 			case 'm':
1699 			case 'M':	/* center */
1700 				*lat = 0.5 * (GMT->common.R.wesn[YLO] + GMT->common.R.wesn[YHI]);
1701 				break;
1702 			case 't':
1703 			case 'T':	/* top */
1704 				*lat = GMT->common.R.wesn[YHI];
1705 				break;
1706 			case 'z':
1707 			case 'Z':	/* z-value */
1708 				z_OK = true;
1709 				break;
1710 			case '+':	/* zmax-location */
1711 				if (z_OK)
1712 					*lon = *lat = DBL_MAX;
1713 				else
1714 					error++;
1715 				break;
1716 			case '-':	/* zmin-location */
1717 				if (z_OK)
1718 					*lon = *lat = -DBL_MAX;
1719 				else
1720 					error++;
1721 				break;
1722 			default:
1723 				error++;
1724 				break;
1725 		}
1726 	}
1727 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized location code %s\n", code);
1728 
1729 	return (error);
1730 }
1731 
1732 /*! . */
gmtsupport_determine_endpoint(struct GMT_CTRL * GMT,double x0,double y0,double length,double az,double * x1,double * y1)1733 GMT_LOCAL double gmtsupport_determine_endpoint (struct GMT_CTRL *GMT, double x0, double y0, double length, double az, double *x1, double *y1) {
1734 	/* compute point a distance length from origin along azimuth, return point separation */
1735 	double s_az, c_az;
1736 	sincosd (az, &s_az, &c_az);
1737 	if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* Spherical solution */
1738 		double s0, c0, s_d, c_d;
1739 		double d = (length / GMT->current.map.dist[GMT_MAP_DIST].scale);	/* Convert from chosen unit to meter or degree */
1740 		if (!GMT->current.map.dist[GMT_MAP_DIST].arc) d /= GMT->current.proj.DIST_M_PR_DEG;	/* Convert meter to spherical degrees */
1741 		sincosd (y0, &s0, &c0);
1742 		sincosd (d, &s_d, &c_d);
1743 		*x1 = x0 + atand (s_d * s_az / (c0 * c_d - s0 * s_d * c_az));
1744 		*y1 = d_asind (s0 * c_d + c0 * s_d * c_az);
1745 	}
1746 	else {	/* Cartesian solution */
1747 		*x1 = x0 + length * s_az;
1748 		*y1 = y0 + length * c_az;
1749 	}
1750 	return (gmt_distance (GMT, x0, y0, *x1, *y1));
1751 }
1752 
1753 /*! . */
gmtsupport_determine_endpoints(struct GMT_CTRL * GMT,double x[],double y[],double length,double az)1754 GMT_LOCAL double gmtsupport_determine_endpoints (struct GMT_CTRL *GMT, double x[], double y[], double length, double az) {
1755 	double s_az, c_az;
1756 	sincosd (az, &s_az, &c_az);
1757 	length /= 2.0;	/* Going half-way in each direction */
1758 	if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* Spherical solution */
1759 		double s0, c0, s_d, c_d;
1760 		double d = (length / GMT->current.map.dist[GMT_MAP_DIST].scale);	/* Convert from chosen unit to meter or degree */
1761 		if (!GMT->current.map.dist[GMT_MAP_DIST].arc) d /= GMT->current.proj.DIST_M_PR_DEG;	/* Convert meter to spherical degrees */
1762 		sincosd (y[0], &s0, &c0);
1763 		sincosd (d, &s_d, &c_d);
1764 		x[1] = x[0] + atand (s_d * s_az / (c0 * c_d - s0 * s_d * c_az));
1765 		y[1] = d_asind (s0 * c_d + c0 * s_d * c_az);
1766 		/* Then redo start point by going in the opposite direction */
1767 		sincosd (az+180.0, &s_az, &c_az);
1768 		x[0] = x[0] + atand (s_d * s_az / (c0 * c_d - s0 * s_d * c_az));
1769 		y[0] = d_asind (s0 * c_d + c0 * s_d * c_az);
1770 	}
1771 	else {	/* Cartesian solution */
1772 		x[1] = x[0] + length * s_az;
1773 		y[1] = y[0] + length * c_az;
1774 		x[0] = x[0] - length * s_az;
1775 		y[0] = y[0] - length * c_az;
1776 	}
1777 	return (gmt_distance (GMT, x[0], y[0], x[1], y[1]));
1778 }
1779 
1780 /*! . */
gmtsupport_determine_circle(struct GMT_CTRL * GMT,double x0,double y0,double r,double x[],double y[],unsigned int n)1781 GMT_LOCAL uint64_t gmtsupport_determine_circle (struct GMT_CTRL *GMT, double x0, double y0, double r, double x[], double y[], unsigned int n) {
1782 	/* Given an origin, radius, and n points, compute a circular path and return it */
1783 	unsigned int k;
1784 	uint64_t np = n;
1785 	double d_angle = 360.0 / n;
1786 
1787 	if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* Spherical solution */
1788 		double R[3][3], v[3], v_prime[3], lat;
1789 		double colat = (r / GMT->current.map.dist[GMT_MAP_DIST].scale);	/* Convert from chosen unit to meter or degree */
1790 		if (!GMT->current.map.dist[GMT_MAP_DIST].arc) colat /= GMT->current.proj.DIST_M_PR_DEG;	/* Convert meter to spherical degrees */
1791 		lat = 90.0 - colat;	/* Get small circle latitude about NP */
1792 		/* Set up small circle in local coordinates, convert to Cartesian, rotate, and covert to geo */
1793 		/* Rotation pole is 90 away from x0, at equator, and rotates by 90-y0 degrees */
1794 		gmt_make_rot_matrix (GMT, x0 + 90.0, 0.0, 90.0 - y0, R);
1795 		for (k = 0; k < n; k++) {
1796 			gmt_geo_to_cart (GMT, lat, d_angle * k, v, true);
1797 			gmt_matrix_vect_mult (GMT, 3U, R, v, v_prime);
1798 			gmt_cart_to_geo (GMT, &y[k], &x[k], v_prime, true);
1799 		}
1800 	}
1801 	else {	/* Cartesian solution */
1802 		double s_az, c_az;
1803 		for (k = 0; k < n; k++) {
1804 			sincosd (d_angle * k, &s_az, &c_az);
1805 			x[k] = x0 + r * s_az;
1806 			y[k] = y0 + r * c_az;
1807 		}
1808 	}
1809 	return (np);
1810 }
1811 
1812 /*! . */
gmtsupport_line_angle_ave(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,int half,int angle_type,bool directed,struct GMT_LABEL * L)1813 GMT_LOCAL void gmtsupport_line_angle_ave (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, int half, int angle_type, bool directed, struct GMT_LABEL *L) {
1814 	int64_t j, sstart, sstop;
1815 	double sum_x = 0.0, sum_x2 = 0.0, sum_xy = 0.0, sum_y = 0.0, sum_y2 = 0.0, dx, dy;
1816 	/* If directed is true then we want the direction of the line, else orientation is fine */
1817 
1818 	if (start == stop) {	/* Can happen if we want no smoothing but landed exactly on a knot point */
1819 		if (start > 0)
1820 			start--;
1821 		else if (stop < (n-1))
1822 			stop++;
1823 	}
1824 	sstart = MAX (0, ((int64_t)start) - half);	/* Must cast since start is unsigned */
1825 	sstop  = MIN (n-1, stop + half);
1826 	for (j = sstart; j <= sstop; j++) {	/* L2 fit for slope over this range of points */
1827 		if (j > sstart) sum_x += (x[j] - x[j-1]), sum_y += (y[j] - y[j-1]);
1828 		dx = x[j] - L->x;
1829 		dy = y[j] - L->y;
1830 		sum_x2 += dx * dx;
1831 		sum_y2 += dy * dy;
1832 		sum_xy += dx * dy;
1833 	}
1834 	if (sum_y2 < GMT_CONV8_LIMIT)	/* Line is horizontal */
1835 		L->line_angle = (directed && sum_x < 0.0) ? 180.0 : 0.0;
1836 	else if (sum_x2 < GMT_CONV8_LIMIT)	/* Line is vertical */
1837 		L->line_angle = (directed && sum_y < 0.0) ? -90.0 : 90.0;
1838 	else {	/* Least-squares fit of slope */
1839 		L->line_angle = d_atan2d (sum_xy, sum_x2);
1840 		if (directed && !(gmt_M_is_zero (sum_x) && gmt_M_is_zero (sum_y))) {
1841 			/* If the line_angle points more or less in the opposite direction as indicated by
1842 			 * sum_x and sum_y we add 180 to it */
1843 			double angle = d_atan2d (sum_y, sum_x);
1844 			if (fabs (L->line_angle - angle) > 145.0) L->line_angle += 180.0;
1845 		}
1846 	}
1847 	if (angle_type == 2) {	/* Just return the fixed angle given (unless NaN) */
1848 		if (gmt_M_is_dnan (cangle)) /* Cannot use this angle - default to along-line angle */
1849 			angle_type = 0;
1850 		else
1851 			L->angle = L->line_angle = cangle;
1852 	}
1853 	if (angle_type != 2) {	/* Must base label angle on the contour angle */
1854 		L->angle = L->line_angle + angle_type * 90.0;	/* May add 90 to get normal */
1855 		if (L->angle < 0.0) L->angle += 360.0;
1856 		if (L->angle > 90.0 && L->angle < 270) L->angle -= 180.0;
1857 	}
1858 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Ave: Label Line angle = %g start/stop = %d/%d atan2d (%g, %g) Label angle = %g\n", L->line_angle, (int)start, (int)stop, sum_xy, sum_x2, L->angle);
1859 }
1860 
1861 /*! . */
gmtsupport_line_angle_line(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,int angle_type,bool directed,struct GMT_LABEL * L)1862 GMT_LOCAL void gmtsupport_line_angle_line (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, int angle_type, bool directed, struct GMT_LABEL *L) {
1863 	double dx, dy;
1864 	gmt_M_unused (directed);
1865 
1866 	if (start == stop) {	/* Can happen if we want no smoothing but landed exactly on a knot point */
1867 		if (start > 0)
1868 			start--;
1869 		else if (stop < (n-1))
1870 			stop++;
1871 	}
1872 	if (stop >= n) stop = n-1;
1873 	dx = x[stop] - x[start];
1874 	dy = y[stop] - y[start];
1875 	L->line_angle =  d_atan2d (dy, dx);
1876 	if (angle_type == 2) {	/* Just return the fixed angle given (unless NaN) */
1877 		if (gmt_M_is_dnan (cangle)) /* Cannot use this angle - default to along-line angle */
1878 			angle_type = 0;
1879 		else
1880 			L->angle = cangle;
1881 	}
1882 	if (angle_type != 2) {	/* Must base label angle on the contour angle */
1883 		L->angle = L->line_angle + angle_type * 90.0;	/* May add 90 to get normal */
1884 		if (L->angle < 0.0) L->angle += 360.0;
1885 		if (L->angle > 90.0 && L->angle < 270) L->angle -= 180.0;
1886 	}
1887 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Vec: Label Line angle = %g start/stop = %d/%d atan2d (%g, %g) Label angle = %g\n", L->line_angle, (int)start, (int)stop, dy, dx, L->angle);
1888 }
1889 
1890 /*! . */
gmtsupport_contlabel_angle_ave(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,struct GMT_LABEL * L,struct GMT_CONTOUR * G)1891 GMT_LOCAL void gmtsupport_contlabel_angle_ave (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, struct GMT_LABEL *L, struct GMT_CONTOUR *G) {
1892 	gmtsupport_line_angle_ave (GMT, x, y, start, stop, cangle, n, G->half_width, G->angle_type, false, L);
1893 }
1894 
1895 /*! . */
gmtsupport_contlabel_angle_line(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,struct GMT_LABEL * L,struct GMT_CONTOUR * G)1896 GMT_LOCAL void gmtsupport_contlabel_angle_line (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, struct GMT_LABEL *L, struct GMT_CONTOUR *G) {
1897 	gmtsupport_line_angle_line (GMT, x, y, start, stop, cangle, n, G->angle_type, false, L);
1898 }
1899 
1900 /*! . */
gmtsupport_contlabel_angle(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,bool contour,struct GMT_LABEL * L,struct GMT_CONTOUR * G)1901 GMT_LOCAL void gmtsupport_contlabel_angle (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, bool contour, struct GMT_LABEL *L, struct GMT_CONTOUR *G) {
1902 	/* Sets L->line_angle and L->angle */
1903 	if ((G->nudge_flag == 2 && G->half_width == UINT_MAX ) || G->half_width == 0) {	/* Want line-angle to follow line */
1904 		gmtsupport_contlabel_angle_line (GMT, x, y, start, stop, cangle, n, L, G);
1905 	}
1906 	else if (G->half_width == UINT_MAX) {	/* Automatic width specification */
1907 		/* Try to come up with a number that is small for short lines and grow slowly for larger lines */
1908 		G->half_width = MAX (1, lrint (ceil (log10 (0.3333333333*n))));
1909 		G->half_width *= G->half_width;	/* New guess at half-width */
1910 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Automatic label-averaging half_width = %d [n = %d]\n", G->half_width, (int)n);
1911 		if (G->half_width == 1)
1912 			gmtsupport_contlabel_angle_line (GMT, x, y, start, stop, cangle, n, L, G);
1913 		else
1914 			gmtsupport_contlabel_angle_ave (GMT, x, y, start, stop, cangle, n, L, G);
1915 		G->half_width = UINT_MAX;	/* Reset back to auto */
1916 	}
1917 	else {	/* Go width the selected half-width */
1918 		gmtsupport_contlabel_angle_ave (GMT, x, y, start, stop, cangle, n, L, G);
1919 	}
1920 	if (contour) {	/* Limit line_angle to -90/+90 */
1921 		if (L->line_angle > +90.0) L->line_angle -= 180.0;
1922 		if (L->line_angle < -90.0) L->line_angle += 180.0;
1923 	}
1924 }
1925 
1926 /*! . */
gmtsupport_decorated_angle_ave(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,struct GMT_LABEL * L,struct GMT_DECORATE * G)1927 GMT_LOCAL void gmtsupport_decorated_angle_ave (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, struct GMT_LABEL *L, struct GMT_DECORATE *G) {
1928 	gmtsupport_line_angle_ave (GMT, x, y, start, stop, cangle, n, G->half_width, G->angle_type, true, L);
1929 }
1930 
1931 /*! . */
gmtsupport_decorated_angle_line(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,struct GMT_LABEL * L,struct GMT_DECORATE * G)1932 GMT_LOCAL void gmtsupport_decorated_angle_line (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, struct GMT_LABEL *L, struct GMT_DECORATE *G) {
1933 	gmtsupport_line_angle_line (GMT, x, y, start, stop, cangle, n, G->angle_type, true, L);
1934 }
1935 
1936 /*! . */
gmtsupport_decorated_angle(struct GMT_CTRL * GMT,double x[],double y[],uint64_t start,uint64_t stop,double cangle,uint64_t n,bool contour,struct GMT_LABEL * L,struct GMT_DECORATE * G)1937 GMT_LOCAL void gmtsupport_decorated_angle (struct GMT_CTRL *GMT, double x[], double y[], uint64_t start, uint64_t stop, double cangle, uint64_t n, bool contour, struct GMT_LABEL *L, struct GMT_DECORATE *G) {
1938 	/* Sets L->line_angle and L->angle */
1939 	if ((G->nudge_flag == 2 && G->half_width == UINT_MAX ) || G->half_width == 0) {	/* Want line-angle to follow line */
1940 		gmtsupport_decorated_angle_line (GMT, x, y, start, stop, cangle, n, L, G);
1941 	}
1942 	else if (G->half_width == UINT_MAX) {	/* Automatic width specification */
1943 		/* Try to come up with a number that is small for short lines and grow slowly for larger lines */
1944 		G->half_width = MAX (1, lrint (ceil (log10 (0.3333333333*n))));
1945 		G->half_width *= G->half_width;	/* New guess at half-width */
1946 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Automatic label-averaging half_width = %d [n = %d]\n", G->half_width, (int)n);
1947 		if (G->half_width == 1)
1948 			gmtsupport_decorated_angle_line (GMT, x, y, start, stop, cangle, n, L, G);
1949 		else
1950 			gmtsupport_decorated_angle_ave (GMT, x, y, start, stop, cangle, n, L, G);
1951 		G->half_width = UINT_MAX;	/* Reset back to auto */
1952 	}
1953 	else {	/* Go width the selected half-width */
1954 		gmtsupport_decorated_angle_ave (GMT, x, y, start, stop, cangle, n, L, G);
1955 	}
1956 	if (contour) {	/* Limit line_angle to -90/+90 */
1957 		if (L->line_angle > +90.0) L->line_angle -= 180.0;
1958 		if (L->line_angle < -90.0) L->line_angle += 180.0;
1959 	}
1960 }
1961 
1962 /*! . */
gmtsupport_sort_label_struct(const void * p_1,const void * p_2)1963 GMT_LOCAL int gmtsupport_sort_label_struct (const void *p_1, const void *p_2) {
1964 	const struct GMT_LABEL **point_1 = (const struct GMT_LABEL **)p_1, **point_2 = (const struct GMT_LABEL **)p_2;
1965 
1966 	if ((*point_1)->dist < (*point_2)->dist) return -1;
1967 	if ((*point_1)->dist > (*point_2)->dist) return +1;
1968 	return 0;
1969 }
1970 
1971 /*! . */
gmtsupport_contlabel_fixpath(struct GMT_CTRL * GMT,double ** xin,double ** yin,double d[],uint64_t * n,struct GMT_CONTOUR * G)1972 GMT_LOCAL void gmtsupport_contlabel_fixpath (struct GMT_CTRL *GMT, double **xin, double **yin, double d[], uint64_t *n, struct GMT_CONTOUR *G) {
1973 	/* Sorts labels based on distance and inserts the label (x,y) point into the x,y path */
1974 	uint64_t i, j, k, np;
1975 	double *xp = NULL, *yp = NULL, *x = NULL, *y = NULL;
1976 
1977 	if (G->n_label == 0) return;	/* No labels, no need to insert points */
1978 
1979 	/* Sort labels based on distance along contour if more than 1 */
1980 	if (G->n_label > 1) qsort (G->L, G->n_label, sizeof (struct GMT_LABEL *), gmtsupport_sort_label_struct);
1981 
1982 	np = *n + G->n_label;	/* Length of extended path that includes inserted label coordinates */
1983 	xp = gmt_M_memory (GMT, NULL, np, double);
1984 	yp = gmt_M_memory (GMT, NULL, np, double);
1985 	x = *xin;	y = *yin;	/* Input coordinate arrays */
1986 
1987 	/* Make sure the xp, yp array contains the exact points at which labels should be placed */
1988 
1989 	for (k = 0, i = j = 0; i < *n && j < np && k < G->n_label; k++) {
1990 		while (i < *n && d[i] < G->L[k]->dist) {	/* Not at the label point yet - just copy points */
1991 			xp[j] = x[i];
1992 			yp[j] = y[i];
1993 			j++;
1994 			i++;
1995 		}
1996 		/* Add the label point */
1997 		G->L[k]->node = j;		/* Update node since we have been adding new points */
1998 		xp[j] = G->L[k]->x;
1999 		yp[j] = G->L[k]->y;
2000 		j++;
2001 	}
2002 	while (i < *n) {	/* Append the rest of the path */
2003 		xp[j] = x[i];
2004 		yp[j] = y[i];
2005 		j++;
2006 		i++;
2007 	}
2008 
2009 	gmt_M_free (GMT, x);	/* Get rid of old path... */
2010 	gmt_M_free (GMT, y);
2011 
2012 	*xin = xp;		/* .. and pass out new path */
2013 	*yin = yp;
2014 
2015 	*n = np;		/* and the new length */
2016 }
2017 
2018 /*! . */
gmtsupport_contlabel_addpath(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,double zval,char * label,bool annot,struct GMT_CONTOUR * G)2019 GMT_LOCAL void gmtsupport_contlabel_addpath (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, double zval, char *label, bool annot, struct GMT_CONTOUR *G) {
2020 	uint64_t i;
2021 	double s = 0.0, c = 1.0, sign = 1.0;
2022 	struct GMT_CONTOUR_LINE *L = NULL;
2023 	/* Adds this segment to the list of contour lines */
2024 
2025 	if (G->n_alloc == 0 || G->n_segments == G->n_alloc) {
2026 		G->n_alloc = (G->n_alloc == 0) ? GMT_CHUNK : (G->n_alloc << 1);
2027 		G->segment = gmt_M_memory (GMT, G->segment, G->n_alloc, struct GMT_CONTOUR_LINE *);
2028 	}
2029 	G->segment[G->n_segments] = gmt_M_memory (GMT, NULL, 1, struct GMT_CONTOUR_LINE);
2030 	L = G->segment[G->n_segments];	/* Pointer to current segment */
2031 	L->n = n;
2032 	L->x = gmt_M_memory (GMT, NULL, L->n, double);
2033 	L->y = gmt_M_memory (GMT, NULL, L->n, double);
2034 	gmt_M_memcpy (L->x, x, L->n, double);
2035 	gmt_M_memcpy (L->y, y, L->n, double);
2036 	gmt_M_memcpy (&L->pen, &G->line_pen, 1, struct GMT_PEN);
2037 	/* gmt_M_memcpy (L->rgb,&G->rgb, 4, double); */
2038 	gmt_M_memcpy (L->rgb, &G->font_label.fill.rgb, 4, double);
2039 	L->name = gmt_M_memory (GMT, NULL, strlen (label)+1, char);
2040 	strcpy (L->name, label);
2041 	L->annot = annot;
2042 	L->z = zval;
2043 	if (G->n_label) {	/* There are labels */
2044 		L->n_labels = G->n_label;
2045 		L->L = gmt_M_memory (GMT, NULL, L->n_labels, struct GMT_LABEL);
2046 		for (i = 0; i < L->n_labels; i++) {
2047 			L->L[i].x = G->L[i]->x;
2048 			L->L[i].y = G->L[i]->y;
2049 			L->L[i].line_angle = G->L[i]->line_angle;
2050 			if (G->nudge_flag) {	/* Must adjust point a bit */
2051 				if (G->nudge_flag == 2) sincosd (L->L[i].line_angle, &s, &c);
2052 				/* If N+1 or N-1 is used we want positive x nudge to extend away from end point */
2053 				sign = (G->number_placement) ? (double)L->L[i].end : 1.0;
2054 				L->L[i].x += sign * (G->nudge[GMT_X] * c - G->nudge[GMT_Y] * s);
2055 				L->L[i].y += sign * (G->nudge[GMT_X] * s + G->nudge[GMT_Y] * c);
2056 			}
2057 			L->L[i].angle = G->L[i]->angle;
2058 			L->L[i].dist = G->L[i]->dist;
2059 			L->L[i].node = G->L[i]->node;
2060 			L->L[i].label = gmt_M_memory (GMT, NULL, strlen (G->L[i]->label)+1, char);
2061 			gmt_M_memcpy (L->L[i].rgb, G->L[i]->rgb, 4, double);
2062 			strcpy (L->L[i].label, G->L[i]->label);
2063 		}
2064 	}
2065 	G->n_segments++;
2066 }
2067 
2068 /*! . */
gmtsupport_get_radii_of_curvature(double x[],double y[],uint64_t n,double r[])2069 GMT_LOCAL void gmtsupport_get_radii_of_curvature (double x[], double y[], uint64_t n, double r[]) {
2070 	/* Calculates radius of curvature along the spatial curve x(t), y(t) */
2071 
2072 	uint64_t i, im, ip;
2073 	double a, b, c, d, e, f, x0, y0, denom;
2074 
2075 	for (im = 0, i = 1, ip = 2; ip < n; i++, im++, ip++) {
2076 		a = (x[im] - x[i]);	b = (y[im] - y[i]);	e = 0.5 * (x[im] * x[im] + y[im] * y[im] - x[i] * x[i] - y[i] * y[i]);
2077 		c = (x[i] - x[ip]);	d = (y[i] - y[ip]);	f = 0.5 * (x[i] * x[i] + y[i] * y[i] - x[ip] * x[ip] - y[ip] * y[ip]);
2078 		denom = b * c - a * d;
2079 		if (denom == 0.0)
2080 			r[i] = DBL_MAX;
2081 		else {
2082 			x0 = (-d * e + b * f) / denom;
2083 			y0 = (c * e - a * f) / denom;
2084 			r[i] = hypot (x[i] - x0, y[i] - y0);
2085 		}
2086 	}
2087 	r[0] = r[n-1] = DBL_MAX;	/* Boundary conditions has zero curvature at end points so r = inf */
2088 }
2089 
2090 /*! . */
gmtsupport_edge_contour(struct GMT_CTRL * GMT,struct GMT_GRID * G,unsigned int col,unsigned int row,unsigned int side,double d,double * x,double * y)2091 GMT_LOCAL void gmtsupport_edge_contour (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigned int col, unsigned int row, unsigned int side, double d, double *x, double *y) {
2092 	gmt_M_unused(GMT);
2093 	if (side == 0) {
2094 		*x = gmt_M_grd_col_to_x (GMT, col+d, G->header);
2095 		*y = gmt_M_grd_row_to_y (GMT, row, G->header);
2096 	}
2097 	else if (side == 1) {
2098 		*x = gmt_M_grd_col_to_x (GMT, col+1, G->header);
2099 		*y = gmt_M_grd_row_to_y (GMT, row-d, G->header);
2100 	}
2101 	else if (side == 2) {
2102 		*x = gmt_M_grd_col_to_x (GMT, col+1-d, G->header);
2103 		*y = gmt_M_grd_row_to_y (GMT, row-1, G->header);
2104 	}
2105 	else {
2106 		*x = gmt_M_grd_col_to_x (GMT, col, G->header);
2107 		*y = gmt_M_grd_row_to_y (GMT, row-1+d, G->header);
2108 	}
2109 }
2110 
2111 /*! . */
gmtsupport_setcontjump(gmt_grdfloat * z,uint64_t nz)2112 GMT_LOCAL void gmtsupport_setcontjump (gmt_grdfloat *z, uint64_t nz) {
2113 /* This routine will check if there is a 360 jump problem
2114  * among these coordinates and adjust them accordingly so
2115  * that subsequent testing can determine if a zero contour
2116  * goes through these edges.  E.g., values like 359, 1
2117  * should become -1, 1 after this function */
2118 
2119 	uint64_t i;
2120 	bool jump = false;
2121 	gmt_grdfloat dz;
2122 
2123 #ifdef DOUBLE_PRECISION_GRID
2124 	for (i = 1; !jump && i < nz; i++) {
2125 		dz = z[i] - z[0];
2126 		if (fabs (dz) > 180.0) jump = true;
2127 	}
2128 
2129 	if (!jump) return;
2130 
2131 	z[0] = fmod (z[0], 360.0);
2132 	if (z[0] > 180.0) z[0] -= 360.0;
2133 	else if (z[0] < -180.0) z[0] += 360.0;
2134 	for (i = 1; i < nz; i++) {
2135 		if (z[i] > 180.0) z[i] -= 360.0;
2136 		else if (z[i] < -180.0) z[i] += 360.0;
2137 		dz = z[i] - z[0];
2138 		if (fabs (dz) > 180.0) z[i] -= copysign (360.0, dz);
2139 		z[i] = fmod (z[i], 360.0);
2140 	}
2141 #else
2142 	for (i = 1; !jump && i < nz; i++) {
2143 		dz = z[i] - z[0];
2144 		if (fabsf (dz) > 180.0f) jump = true;
2145 	}
2146 
2147 	if (!jump) return;
2148 
2149 	z[0] = fmodf (z[0], 360.0f);
2150 	if (z[0] > 180.0f) z[0] -= 360.0f;
2151 	else if (z[0] < -180.0f) z[0] += 360.0f;
2152 	for (i = 1; i < nz; i++) {
2153 		if (z[i] > 180.0f) z[i] -= 360.0f;
2154 		else if (z[i] < -180.0f) z[i] += 360.0f;
2155 		dz = z[i] - z[0];
2156 		if (fabsf (dz) > 180.0f) z[i] -= copysignf (360.0f, dz);
2157 		z[i] = fmodf (z[i], 360.0f);
2158 	}
2159 #endif
2160 }
2161 
2162 /*! . */
gmtsupport_trace_contour(struct GMT_CTRL * GMT,struct GMT_GRID * G,bool test,unsigned int * edge,double ** x,double ** y,unsigned int col,unsigned int row,unsigned int side,uint64_t offset,unsigned int * bit,unsigned int * nan_flag)2163 GMT_LOCAL uint64_t gmtsupport_trace_contour (struct GMT_CTRL *GMT, struct GMT_GRID *G, bool test, unsigned int *edge, double **x, double **y, unsigned int col, unsigned int row, unsigned int side, uint64_t offset, unsigned int *bit, unsigned int *nan_flag) {
2164 	/* Note: side must be signed due to calculations like (side-2)%2 which will not work with unsigned */
2165 	unsigned int side_in, this_side, old_side, n_exits, opposite_side, n_nan, edge_word, edge_bit, periodic = 0;
2166 	int p[5] = {0, 0, 0, 0, 0}, mx;
2167 	bool more, crossed = false;
2168 	size_t n_alloc;
2169 	uint64_t n = 1, m, ij0, ij_in, ij;
2170 	gmt_grdfloat z[5] = {0.0, 0.0, 0.0, 0.0, 0.0}, dz;
2171 	double xk[5] = {0.0, 0.0, 0.0, 0.0, 0.0}, dist1, dist2, *xx = NULL, *yy = NULL;
2172 	static int d_col[5] = {0, 1, 0, 0, 0}, d_row[5] = {0, 0, -1, 0, 0}, d_side[5] = {0, 1, 0, 1, 0};
2173 #if 0
2174 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
2175 
2176 	/* WORK IN PROGRESS.  Currently has trouble with some contours streaking across a W/E map */
2177 	/* For periodic grids we must also be sensitive to exiting in west and reenter in east, and vice versa */
2178 	if (HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT)
2179 		periodic = 1;
2180 	else if (HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_NOREPEAT)
2181 		periodic = 2;
2182 #endif
2183 	/* Note: We need gmt_M_ijp to get us the index into the padded G->data whereas we use gmt_M_ij0 to get the corresponding index for non-padded edge array */
2184 	mx = G->header->mx;	/* Need signed mx below */
2185 	p[0] = p[4] = 0;	p[1] = 1;	p[2] = 1 - mx;	p[3] = -mx;	/* Padded offsets for G->data array */
2186 	*nan_flag = 0;
2187 
2188 	/* Check if this edge was already used */
2189 
2190 	ij0 = gmt_M_ij0 (G->header, row + d_row[side], col + d_col[side]);	/* Index to use with the edge array */
2191 	edge_word = (unsigned int)(ij0 / 32 + d_side[side] * offset);
2192 	edge_bit = (unsigned int)(ij0 % 32);
2193 	if (test && (edge[edge_word] & bit[edge_bit])) return (0);	/* Been here */
2194 
2195 	ij_in = gmt_M_ijp (G->header, row, col);	/* Index to use with the padded G->data array */
2196 	side_in = side;
2197 
2198 	/* First check if contour cuts the starting edge */
2199 
2200 	z[0] = G->data[ij_in+p[side]];
2201 	z[1] = G->data[ij_in+p[side+1]];
2202 	if (GMT->current.map.z_periodic) gmtsupport_setcontjump (z, 2);
2203 
2204 	if (!(z[0] * z[1] < 0.0f)) return (0);	/* This formulation will also return if one of the z's is NaN */
2205 
2206 	n_alloc = GMT_CHUNK;
2207 	m = n_alloc - 2;
2208 
2209 	xx = gmt_M_memory (GMT, NULL, n_alloc, double);
2210 	yy = gmt_M_memory (GMT, NULL, n_alloc, double);
2211 
2212 	gmtsupport_edge_contour (GMT, G, col, row, side, z[0] / (z[0] - z[1]), &(xx[0]), &(yy[0]));
2213 	edge[edge_word] |= bit[edge_bit];
2214 
2215 	more = true;
2216 	do {
2217 		ij = gmt_M_ijp (G->header, row, col);	/* Index to use with the padded G->data array */
2218 
2219 		/* If this is the box and edge we started from, explicitly close the polygon and exit */
2220 
2221 		if (n > 1 && ij == ij_in && side == side_in) {	/* Yes, we close and exit */
2222 			xx[n-1] = xx[0]; yy[n-1] = yy[0];	/* Ensure no roundoff */
2223 			more = false;
2224 			continue;
2225 		}
2226 
2227 		n_exits = 0;
2228 		old_side = side;
2229 		for (this_side = 0; this_side < 5; this_side++) z[this_side] = G->data[ij+p[this_side]];	/* Copy the 4 corners to the z array and duplicate the 1st as a '5th' corner */
2230 		if (GMT->current.map.z_periodic) gmtsupport_setcontjump (z, 5);
2231 
2232 		for (this_side = n_nan = 0; this_side < 4; this_side++) {	/* Loop over the 4 box sides and count possible exits */
2233 
2234 			/* Skip if NaN encountered */
2235 
2236 			if (gmt_M_is_fnan (z[this_side+1]) || gmt_M_is_fnan (z[this_side])) {	/* Check each side, defined by two nodes, for NaNs */
2237 				n_nan++;
2238 				continue;
2239 			}
2240 
2241 			/* Skip if no zero-crossing on this edge */
2242 
2243 			if (z[this_side+1] * z[this_side] > 0.0f) continue;
2244 			if ((dz = z[this_side] - z[this_side+1]) == 0.0f) continue;
2245 
2246 			/* Save normalized distance along edge from corner this_side to crossing of edge this_side */
2247 
2248 			xk[this_side] = z[this_side] / dz;
2249 
2250 			/* Skip if this is the entry edge of the current box (this_side == old_side) */
2251 
2252 			if (this_side == old_side) continue;
2253 
2254 			/* Here we have a new crossing */
2255 
2256 			side = this_side;
2257 			n_exits++;
2258 		}
2259 		xk[4] = xk[0];	/* Repeat the first value */
2260 
2261 		if (n > m) {	/* Must try to allocate more memory */
2262 			n_alloc <<= 1;
2263 			m = n_alloc - 2;
2264 			xx = gmt_M_memory (GMT, xx, n_alloc, double);
2265 			yy = gmt_M_memory (GMT, yy, n_alloc, double);
2266 		}
2267 
2268 		/* These are the possible outcomes of n_exits:
2269 		   0: No exits. We must have struck a wall of NaNs
2270 		   1: One exit. Take it!
2271 		   2: Two exits is not possible!
2272 		   3: Three exits means we have entered on a saddle point
2273 		*/
2274 
2275 		if (n_exits == 0) {	/* We have hit a field of NaNs, finish contour */
2276 			more = false;
2277 			*nan_flag = n_nan;
2278 			continue;
2279 		}
2280 		else if (n_exits == 3) {	/* Saddle point: Turn to the correct side */
2281 			opposite_side = (old_side + 2) % 4;	/* Opposite side */
2282 			dist1 = xk[old_side] + xk[opposite_side];
2283 			dist2 = xk[old_side+1] + xk[opposite_side+1];
2284 			if (dist1 == dist2) {	/* Perfect saddle, must use fixed saddle decision to avoid a later crossing */
2285 				/* Connect S with W and E with N in this case */
2286 				if (old_side == 0)
2287 					side = 3;
2288 				else if (old_side == 1)
2289 					side = 2;
2290 				else if (old_side == 2)
2291 					side = 1;
2292 				else
2293 					side = 0;
2294 			}
2295 			else if (dist1 > dist2)
2296 				side = (old_side + 1) % 4;
2297 			else
2298 				side = (old_side + 3) % 4;
2299 		}
2300 		gmtsupport_edge_contour (GMT, G, col, row, side, xk[side], &(xx[n]), &(yy[n]));
2301 		n++;
2302 
2303 		/* Mark the new edge as used */
2304 
2305 		ij0 = gmt_M_ij0 (G->header, row + d_row[side], col + d_col[side]);	/* Non-padded index used for edge array */
2306 		edge_word = (unsigned int)(ij0 / 32 + d_side[side] * offset);
2307 		edge_bit  = (unsigned int)(ij0 % 32);
2308 		edge[edge_word] |= bit[edge_bit];
2309 
2310 		if ((side == 0 && row == G->header->n_rows - 1) || (side == 2 && row == 1)) {	/* Going out of grid at N or S */
2311 			more = false;
2312 			continue;
2313 		}
2314 		else if ((side == 1 && col == G->header->n_columns - 2) || (side == 3 && col == 0)) {	/* Going out of grid E-W */
2315 			if (periodic == 0) {	/* And that is it. */
2316 				more = false;
2317 				continue;
2318 			}
2319 			if (side == 1) {	/* Exiting on east to reappear on west */
2320 				side = 3;	/* So entering first col bin from left (3) */
2321 				col = 0;	/* First column bin */
2322 				xx[n] = xx[n-1] - 360.0;
2323 			}
2324 			else {			/* Exiting on west to reappear on east */
2325 				side = 1;	/* So entering last col bin from right (1) */
2326 				col = G->header->n_columns - 2;	/* Last column bin since next col is the boundary of the grid */
2327 				xx[n] = xx[n-1] + 360.0;
2328 			}
2329 			yy[n] = yy[n-1];
2330 			crossed = true;	/* Flag that we crossed periodic boundary and add the duplicate coordinate point to array */
2331 			n++;
2332 			ij0 = gmt_M_ij0 (G->header, row + d_row[side], col + d_col[side]);	/* Flag this edge as used as well */
2333 			edge_word = (unsigned int)(ij0 / 32 + d_side[side] * offset);
2334 			edge_bit  = (unsigned int)(ij0 % 32);
2335 			if (test && (edge[edge_word] & bit[edge_bit])) more = false;	/* Been here before */
2336 			edge[edge_word] |= bit[edge_bit];
2337 			continue;
2338 		}
2339 
2340 		switch (side) {	/* Move on to next box (col,row,side) */
2341 			case 0: row++; side = 2; break;	/* Go to row below */
2342 			case 1: col++; side = 3; break;	/* Go to col on right */
2343 			case 2: row--; side = 0; break;	/* Go to row above */
2344 			case 3: col--; side = 1; break;	/* Go to col on left */
2345 		}
2346 
2347 	} while (more);
2348 
2349 	xx = gmt_M_memory (GMT, xx, n, double);
2350 	yy = gmt_M_memory (GMT, yy, n, double);
2351 
2352 	if (crossed) {
2353 		if ((gmtlib_determine_pole (GMT, xx, yy, n) == 0))
2354 			gmt_eliminate_lon_jumps (GMT, xx, n);	/* Ensure longitudes are in the same quadrants */
2355 	}
2356 
2357 	*x = xx;	*y = yy;
2358 	return (n);
2359 }
2360 
2361 /*! . */
gmtsupport_smooth_contour(struct GMT_CTRL * GMT,double ** x_in,double ** y_in,uint64_t n,int sfactor,int stype)2362 GMT_LOCAL int64_t gmtsupport_smooth_contour (struct GMT_CTRL *GMT, double **x_in, double **y_in, uint64_t n, int sfactor, int stype) {
2363 	/* Input n (x_in, y_in) points */
2364 	/* n_out = sfactor * n -1 */
2365 	/* Interpolation scheme used (0 = linear, 1 = Akima, 2 = Cubic spline, 3 = None */
2366 	uint64_t i, j, k, n_out;
2367 	double ds, t_next, *x = NULL, *y = NULL;
2368 	double *t_in = NULL, *t_out = NULL, *x_tmp = NULL, *y_tmp = NULL, x0, x1, y0, y1;
2369 	bool *flag = NULL;
2370 
2371 	if (sfactor == 0 || n < 4) return (n);	/* Need at least 4 points to call Akima */
2372 
2373 	x = *x_in;	y = *y_in;
2374 
2375 	n_out = sfactor * n - 1;	/* Number of new points */
2376 
2377 	t_in  = gmt_M_memory (GMT, NULL, n, double);
2378 
2379 	/* Create dummy distance values for input points, and throw out duplicate points while at it */
2380 
2381 	t_in[0] = 0.0;
2382 	for (i = j = 1; i < n; i++)	{
2383 		if (gmt_M_x_is_lon (GMT, GMT_IN) && gmt_M_360_range (x[i-1], x[i])) {
2384 			ds = 0.0;	/* 360 degree jumps are excluded */
2385 		}
2386 		else
2387 			ds = hypot ((x[i]-x[i-1]), (y[i]-y[i-1]));
2388 		if (ds > 0.0) {
2389 			t_in[j] = t_in[j-1] + ds;
2390 			x[j] = x[i];
2391 			y[j] = y[i];
2392 			j++;
2393 		}
2394 	}
2395 
2396 	n = j;	/* May have lost some duplicates */
2397 	if (sfactor == 0 || n < 4) {	/* Need at least 4 points to call Akima */
2398 		gmt_M_free (GMT, t_in);
2399 		return (n);
2400 	}
2401 
2402 	t_out = gmt_M_memory (GMT, NULL, n_out + n, double);
2403 	x_tmp = gmt_M_memory (GMT, NULL, n_out + n, double);
2404 	y_tmp = gmt_M_memory (GMT, NULL, n_out + n, double);
2405 	flag  = gmt_M_memory (GMT, NULL, n_out + n, bool);
2406 
2407 	/* Create equidistant output points */
2408 
2409 	ds = t_in[n-1] / (n_out-1);
2410 	t_next = ds;
2411 	t_out[0] = 0.0;
2412 	flag[0] = true;
2413 	for (i = j = 1; i < n_out; i++) {
2414 		if (j < n && t_in[j] < t_next) {
2415 			t_out[i] = t_in[j];
2416 			flag[i] = true;
2417 			j++;
2418 			n_out++;
2419 		}
2420 		else {
2421 			t_out[i] = t_next;
2422 			t_next += ds;
2423 		}
2424 	}
2425 	t_out[n_out-1] = t_in[n-1];
2426 	if (t_out[n_out-1] == t_out[n_out-2]) n_out--;
2427 	flag[n_out-1] = true;
2428 
2429 	if (gmt_intpol (GMT, t_in, x, NULL, n, n_out, t_out, x_tmp, 0.0, stype)) {
2430 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT internal error\n");
2431 		gmt_M_free (GMT, t_in);
2432 		gmt_M_free (GMT, t_out);
2433 		gmt_M_free (GMT, flag);
2434 		gmt_M_free (GMT, x_tmp);
2435 		gmt_M_free (GMT, y_tmp);
2436 		return -GMT_RUNTIME_ERROR;
2437 	}
2438 	if (gmt_intpol (GMT, t_in, y, NULL, n, n_out, t_out, y_tmp, 0.0, stype)) {
2439 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT internal error\n");
2440 		gmt_M_free (GMT, t_in);
2441 		gmt_M_free (GMT, t_out);
2442 		gmt_M_free (GMT, flag);
2443 		gmt_M_free (GMT, x_tmp);
2444 		gmt_M_free (GMT, y_tmp);
2445 		return -GMT_RUNTIME_ERROR;
2446 	}
2447 
2448 	/* Make sure interpolated function is bounded on each segment interval */
2449 
2450 	i = 0;
2451 	while (i < (n_out - 1)) {
2452 		j = i + 1;
2453 		while (j < n_out && !flag[j]) j++;
2454 		x0 = x_tmp[i];	x1 = x_tmp[j];
2455 		if (x0 > x1) gmt_M_double_swap (x0, x1);
2456 		y0 = y_tmp[i];	y1 = y_tmp[j];
2457 		if (y0 > y1) gmt_M_double_swap (y0, y1);
2458 		for (k = i + 1; k < j; k++) {
2459 			if (x_tmp[k] < x0)
2460 				x_tmp[k] = x0 + 1.0e-10;
2461 			else if (x_tmp[k] > x1)
2462 				x_tmp[k] = x1 - 1.0e-10;
2463 			if (y_tmp[k] < y0)
2464 				y_tmp[k] = y0 + 1.0e-10;
2465 			else if (y_tmp[k] > y1)
2466 				y_tmp[k] = y1 - 1.0e-10;
2467 		}
2468 		i = j;
2469 	}
2470 
2471 	/* Replace original coordinates */
2472 
2473 	gmt_M_free (GMT, x);
2474 	gmt_M_free (GMT, y);
2475 
2476 	*x_in = x_tmp;
2477 	*y_in = y_tmp;
2478 
2479 	gmt_M_free (GMT, t_in);
2480 	gmt_M_free (GMT, t_out);
2481 	gmt_M_free (GMT, flag);
2482 
2483 	return (n_out);
2484 }
2485 
2486 /*! . */
gmtsupport_splice_contour(struct GMT_CTRL * GMT,double ** x,double ** y,uint64_t n,double * x2,double * y2,uint64_t n2)2487 GMT_LOCAL uint64_t gmtsupport_splice_contour (struct GMT_CTRL *GMT, double **x, double **y, uint64_t n, double *x2, double *y2, uint64_t n2) {
2488 	/* Basically does a "tail -r" on the array x,y and append arrays x2/y2 */
2489 
2490 	uint64_t i, j, m;
2491 	double *x1, *y1;
2492 
2493 	if (n2 < 2) return (n);		/* Nothing to be done when second piece < 2 points */
2494 
2495 	m = n + n2 - 1;	/* Total length since one point is shared */
2496 
2497 	/* Make more space */
2498 
2499 	x1 = *x;	y1 = *y;
2500 	x1 = gmt_M_memory (GMT, x1, m, double);
2501 	y1 = gmt_M_memory (GMT, y1, m, double);
2502 
2503 	/* Move first piece to the back */
2504 
2505 	for (i = m-1, j = n; j > 0; j--, i--) {
2506 		x1[i] = x1[j-1];	y1[i] = y1[j-1];
2507 	}
2508 
2509 	/* Put second piece, in reverse, in the front */
2510 
2511 	for (i = n2-2, j = 1; j < n2; j++, i--) {
2512 		x1[i] = x2[j];	y1[i] = y2[j];
2513 	}
2514 
2515 	*x = x1;
2516 	*y = y1;
2517 
2518 	return (m);
2519 }
2520 
2521 /*! . */
gmtsupport_orient_contour(struct GMT_GRID * G,double * x,double * y,uint64_t n,int orient)2522 GMT_LOCAL void gmtsupport_orient_contour (struct GMT_GRID *G, double *x, double *y, uint64_t n, int orient) {
2523 	/* Determine handedness of the contour and if opposite of orient reverse the contour */
2524 	int side[2], z_dir, k, k2;
2525 	bool reverse;
2526 	uint64_t i, j, ij_ul, ij_ur, ij_ll, ij_lr;
2527 	double fx[2], fy[2], dx, dy;
2528 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
2529 
2530 	if (orient == 0) return;	/* Nothing to be done when no orientation specified */
2531 	if (n < 2) return;		/* Cannot work on a single point */
2532 
2533 	for (k = 0; k < 2; k++) {	/* Calculate fractional node numbers from left/top */
2534 		fx[k] = (x[k] - G->header->wesn[XLO]) * HH->r_inc[GMT_X] - G->header->xy_off;
2535 		fy[k] = (G->header->wesn[YHI] - y[k]) * HH->r_inc[GMT_Y] - G->header->xy_off;
2536 	}
2537 
2538 	/* Get(i,j) of the lower left node in the rectangle containing this contour segment.
2539 	   We use the average x and y coordinate for this to avoid any round-off involved in
2540 	   working on a single coordinate. The average coordinate should always be inside the
2541 	   rectangle and hence the floor/ceil operators will yield the LL node. */
2542 
2543 	i = lrint (floor (0.5 * (fx[0] + fx[1])));
2544 	j = lrint (ceil  (0.5 * (fy[0] + fy[1])));
2545 	ij_ll = gmt_M_ijp (G->header, j, i);     /* lower left corner  */
2546 	ij_lr = gmt_M_ijp (G->header, j, i+1);   /* lower right corner */
2547 	ij_ul = gmt_M_ijp (G->header, j-1, i);   /* upper left corner  */
2548 	ij_ur = gmt_M_ijp (G->header, j-1, i+1); /* upper right corner */
2549 
2550 	for (k = 0; k < 2; k++) {	/* Determine which edge the contour points lie on (0-3) */
2551 		/* We KNOW that for each k, either x[k] or y[k] lies EXACTLY on a gridline.  This is used
2552 		 * to deal with the inevitable round-off that places points slightly off the gridline.  We
2553 		 * pick the coordinate closest to the gridline as the one that should be exactly on the gridline */
2554 
2555 		k2 = 1 - k;	/* The other point */
2556 		dx = fmod (fx[k], 1.0);
2557 		if (dx > 0.5) dx = 1.0 - dx;	/* Fraction to closest vertical gridline */
2558 		dy = fmod (fy[k], 1.0);
2559 		if (dy > 0.5) dy = 1.0 - dy;	/* Fraction to closest horizontal gridline */
2560 		if (dx < dy)		/* Point is on a vertical grid line (left [3] or right [1]) */
2561 			side[k] = (fx[k] < fx[k2]) ? 3 : 1;	/* Simply check order of fx to determine which it is */
2562 		else						/* Point must be on horizontal grid line (top [2] or bottom [0]) */
2563 			side[k] = (fy[k] > fy[k2]) ? 0 : 2;	/* Same for fy */
2564 	}
2565 
2566 	switch (side[0]) {	/* Entry side: check heights of corner points.*/
2567 	                        /* if point to the right of the line is higher z_dir = +1 else -1 */
2568 		case 0:	/* Bottom: check heights of lower left and lower right nodes */
2569 			z_dir = (G->data[ij_lr] > G->data[ij_ll]) ? +1 : -1;
2570 			break;
2571 		case 1:	/* Right */
2572 			z_dir = (G->data[ij_ur] > G->data[ij_lr]) ? +1 : -1;
2573 			break;
2574 		case 2:	/* Top */
2575 			z_dir = (G->data[ij_ul] > G->data[ij_ur]) ? +1 : -1;
2576 			break;
2577 		default:/* Left */
2578 			z_dir = (G->data[ij_ll] > G->data[ij_ul]) ? +1 : -1;
2579 			break;
2580 	}
2581 	reverse = (z_dir != orient);
2582 
2583 	if (reverse) {	/* Must reverse order of contour */
2584 		for (i = 0, j = n-1; i < n/2; i++, j--) {
2585 			gmt_M_double_swap (x[i], x[j]);
2586 			gmt_M_double_swap (y[i], y[j]);
2587 		}
2588 	}
2589 }
2590 
2591 /*! . */
gmtsupport_label_is_OK(struct GMT_CTRL * GMT,struct GMT_LABEL * L,char * this_label,char * label,double this_dist,double this_value_dist,uint64_t xl,uint64_t fj,struct GMT_CONTOUR * G)2592 GMT_LOCAL bool gmtsupport_label_is_OK (struct GMT_CTRL *GMT, struct GMT_LABEL *L, char *this_label, char *label, double this_dist, double this_value_dist, uint64_t xl, uint64_t fj, struct GMT_CONTOUR *G) {
2593 	/* Determines if the proposed label passes various tests.  Return true if we should go ahead and add this label to the list */
2594 	bool label_OK = true;
2595 	uint64_t seg, k;
2596 	char format[GMT_LEN256] = {""};
2597 	struct GMT_CONTOUR_LINE *S = NULL;
2598 
2599 	if (G->isolate) {	/* Must determine if the proposed label is within radius distance of any other label already accepted */
2600 		for (seg = 0; seg < G->n_segments; seg++) {	/* Previously processed labels */
2601 			S = G->segment[seg];	/* Pointer to current segment */
2602 			for (k = 0; k < S->n_labels; k++) if (hypot (L->x - S->L[k].x, L->y - S->L[k].y) < G->label_isolation) return (false);
2603 		}
2604 		/* Also check labels for current segment */
2605 		for (k = 0; k < G->n_label; k++) if (hypot (L->x - G->L[k]->x, L->y - G->L[k]->y) < G->label_isolation) return (false);
2606 	}
2607 
2608 	switch (G->label_type) {
2609 		case GMT_LABEL_IS_NONE:
2610 			if (label && label[0])
2611 				strcpy (this_label, label);
2612 			else
2613 				label_OK = false;
2614 			break;
2615 
2616 		case GMT_LABEL_IS_CONSTANT:
2617 		case GMT_LABEL_IS_HEADER:
2618 			if (G->label[0])
2619 				strcpy (this_label, G->label);
2620 			else
2621 				label_OK = false;
2622 			break;
2623 
2624 		case GMT_LABEL_IS_PDIST:
2625 			if (G->spacing) {	/* Distances are even so use special contour format */
2626 				gmt_get_format (GMT, this_dist * GMT->session.u2u[GMT_INCH][G->dist_unit], G->unit, NULL, format);
2627 				gmt_sprintf_float (GMT, this_label, format, this_dist * GMT->session.u2u[GMT_INCH][G->dist_unit]);
2628 			}
2629 			else {
2630 				gmt_sprintf_float (GMT, this_label, GMT->current.setting.format_float_map, this_dist * GMT->session.u2u[GMT_INCH][G->dist_unit]);
2631 			}
2632 			break;
2633 
2634 		case GMT_LABEL_IS_MDIST:
2635 			gmt_sprintf_float (GMT, this_label, GMT->current.setting.format_float_map, this_value_dist);
2636 			break;
2637 
2638 		case GMT_LABEL_IS_FFILE:
2639 			if (G->f_label[fj] && G->f_label[fj][0])
2640 				strcpy (this_label, G->f_label[fj]);
2641 			else
2642 				label_OK = false;
2643 			break;
2644 
2645 		case GMT_LABEL_IS_XFILE:
2646 			if (G->X->table[0]->segment[xl]->label && G->X->table[0]->segment[xl]->label[0])
2647 				strcpy (this_label, G->X->table[0]->segment[xl]->label);
2648 			else
2649 				label_OK = false;
2650 			break;
2651 
2652 		case GMT_LABEL_IS_SEG:
2653 			sprintf (this_label, "%" PRIu64, (GMT->current.io.status & GMT_IO_SEGMENT_HEADER) ? GMT->current.io.seg_no - 1 : GMT->current.io.seg_no);
2654 			break;
2655 
2656 		case GMT_LABEL_IS_FSEG:
2657 			sprintf (this_label, "%d/%" PRIu64, GMT->current.io.tbl_no, (GMT->current.io.status & GMT_IO_SEGMENT_HEADER) ? GMT->current.io.seg_no - 1 : GMT->current.io.seg_no);
2658 			break;
2659 
2660 		default:	/* Should not happen... */
2661 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT internal error - no label set\n");
2662 			this_label[0] = '\0';
2663 			return false;
2664 			break;
2665 	}
2666 
2667 	return (label_OK);
2668 }
2669 
2670 /*! . */
gmtsupport_place_label(struct GMT_CTRL * GMT,struct GMT_LABEL * L,char * txt,struct GMT_CONTOUR * G,bool use_unit,size_t extra)2671 GMT_LOCAL void gmtsupport_place_label (struct GMT_CTRL *GMT, struct GMT_LABEL *L, char *txt, struct GMT_CONTOUR *G, bool use_unit, size_t extra) {
2672 	/* Allocates needed space and copies in the label */
2673 	size_t n, m = 0;
2674 
2675 	if (use_unit && G->unit[0])
2676 		m = strlen (G->unit);
2677 	n = strlen (txt) + 1 + m + extra;
2678 	if (G->prefix[0]) {	/* Must prepend the prefix string */
2679 		n += strlen (G->prefix) + 1;
2680 		L->label = gmt_M_memory (GMT, NULL, n, char);
2681 		sprintf (L->label, "%s%s", G->prefix, txt);
2682 	}
2683 	else {
2684 		L->label = gmt_M_memory (GMT, NULL, n, char);
2685 		strcpy (L->label, txt);
2686 	}
2687 	if (use_unit && G->unit[0]) {	/* Append a unit string */
2688 		strcat (L->label, G->unit);
2689 	}
2690 	gmt_M_memcpy (L->rgb, G->font_label.fill.rgb, 4, double);	/* Remember color in case it varies */
2691 }
2692 
2693 /*! . */
gmtsupport_hold_contour_sub(struct GMT_CTRL * GMT,double ** xxx,double ** yyy,uint64_t nn,double zval,char * label,char ctype,double cangle,bool closed,bool contour,struct GMT_CONTOUR * G)2694 GMT_LOCAL void gmtsupport_hold_contour_sub (struct GMT_CTRL *GMT, double **xxx, double **yyy, uint64_t nn, double zval, char *label, char ctype, double cangle, bool closed, bool contour, struct GMT_CONTOUR *G) {
2695 	/* The xxx, yyy are expected to be projected x/y inches */
2696 	uint64_t i, j, start = 0;
2697 	size_t n_alloc = GMT_SMALL_CHUNK;
2698 	double *track_dist = NULL, *map_dist = NULL, *value_dist = NULL, *radii = NULL, *xx = NULL, *yy = NULL;
2699 	double dx, dy, width, f, this_dist, step, stept, this_value_dist, lon[2], lat[2];
2700 	struct GMT_LABEL *new_label = NULL;
2701 	char this_label[GMT_BUFSIZ] = {""};
2702 
2703 	if (nn < 2) return;
2704 
2705 	xx = *xxx;	yy = *yyy;
2706 	G->n_label = 0;
2707 
2708 	/* OK, line is long enough to be added to array of lines */
2709 
2710 	if (ctype == 'A' || ctype == 'a') {	/* Annotated contours, must find label placement */
2711 
2712 		/* Calculate distance along contour and store in track_dist array */
2713 
2714 		if (G->dist_kind == 1) gmt_xy_to_geo (GMT, &lon[1], &lat[1], xx[0], yy[0]);
2715 		map_dist = gmt_M_memory (GMT, NULL, nn, double);	/* Distances on map in inches */
2716 		track_dist = gmt_M_memory (GMT, NULL, nn, double);	/* May be km ,degrees or whatever */
2717 		value_dist = gmt_M_memory (GMT, NULL, nn, double);	/* May be km ,degrees or whatever */
2718 		radii = gmt_M_memory (GMT, NULL, nn, double);	/* Radius of curvature, in inches */
2719 
2720 		/* We will calculate the radii of curvature at all points.  By default we don't care and
2721 		 * will place labels at whatever distance we end up with.  However, if the user has asked
2722 		 * for a minimum limit on the radius of curvature [Default 0] we do not want to place labels
2723 		 * at those sections where the curvature is large.  Since labels are placed according to
2724 		 * distance along track, the way we deal with this is to set distance increments to zero
2725 		 * where curvature is large:  that way, there is no increase in distance over those patches
2726 		 * and the machinery for determining when we exceed the next label distance will not kick
2727 		 * in until after curvature drops and increments are again nonzero.  This procedure only
2728 		 * applies to the algorithms based on distance along track.
2729 		 */
2730 
2731 		gmtsupport_get_radii_of_curvature (xx, yy, nn, radii);
2732 
2733 		map_dist[0] = track_dist[0] = value_dist[0] = 0.0;	/* Unnecessary, just so we understand the logic */
2734 		for (i = 1; i < nn; i++) {
2735 			/* Distance from xy */
2736 			dx = xx[i] - xx[i-1];
2737 			if (gmt_M_x_is_lon (GMT, GMT_IN) && GMT->current.map.is_world && fabs (dx) > (width = gmt_half_map_width (GMT, yy[i-1]))) {
2738 				width *= 2.0;
2739 				dx = copysign (width - fabs (dx), -dx);
2740 				if (xx[i] < width)
2741 					xx[i-1] -= width;
2742 				else
2743 					xx[i-1] += width;
2744 			}
2745 			dy = yy[i] - yy[i-1];
2746 			step = stept = hypot (dx, dy);
2747 			map_dist[i] = map_dist[i-1] + step;
2748 			if (G->dist_kind == 1 || G->label_type == GMT_LABEL_IS_MDIST) {
2749 				lon[0] = lon[1];	lat[0] = lat[1];
2750 				gmt_xy_to_geo (GMT, &lon[1], &lat[1], xx[i], yy[i]);
2751 				if (G->dist_kind == 1) step = gmtlib_distance_type (GMT, lon[0], lat[0], lon[1], lat[1], GMT_CONT_DIST);
2752 				if (G->label_type == GMT_LABEL_IS_MDIST) stept = gmtlib_distance_type (GMT, lon[0], lat[0], lon[1], lat[1], GMT_LABEL_DIST);
2753 			}
2754 			if (radii[i] < G->min_radius) step = stept = 0.0;	/* If curvature is too great we simply don't add up distances */
2755 			track_dist[i] = track_dist[i-1] + step;
2756 			value_dist[i] = value_dist[i-1] + stept;
2757 		}
2758 		gmt_M_free (GMT, radii);
2759 
2760 		/* G->L array is only used so we can later sort labels based on distance along track.  Once
2761 		 * GMT_contlabel_draw has been called we will free up the memory as the labels are kept in
2762 		 * the linked list starting at G->anchor. */
2763 
2764 		G->L = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_LABEL *);
2765 
2766 		if (G->spacing) {	/* Place labels based on distance along contours */
2767 			double last_label_dist, dist_offset, dist;
2768 
2769 			dist_offset = (closed && G->dist_kind == 0) ? (1.0 - G->label_dist_frac) * G->label_dist_spacing : 0.0;	/* Label closed contours longer than frac of dist_spacing */
2770 			last_label_dist = 0.0;
2771 			i = 1;
2772 			while (i < nn) {
2773 
2774 				dist = track_dist[i] + dist_offset - last_label_dist;
2775 				if (dist > G->label_dist_spacing) {	/* Time for label */
2776 					new_label = gmt_M_memory (GMT, NULL, 1, struct GMT_LABEL);
2777 					f = (dist - G->label_dist_spacing) / (track_dist[i] - track_dist[i-1]);
2778 					if (f < 0.5) {
2779 						new_label->x = xx[i] - f * (xx[i] - xx[i-1]);
2780 						new_label->y = yy[i] - f * (yy[i] - yy[i-1]);
2781 						new_label->dist = map_dist[i] - f * (map_dist[i] - map_dist[i-1]);
2782 						this_value_dist = value_dist[i] - f * (value_dist[i] - value_dist[i-1]);
2783 					}
2784 					else {
2785 						f = 1.0 - f;
2786 						new_label->x = xx[i-1] + f * (xx[i] - xx[i-1]);
2787 						new_label->y = yy[i-1] + f * (yy[i] - yy[i-1]);
2788 						new_label->dist = map_dist[i-1] + f * (map_dist[i] - map_dist[i-1]);
2789 						this_value_dist = value_dist[i-1] + f * (value_dist[i] - value_dist[i-1]);
2790 					}
2791 					this_dist = G->label_dist_spacing - dist_offset + last_label_dist;
2792 					if (gmtsupport_label_is_OK (GMT, new_label, this_label, label, this_dist, this_value_dist, 0, 0, G)) {
2793 						gmtsupport_place_label (GMT, new_label, this_label, G, !(G->label_type == GMT_LABEL_IS_NONE || G->label_type == GMT_LABEL_IS_PDIST), 0);
2794 						new_label->node = i - 1;
2795 						gmtsupport_contlabel_angle (GMT, xx, yy, i - 1, i, cangle, nn, contour, new_label, G);
2796 						G->L[G->n_label++] = new_label;
2797 						if (G->n_label == n_alloc) {
2798 							size_t old_n_alloc = n_alloc;
2799 							n_alloc <<= 1;
2800 							G->L = gmt_M_memory (GMT, G->L, n_alloc, struct GMT_LABEL *);
2801 							gmt_M_memset (&(G->L[old_n_alloc]), n_alloc - old_n_alloc, struct GMT_LABEL *);	/* Set to NULL */
2802 						}
2803 					}
2804 					else	/* All in vain... */
2805 						gmt_M_free (GMT, new_label);
2806 					dist_offset = 0.0;
2807 					last_label_dist = this_dist;
2808 				}
2809 				else	/* Go to next point in line */
2810 					i++;
2811 			}
2812 			if (G->n_label == 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your -Gd|D option produced no contour labels for z = %g\n", zval);
2813 
2814 		}
2815 		if (G->number) {	/* Place prescribed number of labels evenly along contours */
2816 			uint64_t nc;
2817 			int e_val = 0;
2818 			double dist, last_dist;
2819 
2820 			last_dist = (G->n_cont > 1) ? -map_dist[nn-1] / (G->n_cont - 1) : -0.5 * map_dist[nn-1];
2821 			nc = (map_dist[nn-1] > G->min_dist) ? G->n_cont : 0;
2822 			for (i = j = 0; i < nc; i++) {
2823 				new_label = gmt_M_memory (GMT, NULL, 1, struct GMT_LABEL);
2824 				if (G->number_placement && !closed) {
2825 					e_val = G->number_placement;
2826 					if (G->number_placement == -1 && G->n_cont == 1) {	/* Label justified with start of segment */
2827 						f = d_atan2d (xx[0] - xx[1], yy[0] - yy[1]) + 180.0;	/* 0-360 */
2828 						G->end_just[0] = (f >= 90.0 && f <= 270) ? 7 : 5;
2829 					}
2830 					else if (G->number_placement == +1 && G->n_cont == 1) {	/* Label justified with end of segment */
2831 						f = d_atan2d (xx[nn-1] - xx[nn-2], yy[nn-1] - yy[nn-2]) + 180.0;	/* 0-360 */
2832 						G->end_just[1] = (f >= 90.0 && f <= 270) ? 7 : 5;
2833 					}
2834 					else if (G->number_placement && G->n_cont > 1)	/* One of the end labels */
2835 						e_val = (i == 0) ? -1 : +1;
2836 					dist = (G->n_cont > 1) ? i * track_dist[nn-1] / (G->n_cont - 1) : 0.5 * (G->number_placement + 1.0) * track_dist[nn-1];
2837 					this_value_dist = (G->n_cont > 1) ? i * value_dist[nn-1] / (G->n_cont - 1) : 0.5 * (G->number_placement + 1.0) * value_dist[nn-1];
2838 				}
2839 				else {
2840 					dist = (i + 1 - 0.5 * closed) * track_dist[nn-1] / (G->n_cont + 1 - closed);
2841 					this_value_dist = (i + 1 - 0.5 * closed) * value_dist[nn-1] / (G->n_cont + 1 - closed);
2842 				}
2843 				while (j < nn && track_dist[j] < dist) j++;
2844 				if (j == nn) j--;	/* Safety precaution */
2845 				f = ((j == 0) ? 1.0 : (dist - track_dist[j-1]) / (track_dist[j] - track_dist[j-1]));
2846 				if (f < 0.5) {	/* Pick the smallest fraction to minimize Taylor shortcomings */
2847 					new_label->x = xx[j-1] + f * (xx[j] - xx[j-1]);
2848 					new_label->y = yy[j-1] + f * (yy[j] - yy[j-1]);
2849 					new_label->dist = map_dist[j-1] + f * (map_dist[j] - map_dist[j-1]);
2850 				}
2851 				else {
2852 					f = 1.0 - f;
2853 					new_label->x = (j == 0) ? xx[0] : xx[j] - f * (xx[j] - xx[j-1]);
2854 					new_label->y = (j == 0) ? yy[0] : yy[j] - f * (yy[j] - yy[j-1]);
2855 					new_label->dist = (j == 0) ? 0.0 : map_dist[j] - f * (map_dist[j] - map_dist[j-1]);
2856 				}
2857 				if ((new_label->dist - last_dist) >= G->min_dist) {	/* OK to accept this label */
2858 					this_dist = dist;
2859 					if (gmtsupport_label_is_OK (GMT, new_label, this_label, label, this_dist, this_value_dist, 0, 0, G)) {
2860 						size_t extra = (G->crossect) ? strlen (G->crossect_tag[i]) + 1 : 0;	/* Need to increase allocated space */
2861 						gmtsupport_place_label (GMT, new_label, this_label, G, !(G->label_type == GMT_LABEL_IS_NONE), extra);
2862 						if (G->crossect) {	/* Special crossection mode */
2863 							if (!strcmp (new_label->label, "N/A"))	/* Override the N/A lack of label identifier */
2864 								strcpy (new_label->label, G->crossect_tag[i]);
2865 							else	/* Append tag to the label */
2866 								strcat (new_label->label, G->crossect_tag[i]);
2867 						}
2868 						new_label->node = (j == 0) ? 0 : j - 1;
2869 						gmtsupport_contlabel_angle (GMT, xx, yy, new_label->node, j, cangle, nn, contour, new_label, G);
2870 						if (G->number_placement) new_label->end = e_val;
2871 						G->L[G->n_label++] = new_label;
2872 						if (G->n_label == n_alloc) {
2873 							size_t old_n_alloc = n_alloc;
2874 							n_alloc <<= 1;
2875 							G->L = gmt_M_memory (GMT, G->L, n_alloc, struct GMT_LABEL *);
2876 							gmt_M_memset (&(G->L[old_n_alloc]), n_alloc - old_n_alloc, struct GMT_LABEL *);	/* Set to NULL */
2877 						}
2878 						last_dist = new_label->dist;
2879 					}
2880 					else /* Label had no text */
2881 						gmt_M_free (GMT, new_label);
2882 				}
2883 				else	/* All in vain... */
2884 					gmt_M_free (GMT, new_label);
2885 			}
2886 			if (G->n_label == 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your -Gn|N option produced no contour labels for z = %g\n", zval);
2887 		}
2888 		if (G->crossing) {	/* Determine label positions based on crossing lines */
2889 			uint64_t left, right, line_no;
2890 			struct GMT_DATASEGMENT *S = NULL;
2891 			gmt_init_track (GMT, yy, nn, &(G->ylist));
2892 			for (line_no = 0; line_no < G->X->n_segments; line_no++) {	/* For each of the crossing lines */
2893 				S = G->X->table[0]->segment[line_no];	/* Current segment */
2894 				gmt_init_track (GMT, S->data[GMT_Y], S->n_rows, &(G->ylist_XP));
2895 				G->nx = (unsigned int)gmt_crossover (GMT, S->data[GMT_X], S->data[GMT_Y], NULL, G->ylist_XP, S->n_rows, xx, yy, NULL, G->ylist, nn, false, gmt_M_x_is_lon (GMT, GMT_IN), &G->XC);
2896 				gmt_M_free (GMT, G->ylist_XP);
2897 				if (G->nx == 0) continue;
2898 
2899 				/* OK, we found intersections for labels */
2900 
2901 				for (i = 0; i < (uint64_t)G->nx; i++) {
2902 					left  = lrint (floor (G->XC.xnode[1][i]));
2903 					right = lrint (ceil  (G->XC.xnode[1][i]));
2904 					new_label = gmt_M_memory (GMT, NULL, 1, struct GMT_LABEL);
2905 					new_label->x = G->XC.x[i];
2906 					new_label->y = G->XC.y[i];
2907 					new_label->node = left;
2908 					new_label->dist = track_dist[left];
2909 					f = G->XC.xnode[1][i] - left;
2910 					if (f < 0.5) {
2911 						this_dist = track_dist[left] + f * (track_dist[right] - track_dist[left]);
2912 						new_label->dist = map_dist[left] + f * (map_dist[right] - map_dist[left]);
2913 						this_value_dist = value_dist[left] + f * (value_dist[right] - value_dist[left]);
2914 					}
2915 					else {
2916 						f = 1.0 - f;
2917 						this_dist = track_dist[right] - f * (track_dist[right] - track_dist[left]);
2918 						new_label->dist = map_dist[right] - f * (map_dist[right] - map_dist[left]);
2919 						this_value_dist = value_dist[right] - f * (value_dist[right] - value_dist[left]);
2920 					}
2921 					if (gmtsupport_label_is_OK (GMT, new_label, this_label, label, this_dist, this_value_dist, line_no, 0, G)) {
2922 						gmtsupport_place_label (GMT, new_label, this_label, G, !(G->label_type == GMT_LABEL_IS_NONE), 0);
2923 						gmtsupport_contlabel_angle (GMT, xx, yy, left, right, cangle, nn, contour, new_label, G);
2924 						G->L[G->n_label++] = new_label;
2925 						if (G->n_label == n_alloc) {
2926 							size_t old_n_alloc = n_alloc;
2927 							n_alloc <<= 1;
2928 							G->L = gmt_M_memory (GMT, G->L, n_alloc, struct GMT_LABEL *);
2929 							gmt_M_memset (&(G->L[old_n_alloc]), n_alloc - old_n_alloc, struct GMT_LABEL *);	/* Set to NULL */
2930 						}
2931 					}
2932 					else	/* All in vain... */
2933 						gmt_M_free (GMT, new_label);
2934 				}
2935 				gmt_x_free (GMT, &G->XC);
2936 			}
2937 			gmt_M_free (GMT, G->ylist);
2938 			if (G->n_label == 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your -Gx|X|l|L option produced no contour labels for z = %g\n", zval);
2939 		}
2940 		if (G->fixed) {	/* Prescribed point locations for labels that match points in input records */
2941 			double dist, min_dist;
2942 			for (j = 0; j < (uint64_t)G->f_n; j++) {	/* Loop over fixed point list */
2943 				min_dist = DBL_MAX;
2944 				for (i = 0; i < nn; i++) {	/* Loop over input line/contour */
2945 					if ((dist = hypot (xx[i] - G->f_xy[0][j], yy[i] - G->f_xy[1][j])) < min_dist) {	/* Better fit */
2946 						min_dist = dist;
2947 						start = i;
2948 					}
2949 				}
2950 				if (min_dist < G->slop) {	/* Closest point within tolerance */
2951 					new_label = gmt_M_memory (GMT, NULL, 1, struct GMT_LABEL);
2952 					new_label->x = xx[start];
2953 					new_label->y = yy[start];
2954 					new_label->node = start;
2955 					new_label->dist = track_dist[start];
2956 					this_dist = track_dist[start];
2957 					new_label->dist = map_dist[start];
2958 					this_value_dist = value_dist[start];
2959 					if (gmtsupport_label_is_OK (GMT, new_label, this_label, label, this_dist, this_value_dist, 0, j, G)) {
2960 						gmtsupport_place_label (GMT, new_label, this_label, G, !(G->label_type == GMT_LABEL_IS_NONE), 0);
2961 						gmtsupport_contlabel_angle (GMT, xx, yy, start, start, cangle, nn, contour, new_label, G);
2962 						G->L[G->n_label++] = new_label;
2963 						if (G->n_label == n_alloc) {
2964 							size_t old_n_alloc = n_alloc;
2965 							n_alloc <<= 1;
2966 							G->L = gmt_M_memory (GMT, G->L, n_alloc, struct GMT_LABEL *);
2967 							gmt_M_memset (&(G->L[old_n_alloc]), n_alloc - old_n_alloc, struct GMT_LABEL *);	/* Set to NULL */
2968 						}
2969 					}
2970 					else	/* All in vain... */
2971 						gmt_M_free (GMT, new_label);
2972 				}
2973 			}
2974 
2975 			if (G->n_label == 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your -Gf option produced no contour labels for z = %g\n", zval);
2976 		}
2977 		gmtsupport_contlabel_fixpath (GMT, &xx, &yy, map_dist, &nn, G);	/* Inserts the label x,y into path */
2978 		gmtsupport_contlabel_addpath (GMT, xx, yy, nn, zval, label, true, G);		/* Appends this path and the labels to list */
2979 
2980 		gmt_M_free (GMT, track_dist);
2981 		gmt_M_free (GMT, map_dist);
2982 		gmt_M_free (GMT, value_dist);
2983 		/* Free label structure since info is now copied to segment labels */
2984 		for (i = 0; i < (uint64_t)G->n_label; i++) {
2985 			gmt_M_free (GMT, G->L[i]->label);
2986 			gmt_M_free (GMT, G->L[i]);
2987 		}
2988 		gmt_M_free (GMT, G->L);
2989 	}
2990 	else {   /* just one line, no holes for labels */
2991 		gmtsupport_contlabel_addpath (GMT, xx, yy, nn, zval, label, false, G);		/* Appends this path to list */
2992 	}
2993 	*xxx = xx;
2994 	*yyy = yy;
2995 }
2996 
2997 /*! . */
gmtsupport_add_decoration(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,struct GMT_LABEL * L,struct GMT_DECORATE * G)2998 GMT_LOCAL void gmtsupport_add_decoration (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, struct GMT_LABEL *L, struct GMT_DECORATE *G) {
2999 	/* Add a symbol location to the growing segment */
3000 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
3001 	if (S->n_rows == SH->n_alloc) {	/* Need more memory for the segment */
3002 		uint64_t col;
3003 		SH->n_alloc += GMT_SMALL_CHUNK;
3004 		for (col = 0; col < S->n_columns; col++) {
3005 			S->data[col] = gmt_M_memory (GMT, S->data[col], SH->n_alloc, double);
3006 			SH->alloc_mode[col] = GMT_ALLOC_INTERNALLY;
3007 		}
3008 		S->text = gmt_M_memory (GMT, S->text, SH->n_alloc, char *);
3009 	}
3010 	/* Deal with any justifications or nudging */
3011 	if (G->nudge_flag) {	/* Must adjust point a bit */
3012 		double s = 0.0, c = 1.0, sign = 1.0;
3013 		if (G->nudge_flag == 2) sincosd (L->line_angle, &s, &c);
3014 		/* If N+1 or N-1 is used we want positive x nudge to extend away from end point */
3015 		sign = (G->number_placement) ? (double)L->end : 1.0;
3016 		L->x += sign * (G->nudge[GMT_X] * c - G->nudge[GMT_Y] * s);
3017 		L->y += sign * (G->nudge[GMT_X] * s + G->nudge[GMT_Y] * c);
3018 	}
3019 	/* Build record with Cartesian (hence col = GMT_Z) "x y angle symbol" since we are using -Jx1i */
3020 	S->data[GMT_X][S->n_rows] = L->x;
3021 	S->data[GMT_Y][S->n_rows] = L->y;
3022 	S->data[GMT_Z][S->n_rows] = gmt_M_to_inch (GMT, G->size);
3023 	S->data[3][S->n_rows] = L->line_angle;	/* Change this in inches internally instead of string */
3024 	S->text[S->n_rows++] = strdup (G->symbol_code);
3025 }
3026 
3027 /*! . */
gmtsupport_decorated_line_sub(struct GMT_CTRL * GMT,double * xx,double * yy,uint64_t nn,struct GMT_DECORATE * G,struct GMT_DATASET * D,uint64_t seg)3028 GMT_LOCAL void gmtsupport_decorated_line_sub (struct GMT_CTRL *GMT, double *xx, double *yy, uint64_t nn, struct GMT_DECORATE *G, struct GMT_DATASET *D, uint64_t seg) {
3029 	/* The xxx, yyy are expected to be projected x/y inches.
3030 	 * This function is modelled after gmtsupport_hold_contour_sub but tweaked to deal with
3031 	 * the placement of psxy-symbols rather that text labels.  This is in most regards
3032 	 * simpler than placing text so many lines related to text have been yanked.  There
3033 	 * is the assumption that the symbols will be filled so we make no attempt to clip
3034 	 * the lines as we do for contours/quated lines.  It would be very complicated to
3035 	 * determine that clip path if the symbol is a star, for instance.  So if fill is
3036 	 * not given then we will see the line beneath the symbol. */
3037 	uint64_t i, j, start = 0;
3038 	bool closed;
3039 	double *track_dist = NULL, *map_dist = NULL, *value_dist = NULL;
3040 	double dx, dy, width, f, this_dist, step, stept, lon[2], lat[2];
3041 	struct GMT_DATASEGMENT *S = D->table[0]->segment[seg];	/* A single segment */
3042 	struct GMT_LABEL L;	/* Needed to pick up angles */
3043 	if (nn < 2) return;	/* You, sir, are not a line! */
3044 
3045 	closed = (nn > 2 && !gmt_polygon_is_open (GMT, xx, yy, nn));	/* true if this is a polygon */
3046 
3047 	/* Calculate distance along line and store in track_dist array */
3048 
3049 	if (G->dist_kind == 1) gmt_xy_to_geo (GMT, &lon[1], &lat[1], xx[0], yy[0]);
3050 	map_dist   = gmt_M_memory (GMT, NULL, nn, double);	/* Distances on map in inches */
3051 	track_dist = gmt_M_memory (GMT, NULL, nn, double);	/* May be km, degrees or whatever */
3052 	value_dist = gmt_M_memory (GMT, NULL, nn, double);	/* May be km, degrees or whatever */
3053 
3054 	map_dist[0] = track_dist[0] = value_dist[0] = 0.0;	/* Unnecessary, just so we understand the logic */
3055 	for (i = 1; i < nn; i++) {
3056 		/* Distance from xy in plot distances (inch) */
3057 		dx = xx[i] - xx[i-1];
3058 		if (gmt_M_x_is_lon (GMT, GMT_IN) && GMT->current.map.is_world && fabs (dx) > (width = gmt_half_map_width (GMT, yy[i-1]))) {
3059 			width *= 2.0;
3060 			dx = copysign (width - fabs (dx), -dx);
3061 			if (xx[i] < width)
3062 				xx[i-1] -= width;
3063 			else
3064 				xx[i-1] += width;
3065 		}
3066 		dy = yy[i] - yy[i-1];
3067 		step = stept = hypot (dx, dy);	/* Initially these steps are in inches */
3068 		map_dist[i] = map_dist[i-1] + step;
3069 		if (G->dist_kind == 1) {	/* Wanted spacing in map distance units */
3070 			lon[0] = lon[1];	lat[0] = lat[1];
3071 			gmt_xy_to_geo (GMT, &lon[1], &lat[1], xx[i], yy[i]);
3072 			if (G->dist_kind == 1) step = gmtlib_distance_type (GMT, lon[0], lat[0], lon[1], lat[1], GMT_CONT_DIST);
3073 		}
3074 		track_dist[i] = track_dist[i-1] + step;
3075 		value_dist[i] = value_dist[i-1] + stept;
3076 	}
3077 
3078 	if (G->spacing) {	/* Place symbols based on distance along lines */
3079 		double last_label_dist, dist_offset, dist;
3080 
3081 		dist_offset = (closed && G->dist_kind == 0) ? (1.0 - G->symbol_dist_frac) * G->symbol_dist_spacing : 0.0;	/* Only place symbols on closed contours longer than frac of dist_spacing */
3082 		last_label_dist = 0.0;
3083 		i = 1;
3084 		while (i < nn) {
3085 			dist = track_dist[i] + dist_offset - last_label_dist;
3086 			if (dist > G->symbol_dist_spacing) {	/* Time for symbol */
3087 				f = (dist - G->symbol_dist_spacing) / (track_dist[i] - track_dist[i-1]);
3088 				if (f < 0.5) {
3089 					L.x = xx[i] - f * (xx[i] - xx[i-1]);
3090 					L.y = yy[i] - f * (yy[i] - yy[i-1]);
3091 				}
3092 				else {
3093 					f = 1.0 - f;
3094 					L.x = xx[i-1] + f * (xx[i] - xx[i-1]);
3095 					L.y = yy[i-1] + f * (yy[i] - yy[i-1]);
3096 				}
3097 				this_dist = G->symbol_dist_spacing - dist_offset + last_label_dist;
3098 				gmtsupport_decorated_angle (GMT, xx, yy, i - 1, i, G->symbol_angle, nn, false, &L, G);
3099 				gmtsupport_add_decoration (GMT, S, &L, G);
3100 				dist_offset = 0.0;
3101 				last_label_dist = this_dist;
3102 			}
3103 			else	/* Go to next point in line */
3104 				i++;
3105 		}
3106 	}
3107 	else if (G->number) {	/* Place prescribed number of symbols evenly along lines */
3108 		uint64_t nc;
3109 		double dist;
3110 		nc = (map_dist[nn-1] > G->min_dist) ? G->n_cont : 0;
3111 		L.end = 0;
3112 		for (i = j = 0; i < nc; i++) {
3113 			if (G->number_placement && !closed) {
3114 				dist = (G->n_cont > 1) ? i * track_dist[nn-1] / (G->n_cont - 1) : 0.5 * (G->number_placement + 1.0) * track_dist[nn-1];
3115 				L.end = (G->number_placement && G->n_cont > 1) ? ((i == 0) ? -1 : +1) : G->number_placement;
3116 			}
3117 			else
3118 				dist = (i + 1 - 0.5 * closed) * track_dist[nn-1] / (G->n_cont + 1 - closed);
3119 			while (j < nn && track_dist[j] < dist) j++;
3120 			if (j == nn) j--;	/* Safety precaution */
3121 			f = ((j == 0) ? 1.0 : (dist - track_dist[j-1]) / (track_dist[j] - track_dist[j-1]));
3122 			if (f < 0.5) {	/* Pick the smallest fraction to minimize Taylor shortcomings */
3123 				L.x = xx[j-1] + f * (xx[j] - xx[j-1]);
3124 				L.y = yy[j-1] + f * (yy[j] - yy[j-1]);
3125 			}
3126 			else {
3127 				f = 1.0 - f;
3128 				L.x = (j == 0) ? xx[0] : xx[j] - f * (xx[j] - xx[j-1]);
3129 				L.y = (j == 0) ? yy[0] : yy[j] - f * (yy[j] - yy[j-1]);
3130 			}
3131 			L.node = (j == 0) ? 0 : j - 1;
3132 			gmtsupport_decorated_angle (GMT, xx, yy, L.node, j, G->symbol_angle, nn, false, &L, G);
3133 			gmtsupport_add_decoration (GMT, S, &L, G);
3134 		}
3135 	}
3136 	else if (G->crossing) {	/* Determine label positions based on crossing lines */
3137 		uint64_t left, right, line_no;
3138 		struct GMT_DATASEGMENT *Sd = NULL;
3139 		gmt_init_track (GMT, yy, nn, &(G->ylist));
3140 		for (line_no = 0; line_no < G->X->n_segments; line_no++) {	/* For each of the crossing lines */
3141 			Sd = G->X->table[0]->segment[line_no];	/* Current segment */
3142 			gmt_init_track (GMT, Sd->data[GMT_Y], Sd->n_rows, &(G->ylist_XP));
3143 			G->nx = (unsigned int)gmt_crossover (GMT, Sd->data[GMT_X], Sd->data[GMT_Y], NULL, G->ylist_XP, Sd->n_rows, xx, yy, NULL, G->ylist, nn, false, gmt_M_x_is_lon (GMT, GMT_IN), &G->XC);
3144 			gmt_M_free (GMT, G->ylist_XP);
3145 			if (G->nx == 0) continue;
3146 
3147 			/* OK, we found intersections for labels */
3148 
3149 			for (i = 0; i < (uint64_t)G->nx; i++) {
3150 				left  = lrint (floor (G->XC.xnode[1][i]));
3151 				right = lrint (ceil  (G->XC.xnode[1][i]));
3152 				L.x = G->XC.x[i];	L.y = G->XC.y[i];
3153 				gmtsupport_decorated_angle (GMT, xx, yy, left, right, G->symbol_angle, nn, false, &L, G);
3154 				gmtsupport_add_decoration (GMT, S, &L, G);
3155 			}
3156 			gmt_x_free (GMT, &G->XC);
3157 		}
3158 		gmt_M_free (GMT, G->ylist);
3159 	}
3160 	else if (G->fixed) {	/* Prescribed point locations for labels that match points in input records */
3161 		double dist, min_dist;
3162 		for (j = 0; j < (uint64_t)G->f_n; j++) {	/* Loop over fixed point list */
3163 			min_dist = DBL_MAX;
3164 			for (i = 0; i < nn; i++) {	/* Loop over input line/contour */
3165 				if ((dist = hypot (xx[i] - G->f_xy[0][j], yy[i] - G->f_xy[1][j])) < min_dist) {	/* Better fit */
3166 					min_dist = dist;
3167 					start = i;
3168 				}
3169 			}
3170 			if (min_dist < G->slop) {	/* Closest point within tolerance */
3171 				L.x = xx[start];	L.y = yy[start];
3172 				gmtsupport_decorated_angle (GMT, xx, yy, start, start, G->symbol_angle, nn, false, &L, G);
3173 				gmtsupport_add_decoration (GMT, S, &L, G);
3174 			}
3175 		}
3176 	}
3177 
3178 	gmt_M_free (GMT, track_dist);
3179 	gmt_M_free (GMT, map_dist);
3180 	gmt_M_free (GMT, value_dist);
3181 }
3182 
3183 /*! . */
gmtsupport_getprevpoint(double plon,double lon[],uint64_t n,uint64_t this_p)3184 GMT_LOCAL uint64_t gmtsupport_getprevpoint (double plon, double lon[], uint64_t n, uint64_t this_p) {
3185 	/* Return the previous point that does NOT equal plon */
3186 	uint64_t ip = (this_p == 0) ? n - 2 : this_p - 1;	/* Previous point (-2 because last is a duplicate of first) */
3187 	while (doubleAlmostEqualZero (plon, lon[ip]) || doubleAlmostEqual (fabs(plon - lon[ip]), 360.0)) {	/* Same as plon */
3188 		if (ip == 0)
3189 			ip = n - 2;
3190 		else
3191 			ip--;
3192 	}
3193 	return (ip);
3194 }
3195 
3196 /*! . */
gmtsupport_same_longitude(double a,double b)3197 static inline bool gmtsupport_same_longitude (double a, double b) {
3198 	/* return true if a and b are the same longitude */
3199 	while (a < 0.0)   a += 360.0;
3200 	while (a > 360.0) a -= 360.0;
3201 	while (b < 0.0)   b += 360.0;
3202 	while (b > 360.0) b -= 360.0;
3203 	return doubleAlmostEqualZero (a, b);
3204 }
3205 
3206 #define gmt_M_same_latitude(A,B)  (doubleAlmostEqualZero (A,B))			/* A and B are the same latitude */
3207 
3208 /*! . */
gmtsupport_inonout_sphpol_count(double plon,double plat,const struct GMT_DATASEGMENT * P,unsigned int count[])3209 GMT_LOCAL int gmtsupport_inonout_sphpol_count (double plon, double plat, const struct GMT_DATASEGMENT *P, unsigned int count[]) {
3210 	/* Case of a polar cap */
3211 	uint64_t i, in, ip, prev;
3212 	int cut;
3213 	double W, E, S, N, lon, lon1, lon2, dlon, x_lat, dx1, dx2;
3214 
3215 	/* Draw meridian through P and count all the crossings with the line segments making up the polar cap S */
3216 
3217 	gmt_M_memset (count, 2, unsigned int);	/* Initialize counts to zero */
3218 	for (i = 0; i < P->n_rows - 1; i++) {	/* -1, since we know last point repeats the first */
3219 		/* Here lon1 and lon2 are the end points (in longitude) of the current line segment in S.  There are
3220 		 * four cases to worry about:
3221 		 * 1) lon equals lon1 (i.e., the meridian through lon goes right through lon1)
3222 		 * 2) lon equals lon2 (i.e., the meridian through lon goes right through lon2)
3223 		 * 3) lon lies between lon1 and lon2 and crosses the segment
3224 		 * 4) none of the above
3225 		 * Since we want to obtain either ONE or ZERO intersections per segment we will skip to next
3226 		 * point if case (2) occurs: this avoids counting a crossing twice for consecutive segments.
3227 		 */
3228 		if (gmtsupport_same_longitude (plon, P->data[GMT_X][i]) && gmt_M_same_latitude (plat, P->data[GMT_Y][i])) return (GMT_ONEDGE);	/* Point is on the perimeter */
3229 		in = i + 1;			/* Next point index */
3230 		/* Next deal with case when the longitude of P goes ~right through the second of the line nodes */
3231 		if (gmtsupport_same_longitude (plon, P->data[GMT_X][in])) continue;	/* Line goes through the 2nd node - ignore */
3232 		lon1 = P->data[GMT_X][i];	/* Copy the first of two longitudes since we may need to mess with them */
3233 		lon2 = P->data[GMT_X][in];	/* Copy the second of two longitudes since we may need to mess with them */
3234 		if (gmtsupport_same_longitude (plon, lon1)) {	/* Line goes through the 1st node */
3235 			/* Must check that the two neighboring points are on either side; otherwise it is just a tangent line */
3236 			ip = gmtsupport_getprevpoint (plon, P->data[GMT_X], P->n_rows, i);	/* Index of previous point != plon */
3237 			gmt_M_set_delta_lon (lon1, P->data[GMT_X][ip], dx1);	/* Allow for jumps across discontinuous 0 or 180 boundary */
3238 			if (dx1 == 0.0) continue;	/* Points ip and i forms a meridian, we a tangent line */
3239 			gmt_M_set_delta_lon (lon1, lon2, dx2);				/* Allow for jumps across discontinuous 0 or 180 boundary */
3240 			if (dx1*dx2 > 0.0) continue;	/* Both on same side since signs are the same */
3241 			cut = (P->data[GMT_Y][i] > plat) ? 0 : 1;	/* node is north (0) or south (1) of P */
3242 			count[cut]++;
3243 			prev = ip + 1;	/* Always exists because ip is <= n-2 */
3244 			/* If prev < i then we have a vertical segment of 2 or more points; prev points to the other end of the segment.
3245 			 * We must then check if our points plat is within that range, meaning the point lies on the segment */
3246 			if (prev < i && ((plat <= P->data[GMT_Y][prev] && plat >= P->data[GMT_Y][i]) || (plat <= P->data[GMT_Y][i] && plat >= P->data[GMT_Y][prev]))) return (GMT_ONEDGE);	/* P is on segment boundary; we are done */
3247 			continue;
3248 		}
3249 		/* OK, not exactly on a node, deal with crossing a line */
3250 		dlon = lon2 - lon1;
3251 		if (dlon > 180.0)		/* Jumped across Greenwich going westward */
3252 			lon2 -= 360.0;
3253 		else if (dlon < -180.0)		/* Jumped across Greenwich going eastward */
3254 			lon1 -= 360.0;
3255 		if (lon1 <= lon2) {	/* Segment goes W to E (or N-S) */
3256 			W = lon1;
3257 			E = lon2;
3258 		}
3259 		else {			/* Segment goes E to W */
3260 			W = lon2;
3261 			E = lon1;
3262 		}
3263 		lon = plon;			/* Local copy of plon, below adjusted given the segment lon range */
3264 		while (lon > W) lon -= 360.0;	/* Make sure we rewind way west for starters */
3265 		while (lon < W) lon += 360.0;	/* Then make sure we wind to inside the lon range or way east */
3266 		if (lon > E) continue;	/* Not crossing this segment */
3267 		if (dlon == 0.0) {	/* Special case of N-S segment: does P lie on it? */
3268 			if (P->data[GMT_Y][in] < P->data[GMT_Y][i]) {	/* Get N and S limits for segment */
3269 				S = P->data[GMT_Y][in];
3270 				N = P->data[GMT_Y][i];
3271 			}
3272 			else {
3273 				N = P->data[GMT_Y][in];
3274 				S = P->data[GMT_Y][i];
3275 			}
3276 			if (plat < S || plat > N) continue;	/* P is not on this segment */
3277 			return (1);	/* P is on segment boundary; we are done*/
3278 		}
3279 		/* Calculate latitude at intersection */
3280 		if (gmt_M_same_latitude (P->data[GMT_Y][i], P->data[GMT_Y][in])) {	/* Special cases */
3281 			if (gmt_M_same_latitude (plat, P->data[GMT_Y][in])) return (GMT_ONEDGE);	/* P is on S boundary */
3282 			x_lat = P->data[GMT_Y][i];
3283 		}
3284 		else
3285 			x_lat = P->data[GMT_Y][i] + ((P->data[GMT_Y][in] - P->data[GMT_Y][i]) / (lon2 - lon1)) * (lon - lon1);
3286 		if (doubleAlmostEqualZero (x_lat, plat))
3287 			return (1);	/* P is on S boundary */
3288 
3289 		cut = (x_lat > plat) ? 0 : 1;	/* Cut is north (0) or south (1) of P */
3290 		count[cut]++;
3291 	}
3292 
3293 	return (0);	/* This means no special cases were detected that warranted an immediate return */
3294 }
3295 
3296 /*! . */
gmtsupport_inonout_sphpol(struct GMT_CTRL * GMT,double plon,double plat,struct GMT_DATASEGMENT * S)3297 GMT_LOCAL unsigned int gmtsupport_inonout_sphpol (struct GMT_CTRL *GMT, double plon, double plat, struct GMT_DATASEGMENT *S) {
3298 /* This function is used to see if some point P = (plon, plat) is located inside, outside, or on the boundary of the
3299  * spherical polygon S read by GMT_import_table.  Note GMT->current.io.skip_duplicates must be true when the polygon
3300  * was read so there are NO duplicate (repeated) points.
3301  * Returns the following values:
3302  *	0:	P is outside of S
3303  *	1:	P is inside of S
3304  *	2:	P is on boundary of S
3305  */
3306 
3307 	/* Algorithm:
3308 	 * Case 1: The polygon S contains a geographical pole
3309 	 *	   a) if P is beyond the far latitude then P is outside
3310 	 *	   b) Draw meridian through P and count intersections:
3311 	 *		odd: P is outside; even: P is inside
3312 	 * Case 2: S does not contain a pole
3313 	 *	   a) If P is outside range of latitudes then P is outside
3314 	 *	   c) Draw meridian through P and count intersections:
3315 	 *		odd: P is inside; even: P is outside
3316 	 * In all cases, we check if P is on the outline of S
3317 	 * Limitation: We assume points are closely spaced so that we can do linear
3318 	 * approximation between successive points in the polygon.
3319 	 */
3320 
3321 	unsigned int count[2];
3322 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
3323 	gmt_M_unused(GMT);
3324 
3325 	if (SH->pole) {	/* Case 1 of an enclosed polar cap */
3326 		if (SH->pole == +1) {	/* N polar cap */
3327 			if (plat < S->min[GMT_Y]) return (GMT_OUTSIDE);	/* South of a N polar cap */
3328 			if (plat > SH->lat_limit) return (GMT_INSIDE);	/* Clearly inside of a N polar cap */
3329 		}
3330 		else if (SH->pole == -1) {	/* S polar cap */
3331 			if (plat > S->max[GMT_Y]) return (GMT_OUTSIDE);	/* North of a S polar cap */
3332 			if (plat < SH->lat_limit) return (GMT_INSIDE);	/* Clearly inside of a S polar cap */
3333 		}
3334 
3335 		/* Tally up number of intersections between polygon and meridian through P */
3336 
3337 		if (gmtsupport_inonout_sphpol_count (plon, plat, S, count)) return (GMT_ONEDGE);	/* Found P is on S */
3338 
3339 		if (SH->pole == +1 && count[0] % 2 == 0) return (GMT_INSIDE);
3340 		if (SH->pole == -1 && count[1] % 2 == 0) return (GMT_INSIDE);
3341 
3342 		return (GMT_OUTSIDE);
3343 	}
3344 
3345 	/* Here is Case 2.  First check latitude range */
3346 
3347 	if (plat < S->min[GMT_Y] || plat > S->max[GMT_Y]) return (GMT_OUTSIDE);
3348 
3349 	/* Longitudes are tricker and are tested with the tallying of intersections */
3350 
3351 	if (gmtsupport_inonout_sphpol_count (plon, plat, S, count)) return (GMT_ONEDGE);	/* Found P is on S */
3352 
3353 	if (count[0] % 2) return (GMT_INSIDE);
3354 
3355 	return (GMT_OUTSIDE);	/* Nothing triggered the tests; we are outside */
3356 }
3357 
3358 /*! . */
gmtsupport_inonout_sub(struct GMT_CTRL * GMT,double x,double y,struct GMT_DATASEGMENT * S)3359 GMT_LOCAL unsigned int gmtsupport_inonout_sub (struct GMT_CTRL *GMT, double x, double y, struct GMT_DATASEGMENT *S) {
3360 	/* Front end for both spherical and Cartesian in-on-out functions */
3361 	unsigned int side;
3362 
3363 	if (GMT->current.proj.sph_inside) {	/* Assumes these are input polygons */
3364 		struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
3365 		if (SH->pole)	/* 360-degree polar cap, must check fully */
3366 			side = gmtsupport_inonout_sphpol (GMT, x, y, S);
3367 		else {	/* See if we are outside range of longitudes for polygon */
3368 			while (x > S->min[GMT_X]) x -= 360.0;	/* Wind clear of west */
3369 			while (x < S->min[GMT_X]) x += 360.0;	/* Wind east until inside or beyond east */
3370 			if (x > S->max[GMT_X]) return (GMT_OUTSIDE);	/* Point outside, no need to assign value */
3371 			side = gmtsupport_inonout_sphpol (GMT, x, y, S);
3372 		}
3373 	}
3374 	else {	/* Flat Earth case */
3375 		if (y < S->min[GMT_Y] || y > S->max[GMT_Y])
3376 			return (GMT_OUTSIDE);	/* Point outside, no need to assign value */
3377 		if (gmt_M_x_is_lon (GMT, GMT_IN)) {	/* Deal with longitude periodicity */
3378 			if (x < S->min[GMT_X]) {
3379 				x += 360.0;
3380 				if (x > S->max[GMT_X])
3381 					return (GMT_OUTSIDE);	/* Point outside, no need to assign value */
3382 			}
3383 			else if (x > S->max[GMT_X]) {
3384 				x -= 360.0;
3385 				if (x < S->min[GMT_X])
3386 					return (GMT_OUTSIDE);	/* Point outside, no need to assign value */
3387 			}
3388 		}
3389 		else if (x < S->min[GMT_X] || x > S->max[GMT_X])
3390 			return (GMT_OUTSIDE);	/* Point outside, no need to assign value */
3391 		/* It is essential that the longitudes in S are within the min/max limits since this is a Cartesian algorithm */
3392 		side = gmt_non_zero_winding (GMT, x, y, S->data[GMT_X], S->data[GMT_Y], S->n_rows);
3393 	}
3394 	return (side);
3395 }
3396 
gmt_signum(double x)3397 int gmt_signum (double x) {
3398 	/* The standard sign function */
3399 	if (x < 0.0) return -1;
3400 	if (x > 0.0) return +1;
3401 	return 0;
3402 }
3403 
3404 #ifdef TRIANGLE_D
3405 
3406 /*
3407  * New gmt_delaunay interface routine that calls the triangulate function
3408  * developed by Jonathan Richard Shewchuk, University of California at Berkeley.
3409  * Suggested by alert GMT user Alain Coat.  You need to get triangle.c and
3410  * triangle.h from www.cs.cmu.edu/~quake/triangle.html
3411  */
3412 
3413 #define REAL double
3414 #include "triangle.h"
3415 
3416 /* Leave link as int**, not uint64_t** */
3417 /*! . */
gmtsupport_delaunay_shewchuk(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n,int ** link)3418 GMT_LOCAL uint64_t gmtsupport_delaunay_shewchuk (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n, int **link) {
3419 	/* GMT interface to the triangle package; see above for references.
3420 	 * All that is done is reformatting of parameters and calling the
3421 	 * main triangulate routine.  Thanx to Alain Coat for the tip.
3422 	 */
3423 
3424 	uint64_t i, j;
3425 	struct triangulateio In, Out, vorOut;
3426 
3427 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Delaunay triangulation calculated by Jonathan Shewchuk's Triangle [http://www.cs.cmu.edu/~quake/triangle.html]\n");
3428 
3429 	/* Set everything to 0 and NULL */
3430 
3431 	gmt_M_memset (&In,     1, struct triangulateio);
3432 	gmt_M_memset (&Out,    1, struct triangulateio);
3433 	gmt_M_memset (&vorOut, 1, struct triangulateio);
3434 
3435 	/* Allocate memory for input points */
3436 
3437 	In.numberofpoints = (int)n;
3438 	In.pointlist = gmt_M_memory (GMT, NULL, 2 * n, double);
3439 
3440 	/* Copy x,y points to In structure array */
3441 
3442 	for (i = j = 0; i < n; i++) {
3443 		In.pointlist[j++] = x_in[i];
3444 		In.pointlist[j++] = y_in[i];
3445 	}
3446 
3447 	/* Call Jonathan Shewchuk's triangulate algorithm.  This is 64-bit safe since
3448 	 * all the structures use 4-byte ints (longs are used internally). The options are
3449 	 *  z : Start numbering at zero instead of 1.
3450 	 *	I : No iteration numbers
3451 	 *  Q : Quiet - suppress all explanations
3452 	 *  B : Suppresses output of boundary information
3453 	 *  D : Conforming Delaunay:  all triangles are truly Delaunay.
3454 	 */
3455 
3456 	triangulate ("zIQBD", &In, &Out, &vorOut);
3457 
3458 	*link = Out.trianglelist;	/* List of node numbers to return via link [NOT ALLOCATED BY gmt_M_memory] */
3459 
3460 	gmt_M_str_free (Out.pointlist);
3461 	gmt_M_free (GMT, In.pointlist);
3462 
3463 	return (Out.numberoftriangles);
3464 }
3465 
3466 /*! . */
gmtsupport_voronoi_shewchuk(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n64,double * wesn,unsigned int mode)3467 GMT_LOCAL struct GMT_DATASET * gmtsupport_voronoi_shewchuk (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n64, double *wesn, unsigned int mode) {
3468 	/* GMT interface to the triangle package; see above for references.
3469 	 * All that is done is reformatting of parameters and calling the
3470 	 * main triangulate routine.  Here we return Voronoi information
3471 	 * and package the coordinates of the edges in the output dataset.
3472 	 * The wesn[] array contains the min/max x (or lon) and y (or lat) coordinates.
3473 	 * Since the internal variables in triangle are ints (e.g., edgelist) we use
3474 	 * ints here as well
3475 	 */
3476 
3477 	/* Currently we only write the edges of a Voronoi cell but we want polygons later.
3478 	 * Info from triangle re Voronoi polygons: "Triangle does not write a list of
3479 	 * the edges adjoining each Voronoi cell, but you can reconstructed it straightforwardly.
3480 	 * For instance, to find all the edges of Voronoi cell 1, search the output .edge file
3481 	 * for every edge that has input vertex 1 as an endpoint.  The corresponding dual
3482 	 * edges in the output .v.edge file form the boundary of Voronoi cell 1." */
3483 
3484 	uint64_t dim[GMT_DIM_SIZE] = {1, 0, 2, 2};
3485 	int i, j, k, n, km1, j2, i2, seg, n_int_edges, n_edges, first_edge = 0, n_extra = 0;
3486 	int n_to_clip = 0, n_int_vertex = 0, p = 0, corners = 0, n_vertex, change, n_edges_2;
3487 	unsigned int geometry, side, corner;
3488 	char header[GMT_LEN64] = {""};
3489 	unsigned char *point_type = NULL;
3490 	struct triangulateio In, Out, vorOut;
3491 	struct GMT_DATASET *P = NULL;
3492 	struct GMT_DATASEGMENT *S = NULL;
3493 	double dy, new_x, xe, ye, xp, yp, x0, y0;
3494 
3495 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Voronoi partitioning calculated by Jonathan Shewchuk's Triangle [http://www.cs.cmu.edu/~quake/triangle.html]\n");
3496 
3497 	/* Set everything to 0 and NULL */
3498 
3499 	gmt_M_memset (&In,     1, struct triangulateio);
3500 	gmt_M_memset (&Out,    1, struct triangulateio);
3501 	gmt_M_memset (&vorOut, 1, struct triangulateio);
3502 
3503 	/* Allocate memory for input points */
3504 
3505 	In.numberofpoints = n = (int)n64;
3506 	In.pointlist = gmt_M_memory (GMT, NULL, 2 * n, double);
3507 
3508 	/* Copy x,y points to In structure array */
3509 
3510 	for (i = j = 0; i < n; i++) {
3511 		In.pointlist[j++] = x_in[i];
3512 		In.pointlist[j++] = y_in[i];
3513 	}
3514 
3515 	/* Call Jonathan Shewchuk's triangulate algorithm.  This is 64-bit safe since
3516 	 * all the structures use 4-byte ints (longs are used internally). The options are
3517 	 *  z : Start numbering at zero instead of 1.
3518 	 *	I : No iteration numbers
3519 	 *  Q : Quiet - suppress all explanations
3520 	 *  B : Suppresses output of boundary information
3521 	 *  v : Get Voronoi vertices.
3522 	 *  D : Conforming Delaunay:  all triangles are truly Delaunay.
3523 	 *  j : jettison unused vertices from list.
3524 	 */
3525 
3526 	triangulate ("zIQBvDj", &In, &Out, &vorOut);
3527 
3528 	/* Determine output size for all edges */
3529 
3530 	n_int_edges = vorOut.numberofedges;
3531 	/* Count Voronoi vertices and number of infinite rays */
3532 	for (i = 0, k = 0; i < n_int_edges; i++, k += 2) {
3533 		if (vorOut.edgelist[k+1] == -1) n_extra++;	/* Infinite rays */
3534 		if (vorOut.edgelist[k] > n_int_vertex) n_int_vertex = vorOut.edgelist[k];
3535 		if (vorOut.edgelist[k+1] > n_int_vertex) n_int_vertex = vorOut.edgelist[k+1];
3536 	}
3537 	/* Count Voronoi vertices outside w/e/s/n region */
3538 	for (i = k = 0; i < n_int_vertex; i++, k += 2)
3539 		if (vorOut.pointlist[k] < wesn[XLO] || vorOut.pointlist[k] > wesn[XHI] || vorOut.pointlist[k+1] < wesn[YLO] || vorOut.pointlist[k+1] > wesn[YHI])
3540 			n_to_clip++;
3541 
3542 #ifdef DEBUG
3543 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Output from triangulate:\n");
3544 	for (i = k = 0; i < n_int_edges; i++, k += 2)
3545 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Edge %8" PRIu64 " Point %8d to %8d normlist = %8g\t%8g\n", i, vorOut.edgelist[k], vorOut.edgelist[k+1], vorOut.normlist[k], vorOut.normlist[k+1]);
3546 	for (i = k = 0; i < n_int_vertex; i++, k += 2)
3547 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Point %8" PRIu64 " at %g\t%g\n", i, vorOut.pointlist[k], vorOut.pointlist[k+1]);
3548 #endif
3549 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "n_vertex = %d n_edge = %d n_inf_rays = %d n_outside = %d\n", n_int_vertex, n_int_edges, n_extra, n_to_clip);
3550 
3551 	n_int_vertex++;				/* The next edge number */
3552 	p = 2 * n_int_vertex;		/* Index into vorOut.pointlist for next point to be added along boundary */
3553 	n_vertex = n_int_vertex;	/* Number of all vertices so far */
3554 
3555 	if (mode) {	/* Want closed Voronoi polygons, so must allocate more space to hold boundary rays and corners */
3556 		corners = 4;		/* Need to add this many boundingbox corner coordinates */
3557 		dim[GMT_SEG] = n;	/* When all Voronoi polygons are closed there will be one surrounding each input data point */
3558 		dim[GMT_ROW] = 0;	/* Variable row length so cannot specify it here - allocate later */
3559 		geometry = GMT_IS_POLY;	/* Since we will be making closed polygons */
3560 		/* Allocate array for point type.  This holds which side we exited.  Normally 0-3, here we add 1 to use 1-4
3561 		 * instead since here, 0 means interior point.  1 is south and then we go CCW to 2 (east), 3 (north) and 4 (west) */
3562 		point_type = gmt_M_memory (GMT, NULL, n_int_edges + n_extra + corners + n_to_clip, char);
3563 	}
3564 	else {	/* Want line edges only, all dimensions are already known */
3565 		dim[GMT_SEG] = n_int_edges;	/* All dimensions are known since we issue just 1 line per segment */
3566 		geometry = GMT_IS_LINE;	/* Since we only report edges */
3567 	}
3568 	/* Create dataset with a single table with one segment per edge or polygon */
3569 	if ((P = GMT_Create_Data (GMT->parent, GMT_IS_DATASET, geometry, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) {
3570 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to create a data set for gmtsupport_voronoi_shewchuk\n");
3571 		gmt_M_free (GMT, point_type);
3572 		GMT->parent->error = GMT_RUNTIME_ERROR;
3573 		return NULL;
3574 	}
3575 
3576 	/* Reallocate the triangle arrays to hold the extra vertices we will need to add */
3577 	vorOut.pointlist = realloc (vorOut.pointlist, 2 * (n_vertex + n_extra + corners + 2*n_to_clip) * sizeof (double));
3578 	vorOut.edgelist  = realloc (vorOut.edgelist,  2 * (n_int_edges  + n_extra + corners + n_to_clip) * sizeof (int));
3579 
3580 	/* First replace infinite rays with finite points where they intersect the border (i.e., we clip the rays to w/e/s/n) */
3581 
3582 	for (i = k = 0; i < n_int_edges; i++, k++) {
3583 		km1 = k++;	/* Index to this edgelist's first point */
3584 		j2 = 2 * vorOut.edgelist[km1];	/* Index into pointlist with x-coordinate of this edge point */
3585 		/* Get the coordinates of the point P; this could be an interior [keep] or exterior Voronoi vertex [to be skipped] */
3586 		xp = vorOut.pointlist[j2++];
3587 		yp = vorOut.pointlist[j2];
3588 		if (vorOut.edgelist[k] == -1) {	/* Infinite ray; calculate intersection with region boundary */
3589 			/* Each edgelist always has a Voronoi vertex as the first point so j2 is never 2 * (-1) */
3590 			if (xp < wesn[XLO] || xp > wesn[XHI] || yp < wesn[YLO] || yp > wesn[YHI]) {	/* Infinite ray outside boundary - skip edge */
3591 				vorOut.edgelist[km1] = -1;	/* Mark as a skipped edge */
3592 				continue;
3593 			}
3594 			/* Determine (xe, ye), the intersection of the ray and the bounding box */
3595 			if (vorOut.normlist[km1] < 0.0) {	/* Ray will intersect x = xmin, called side = 4 */
3596 				xe = wesn[XLO];	side = 4;
3597 			}
3598 			else {	/* Ray will intersect x = xmax, called side = 2 */
3599 				xe = wesn[XHI];	side = 2;
3600 			}
3601 			/* Compute y-value at the intersection or ray and border */
3602 			dy = fabs ((vorOut.normlist[k] / vorOut.normlist[km1]) * (xe - xp));
3603 			ye = yp + dy * copysign (1.0, vorOut.normlist[k]);
3604 			if (ye < wesn[YLO]) {	/* Recompute the x-crossing along y = ymin instead and set ye to ymin */
3605 				side = 1;	/* South */
3606 				new_x = xp + (wesn[YLO] - yp) * (xe - xp) / (ye - yp);
3607 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Voronoi infinite ray truncated from %g %g to %g %g\n", xe, ye, new_x, wesn[YLO]);
3608 				xe = new_x;	ye = wesn[YLO];
3609 			}
3610 			else if (ye > wesn[YHI]) {	/* Recompute the x-crossing along y = ymax instead  and set ye to ymax */
3611 				side = 3;	/* North */
3612 				new_x = xp + (wesn[YHI] - yp) * (xe - xp) / (ye - yp);
3613 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Voronoi infinite ray truncated from %g %g to %g %g\n", xe, ye, new_x, wesn[YHI]);
3614 				xe = new_x;	ye = wesn[YHI];
3615 			}
3616 			/* Update the truncated ray (-1) in the edge list with a new vertex and add the vertex coordinates to pointlist */
3617 			if (mode) point_type[n_vertex] = (unsigned char)side;	/* Mark as a border point 1-4 */
3618 			vorOut.edgelist[k] = n_vertex++;		/* Replace the -1 with the actual point on the boundary */
3619 			vorOut.pointlist[p++] = xe;				/* Add the ray intersection point to the pointlist */
3620 			vorOut.pointlist[p++] = ye;
3621 		}
3622 		else {	/* A regular edge specified by two points */
3623 			i2 = 2 * vorOut.edgelist[k];	/* 2nd index into pointlist with x-coordinate */
3624 			/* Get the coordinates of the second Voronoi vertex */
3625 			x0 = vorOut.pointlist[i2++];
3626 			y0 = vorOut.pointlist[i2];
3627 			/* Must check if one of the two points (xp,yp) and (x0,y0) lies outside the region; if so compute intersection with side */
3628 			if (xp < wesn[XLO]) {	/* Crossing at xmin */
3629 				xe = wesn[XLO];
3630 				ye = y0 - (y0 - yp) * (x0 - xe) / (x0 - xp);
3631 				change = k - 1;	side = 4;
3632 			}
3633 			else if (xp > wesn[XHI]) {	/* Crossing at xmax */
3634 				xe = wesn[XHI];
3635 				ye = y0 - (y0 - yp) * (x0 - xe) / (x0 - xp);
3636 				change = k - 1;	side = 2;
3637 			}
3638 			else if (yp < wesn[YLO]) {	/* Crossing at ymin */
3639 				ye = wesn[YLO];
3640 				xe = x0 - (x0 - xp) * (y0 - ye) / (y0 - yp);
3641 				change = k - 1;	side = 1;
3642 			}
3643 			else if (yp > wesn[YHI]) {	/* Crossing at ymax */
3644 				ye = wesn[YHI];
3645 				xe = x0 - (x0 - xp) * (y0 - ye) / (y0 - yp);
3646 				change = k - 1;	side = 3;
3647 			}
3648 			else if (x0 < wesn[XLO]) {	/* Crossing at xmin */
3649 				xe = wesn[XLO];
3650 				ye = yp - (yp - y0) * (xp - xe) / (xp - x0);
3651 				change = k;	side = 4;
3652 			}
3653 			else if (x0 > wesn[XHI]) {	/* Crossing at xmax */
3654 				xe = wesn[XHI];
3655 				ye = yp - (yp - y0) * (xp - xe) / (xp - x0);
3656 				change = k;	side = 2;
3657 			}
3658 			else if (y0 < wesn[YLO]) {	/* Crossing at ymin */
3659 				ye = wesn[YLO];
3660 				xe = xp - (xp - x0) * (yp - ye) / (yp - y0);
3661 				change = k;	side = 1;
3662 			}
3663 			else if (y0 > wesn[YHI]) {	/* Crossing at ymax */
3664 				ye = wesn[YHI];
3665 				xe = xp - (xp - x0) * (yp - ye) / (yp - y0);
3666 				change = k;	side = 3;
3667 			}
3668 			else	/* Normal edge - nothing to do for now */
3669 				continue;
3670 			/* Here we replace the edge vertex point with the intersection point and add that point as a new point */
3671 			if (mode) point_type[n_vertex] = (unsigned char)side;	/* Mark new point as a border point 1-4 */
3672 			vorOut.edgelist[change] = n_vertex++;	/* Update edgelist with new point on the border, then increase point count */
3673 			vorOut.pointlist[p++] = xe;		/* Place the new coordinates into the pointlist array */
3674 			vorOut.pointlist[p++] = ye;
3675 		}
3676 	}
3677 
3678 	/* Now edgelist only contains actual point IDs and pointlist has been updated to hold all added border points */
3679 
3680 #ifdef DEBUG
3681 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "After infinite ray and exterior vertex crossing replacements:\n");
3682 		for (i = k = 0; i < n_int_edges; i++, k += 2)
3683 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Edge %8" PRIu64 " Point %8d to %8d normlist = %8g\t%8g\n", i, vorOut.edgelist[k], vorOut.edgelist[k+1], vorOut.normlist[k], vorOut.normlist[k+1]);
3684 		for (i = k = 0; i < n_vertex; i++, k += 2)
3685 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Point %8" PRIu64 " at %g\t%g\n", i, vorOut.pointlist[k], vorOut.pointlist[k+1]);
3686 #endif
3687 
3688 	if (mode) {	/* Need to make closed polygons from edges */
3689 		bool first_turn, go_i, go_j;
3690 		signed char *edge_use = NULL;
3691 		int expected_sign = 0, prev_sign, next_sign, *start_vertex = NULL, *stop_vertex = NULL;
3692 		int64_t edge, pstart, pstop, start_point, next_point, prev_point, np;
3693 		double xstart, xstop, xend, ystart, ystop, yend, cross_product;
3694 		double *xcoord = NULL, *ycoord = NULL, *dx = NULL, *dy = NULL, *x_pos = NULL, *y_pos = NULL;
3695 		/* Add the 4 border corners to the point list as well and mark them as corners with special side = 5-8 (i.e., 4 + side 1-4).
3696 		   We need different sides for corners since xmax,ymax is the end for two separate axes. */
3697 		vorOut.pointlist[p++] = wesn[XHI];	vorOut.pointlist[p++] = wesn[YLO];	point_type[n_vertex++] = 5;
3698 		vorOut.pointlist[p++] = wesn[XHI];	vorOut.pointlist[p++] = wesn[YHI];	point_type[n_vertex++] = 6;
3699 		vorOut.pointlist[p++] = wesn[XLO];	vorOut.pointlist[p++] = wesn[YHI];	point_type[n_vertex++] = 7;
3700 		vorOut.pointlist[p++] = wesn[XLO];	vorOut.pointlist[p++] = wesn[YLO];	point_type[n_vertex++] = 8;
3701 
3702 #ifdef DEBUG
3703 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Before border edges are added:\n");
3704 		for (i = k = 0; i < n_int_edges; i++, k += 2)
3705 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Edge %8" PRIu64 " Point %8d to %8d normlist = %8g\t%8g\n", i, vorOut.edgelist[k], vorOut.edgelist[k+1], vorOut.normlist[k], vorOut.normlist[k+1]);
3706 		for (i = k = 0; i < n_vertex; i++, k += 2)
3707 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Point %8" PRIu64 " [%d] at %g\t%g\n", i, point_type[i], vorOut.pointlist[k], vorOut.pointlist[k+1]);
3708 #endif
3709 		/* Finally add the new edges between new points along the border and with the corners */
3710 		/* Do the y = ymin and y = ymax sides first (i.e., side 1 and 3) */
3711 		edge = 2 * n_int_edges;	/* Start index of next added edge in edgelist */
3712 		for (side = 1; side <= 3; side += 2) {	/* Since almost the same logic for the two sides */
3713 			corner = (side == 1) ? 5 : 6;	/* The corner at the end of this x-axis */
3714 			xstart = wesn[XLO];	pstart = (side == 1) ? n_vertex - 1 : n_vertex - 2;	/* Initialize the start of the edge at beginning of x-axis */
3715 			xstop  = xend  = wesn[XHI];	pstop  = (side == 1) ? n_vertex - 4 : n_vertex - 3;	/* End of x-axis is end of edge unless there are intersections */
3716 			while (xstart < wesn[XHI]) {	/* As long as we have incomplete edges */
3717 				for (i = n_int_vertex; i < n_vertex; i++) {	/* Must check all of them for one that could be a candidate along this x-axis */
3718 					if (!(point_type[i] == side || point_type[i] == corner)) continue;	/* Not along the x-axis including the end corner */
3719 					j2 = 2 * i;	/* Index into pointlist for the x-coordinate */
3720 					if (vorOut.pointlist[j2] <= xstop && vorOut.pointlist[j2] > xstart) {	/* An intersection closer to the start of our edge, update xstop */
3721 						xstop = vorOut.pointlist[j2];
3722 						pstop = i;
3723 					}
3724 				}
3725 				/* Here, xstop is the point closest to xstart and pstop is its index. */
3726 				/* Add this edge to edgelist */
3727 				vorOut.edgelist[edge++] = (int)pstart;	vorOut.edgelist[edge++] = (int)pstop;
3728 				pstart = pstop;	xstart = xstop;	xstop = xend;	/* Let the end of this edge become start of the next edge */
3729 			}
3730 		}
3731 		/* Do the xmin and xmax sides next (sides 2 and 4) */
3732 		for (side = 2; side <= 4; side += 2) {
3733 			corner = (side == 2) ? 6 : 7;	/* The corner at the end of this y-axis */
3734 			ystart = wesn[YLO];	pstart = (side == 2) ? n_vertex - 4 : n_vertex - 1;	/* Initialize the start of the edge at beginning of y-axis */
3735 			ystop  = yend = wesn[YHI];	pstop  = (side == 2) ? n_vertex - 3 : n_vertex - 2;	/* End of y-axis is end of edge unless there are intersections */
3736 			while (ystart < wesn[YHI]) {	/* As long as we have incomplete edges */
3737 				for (i = n_int_vertex; i < n_vertex; i++) {	/* Must check all of them for one that could be a candidate along this y-axis */
3738 					if (!(point_type[i] == side || point_type[i] == corner)) continue;	/* Not along the y-axis including the end corner */
3739 					j2 = 2 * i + 1;	/* Index into pointlist for the y-coordinate */
3740 					if (vorOut.pointlist[j2] <= ystop && vorOut.pointlist[j2] > ystart) {	/* An intersection closer to the start of our edge, update ystop */
3741 						ystop = vorOut.pointlist[j2];
3742 						pstop = i;
3743 					}
3744 				}
3745 				/* Here, ystop is the point closest to xstart and pstop is its index. */
3746 				/* Add this edge to edgelist */
3747 				vorOut.edgelist[edge++] = (int)pstart;			vorOut.edgelist[edge++] = (int)pstop;
3748 				pstart = pstop;	ystart = ystop;	ystop = yend;	/* Let the end of this edge become start of the next edge */
3749 			}
3750 		}
3751 		n_edges = (int)edge;	/* Total number of all edges times 2 */
3752 #ifdef DEBUG
3753 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "\nAfter border edges are added:\n");
3754 		for (i = k = 0; k < n_edges; i++, k += 2) {
3755 			if (i < n_int_edges)
3756 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Edge %8" PRIu64 " Point %8d to %8d normlist = %8g\t%8g\n", i, vorOut.edgelist[k], vorOut.edgelist[k+1], vorOut.normlist[k], vorOut.normlist[k+1]);
3757 			else
3758 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Edge %8" PRIu64 " Point %8d to %8d\n", i, vorOut.edgelist[k], vorOut.edgelist[k+1]);
3759 		}
3760 		for (i = k = 0; i < n_vertex; i++, k += 2)
3761 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Point %8" PRIu64 " [%d] at %g\t%g\n", i, point_type[i], vorOut.pointlist[k], vorOut.pointlist[k+1]);
3762 #endif
3763 		gmt_M_free (GMT, point_type);
3764 
3765 		vorOut.normlist = realloc (vorOut.normlist, n_edges * sizeof (double));
3766 		/* Remove the unneeded external edges flagged by two -1 signs in the edgelist */
3767 		for (j2 = i2 = 0; j2 < n_edges; j2 += 2) {	/* For all the edges */
3768 			if (i2 < j2) {	/* Shuffle the entries to lower indices */
3769 				vorOut.edgelist[i2] = vorOut.edgelist[j2];
3770 				vorOut.edgelist[i2+1] = vorOut.edgelist[j2+1];
3771 				vorOut.normlist[i2] = vorOut.normlist[j2];
3772 				vorOut.normlist[i2+1] = vorOut.normlist[j2+1];
3773 			}
3774 			if (!(vorOut.edgelist[j2] == -1 && vorOut.edgelist[j2+1] == -1)) i2 += 2;	/* Only increment output index when not a dummy edge */
3775 		}
3776 		/* Update the count of edges.  All the unneeded edges will lie within the original n_int_edges section */
3777 		n_edges -= (j2 - i2);
3778 		n_int_edges -= ((j2 - i2)/2);
3779 #ifdef DEBUG
3780 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "\nAfter removing unused edges:\n");
3781 		for (i = k = 0; k < n_edges; i++, k += 2) {
3782 			if (i < n_int_edges)
3783 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Edge %8" PRIu64 " Point %8d to %8d normlist = %8g\t%8g\n", i, vorOut.edgelist[k], vorOut.edgelist[k+1], vorOut.normlist[k], vorOut.normlist[k+1]);
3784 			else
3785 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Edge %8" PRIu64 " Point %8d to %8d\n", i, vorOut.edgelist[k], vorOut.edgelist[k+1]);
3786 		}
3787 #endif
3788 
3789 		/* Precalculate dx, dy for each edge and store these in normlist (which we need to reallocate first) */
3790 		n_edges_2 = n_edges / 2;
3791 		edge_use = gmt_M_memory (GMT, NULL, n_edges_2, signed char);	/* 0 = unused, +/-1 = used once in normal or reverse direction, 2 = used twice and done with */
3792 		dx = vorOut.normlist;	dy = &vorOut.normlist[1];	/* So we can use dx[index] and dy[index] */
3793 		start_vertex = vorOut.edgelist;	stop_vertex = &vorOut.edgelist[1];
3794 		x_pos = vorOut.pointlist;	y_pos = &vorOut.pointlist[1];
3795 		for (i = k = 0; k < n_edges; i++, k += 2) {	/* For all the edges */
3796 			i2 = 2 * start_vertex[k];	/* Location of x and y coordinates for start of edge */
3797 			j2 = 2 * stop_vertex[k];	/* Location of x and y coordinates for end of edge */
3798 			dx[k] = x_pos[j2] - x_pos[i2];	/* dx = end_x - start_x */
3799 			dy[k] = y_pos[j2] - y_pos[i2];	/* dy = end_y - start_y */
3800 			if (i >= n_int_edges) edge_use[i] = 1;	/* These edges will only be used once (sign is not important here) */
3801 		}
3802 		/* Need temp array to hold the coordinates of a single polygon [I hope this is large enough - don't know yet] */
3803 		xcoord = gmt_M_memory (GMT, NULL, n_int_edges/2, double);
3804 		ycoord = gmt_M_memory (GMT, NULL, n_int_edges/2, double);
3805 
3806 		/* Now stitch together the closed polygons */
3807 		seg = 0;
3808 		first_turn = true;	/* When we find the first 3 points in a polygon we can compute the cross-product and determine sign for all polygons */
3809 		while (seg < n) {
3810 			/* Find the first interior edge that has not already been used twice */
3811 			for (i = first_edge, go_i = true; go_i && i < n_int_edges; i++) {
3812 				if (edge_use[i] == 2) {	/* Done with this edge */
3813 					first_edge++;	/* Might as well skip this one from now on */
3814 					continue;
3815 				}
3816 				/* Here, i2 = 2*i is the index into the edgelist and normlist arrays for the previous edge */
3817 				i2 = 2 * i;
3818 				if (edge_use[i] == +1) {	/* Must reverse direction the 2nd time we start with this edge */
3819 					start_point = prev_point = stop_vertex[i2];
3820 					next_point = start_vertex[i2];
3821 					prev_sign = -1;		/* Since we are reversing this edge */
3822 					edge_use[i] = 2;	/* Flag we are finished with this edge */
3823 				}
3824 				else if (edge_use[i] == -1) {	/* Go in the forward direction the 2nd time we start with this edge */
3825 					start_point = prev_point = start_vertex[i2];
3826 					next_point = stop_vertex[i2];
3827 					prev_sign = +1;	/* Since we are NOT reversing this edge */
3828 					edge_use[i] = 2;	/* Flag we are finished with this edge */
3829 				}
3830 				else {	/* Go in the forward direction the first time */
3831 					start_point = prev_point = start_vertex[i2];
3832 					next_point = stop_vertex[i2];
3833 					prev_sign = +1;	/* Since we are NOT reversing this edge */
3834 					edge_use[i] = 1;	/* Flag we have used this edge one more time */
3835 				}
3836 				/* Add this edge as the first line in the new polygon */
3837 				xcoord[0] = x_pos[2*start_point];	ycoord[0] = y_pos[2*start_point];
3838 				xcoord[1] = x_pos[2*next_point];	ycoord[1] = y_pos[2*next_point];
3839 				np = 2;	/* Total points in this polygon so far */
3840 				while (next_point != start_point) {	/* When this is false we have automatically closed the polygon */
3841 					/* Find first edge of all edges not equal the current edge that has next_point either as 1st or 2nd vertex and the other vertex is not our prev_point */
3842 					for (j = first_edge, go_j = true; go_j && j < n_edges_2; j++) {
3843 						if (j == i || edge_use[j] == 2) continue;	/* Either the same edge or we are already done with this edge */
3844 						/* Here, j2 = 2*j is the index into the edgelist and normlist arrays for the next edge */
3845 						j2 = 2 * j;
3846 						if (start_vertex[j2] == next_point && stop_vertex[j2] != prev_point)	/* Must go in forward order */
3847 							next_sign = 1;
3848 						else if (stop_vertex[j2] == next_point && start_vertex[j2] != prev_point)	/* Go in reverse order */
3849 							next_sign = -1;
3850 						else	/* Not a desired edge since we could not match the vertex number */
3851 							continue;
3852 
3853 						/* Here we found an edge candidate, but we don't know if it leads to a convex angle */
3854 						cross_product = dx[i2] * dy[j2] - dx[j2] * dy[i2];	/* This should be negative for right turns */
3855 						if (first_turn) {	/* First pair of edges defines the desired sign of all the subsequent cross-products */
3856 							expected_sign = prev_sign * next_sign * gmt_signum (cross_product);	/* sign may flip if any edge was reversed */
3857 							first_turn = false;
3858 						}
3859 						else if ((prev_sign * next_sign * gmt_signum (cross_product)) != expected_sign)	/* Not making a convex polygon */
3860 							continue;
3861 
3862 						/* Here we are going in a convex direction, so we add this new edge to the polygon */
3863 						prev_point = next_point;
3864 						next_point = (next_sign == 1) ? stop_vertex[j2] : start_vertex[j2];
3865 						prev_sign = next_sign;
3866 						i2 = j2;
3867 						/* Add next_point to our polygon */
3868 						xcoord[np] = x_pos[2*next_point];
3869 						ycoord[np] = y_pos[2*next_point];
3870 						if (edge_use[j])	/* Now used twice */
3871 							edge_use[j] = 2;
3872 						else	/* Flag according to which direction we used it */
3873 							edge_use[j] = (char)next_sign;
3874 						np++;	/* Increase polygon length counter */
3875 						go_j = false;	/* Exit this loop and search for next edge */
3876 					}
3877 				}
3878 				/* Finalize the polygon */
3879 				snprintf (header, GMT_LEN64, "Voronoi polygon # %d -L%d", seg, seg);
3880 				S = P->table[0]->segment[seg] = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, np, 2U, header, P->table[0]->segment[seg]);
3881 				gmt_M_memcpy (S->data[GMT_X], xcoord, np, double);
3882 				gmt_M_memcpy (S->data[GMT_Y], ycoord, np, double);
3883 				P->table[0]->n_records += np;	/* Update counts */
3884 				P->n_records += np;
3885 				seg++;
3886 				go_i = false;	/* Exit loop and move to the next polygon */
3887 			}
3888 		}
3889 		/* Free temporary arrays */
3890 		gmt_M_free (GMT, xcoord);
3891 		gmt_M_free (GMT, ycoord);
3892 		gmt_M_free (GMT, edge_use);
3893 	}
3894 	else {	/* Only want line edges */
3895 		for (seg = k = 0; seg < n_int_edges; seg++) {
3896 			j2 = 2 * vorOut.edgelist[k++];
3897 			S = P->table[0]->segment[seg];	/* Current output edge segment */
3898 			S->data[GMT_X][0] = vorOut.pointlist[j2++];
3899 			S->data[GMT_Y][0] = vorOut.pointlist[j2];
3900 			j2 = 2 * vorOut.edgelist[k++];
3901 			S->data[GMT_X][1] = vorOut.pointlist[j2++];
3902 			S->data[GMT_Y][1] = vorOut.pointlist[j2];
3903 			snprintf (header, GMT_LEN64, "Voronoi edge # %d -L%d", seg, seg);
3904 			S->header = strdup (header);
3905 		}
3906 	}
3907 
3908 	gmt_set_dataset_minmax (GMT, P);	/* Determine min/max for each column */
3909 
3910 	/* Free the triangulate arrays that were all allocated internally */
3911 	gmt_M_str_free (Out.pointlist);
3912 	gmt_M_str_free (Out.trianglelist);
3913 	gmt_M_str_free (vorOut.pointattributelist);
3914 	gmt_M_str_free (vorOut.pointlist);
3915 	gmt_M_str_free (vorOut.edgelist);
3916 	gmt_M_str_free (vorOut.normlist);
3917 	gmt_M_free (GMT, In.pointlist);
3918 
3919 	return (P);
3920 }
3921 #else
3922 /*! Dummy functions since not installed */
gmtsupport_delaunay_shewchuk(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n,int ** link)3923 GMT_LOCAL uint64_t gmtsupport_delaunay_shewchuk (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n, int **link) {
3924 	gmt_M_unused (x_in);
3925 	gmt_M_unused (y_in);
3926 	gmt_M_unused (n);
3927 	gmt_M_unused (link);
3928 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "unavailable: Shewchuk's triangle option was not selected during GMT installation\n");
3929 	return (0);
3930 }
3931 
3932 /*! . */
gmtsupport_voronoi_shewchuk(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n,double * wesn,unsigned int mode)3933 GMT_LOCAL struct GMT_DATASET * gmtsupport_voronoi_shewchuk (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n, double *wesn, unsigned int mode) {
3934 	gmt_M_unused (x_in);
3935 	gmt_M_unused (y_in);
3936 	gmt_M_unused (n);
3937 	gmt_M_unused (wesn);
3938 	gmt_M_unused (mode);
3939 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "unavailable: Shewchuk's triangle option was not selected during GMT installation\n");
3940 	return (NULL);
3941 }
3942 #endif
3943 
3944 /*
3945  * gmt_delaunay performs a Delaunay triangulation on the input data
3946  * and returns a list of indices of the points for each triangle
3947  * found.  Algorithm translated from
3948  * Watson, D. F., ACORD: Automatic contouring of raw data,
3949  *   Computers & Geosciences, 8, 97-101, 1982.
3950  */
3951 
3952 struct GMT_PAIR {
3953 	double x, y;
3954 	uint64_t rec;
3955 };
3956 
3957 /*! . */
gmtsupport_sort_pair(const void * p_1,const void * p_2)3958 GMT_LOCAL int gmtsupport_sort_pair (const void *p_1, const void *p_2) {
3959 	const struct GMT_PAIR *point_1 = (const struct GMT_PAIR *)p_1, *point_2 = (const struct GMT_PAIR *)p_2;
3960 
3961 	if (point_1->x < point_2->x) return -1;
3962 	if (point_1->x > point_2->x) return +1;
3963 	if (point_1->y < point_2->y) return -1;
3964 	if (point_1->y > point_2->y) return +1;
3965 	return 0;
3966 }
3967 
3968 /* Leave link as int**, not int** */
3969 /*! . */
gmtsupport_delaunay_watson(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n,int ** link)3970 GMT_LOCAL uint64_t gmtsupport_delaunay_watson (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n, int **link) {
3971 	/* Input point x coordinates */
3972 	/* Input point y coordinates */
3973 	/* Number of input points */
3974 	/* pointer to List of point ids per triangle.  Vertices for triangle no i
3975 	   is in link[i*3], link[i*3+1], link[i*3+2] */
3976 
3977 	int *index = NULL;	/* Must be int not uint64_t */
3978 	int ix[3], iy[3];
3979 	bool done;
3980 	uint64_t i, j, nuc, ij, jt, km, id, isp, l1, l2, k, k1, jz, i2, kmt, kt, size;
3981 	int64_t *istack = NULL, *x_tmp = NULL, *y_tmp = NULL;
3982 	double det[2][3], *x_circum = NULL, *y_circum = NULL, *r2_circum = NULL, *x = NULL, *y = NULL;
3983 	double xmin, xmax, ymin, ymax, datax, dx, dy, dsq, dd;
3984 
3985 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Delaunay triangulation calculated by Dave Watson's ACORD [Computers & Geosciences, 8, 97-101, 1982]\n");
3986 
3987 	{
3988 		/* Note 2019/01/07: We were notified via https://github.com/GenericMappingTools/gmt/issues/279
3989 		 * that the Watson algorithm may give junk if there are duplicate entries in the input, and if so we issue
3990 		 * a stern warning to users so they can clean up the file first before calling gmtsupport_delaunay_watson */
3991 
3992 		struct GMT_PAIR *P = gmt_M_memory (GMT, NULL, n, struct GMT_PAIR);
3993 		uint64_t n_duplicates = 0;
3994 		for (i = 0; i < n; i++) {
3995 			P[i].x = x_in[i];
3996 			P[i].y = y_in[i];
3997 			P[i].rec = i + 1;
3998 		}
3999 		qsort (P, n, sizeof (struct GMT_PAIR), gmtsupport_sort_pair);
4000 		for (i = 1; i < n; i++) {
4001 			if (doubleAlmostEqualZero (P[i].x, P[i-1].x) && doubleAlmostEqualZero (P[i].y, P[i-1].y)) {
4002 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Records %" PRIu64 " and %" PRIu64 " are duplicates!\n", P[i-1].rec, P[i].rec);
4003 				n_duplicates++;
4004 			}
4005 		}
4006 		if (n_duplicates) {	/* Clearly not monotonically increasing or decreasing in x */
4007 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Bug Report Advice for Watson ACORD External Code:\n");
4008 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "The Watson algorithm can fail if there are duplicate (x,y) records.\n");
4009 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "We found %" PRIu64 " duplicate records in your data set.\n", n_duplicates);
4010 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Please remove duplicates and redo your analysis if the results are corrupted.\n");
4011 		}
4012 		gmt_M_free (GMT, P);
4013 	}
4014 
4015 	size = 10 * n + 1;
4016 	n += 3;
4017 	index = gmt_M_memory (GMT, NULL, 3 * size, int);
4018 	istack = gmt_M_memory (GMT, NULL, size, int64_t);
4019 	x_tmp = gmt_M_memory (GMT, NULL, size, int64_t);
4020 	y_tmp = gmt_M_memory (GMT, NULL, size, int64_t);
4021 	x_circum = gmt_M_memory (GMT, NULL, size, double);
4022 	y_circum = gmt_M_memory (GMT, NULL, size, double);
4023 	r2_circum = gmt_M_memory (GMT, NULL, size, double);
4024 	x = gmt_M_memory (GMT, NULL, n, double);
4025 	y = gmt_M_memory (GMT, NULL, n, double);
4026 
4027 	x[0] = x[1] = -1.0;	x[2] = 5.0;
4028 	y[0] = y[2] = -1.0;	y[1] = 5.0;
4029 	x_circum[0] = y_circum[0] = 2.0;	r2_circum[0] = 18.0;
4030 
4031 	ix[0] = ix[1] = 0;	ix[2] = 1;
4032 	iy[0] = 1;	iy[1] = iy[2] = 2;
4033 
4034 	for (i = 0; i < 3; i++) index[i] = (int)i;
4035 	for (i = 0; i < size; i++) istack[i] = i;
4036 
4037 	xmin = ymin = 1.0e100;	xmax = ymax = -1.0e100;
4038 
4039 	for (i = 3, j = 0; i < n; i++, j++) {	/* Copy data and get extremas */
4040 		x[i] = x_in[j];
4041 		y[i] = y_in[j];
4042 		if (x[i] > xmax) xmax = x[i];
4043 		if (x[i] < xmin) xmin = x[i];
4044 		if (y[i] > ymax) ymax = y[i];
4045 		if (y[i] < ymin) ymin = y[i];
4046 	}
4047 
4048 	/* Normalize data */
4049 
4050 	datax = 1.0 / MAX (xmax - xmin, ymax - ymin);
4051 
4052 	for (i = 3; i < n; i++) {
4053 		x[i] = (x[i] - xmin) * datax;
4054 		y[i] = (y[i] - ymin) * datax;
4055 	}
4056 
4057 	isp = id = 1;
4058 	for (nuc = 3; nuc < n; nuc++) {
4059 
4060 		km = 0;
4061 
4062 		for (jt = 0; jt < isp; jt++) {	/* loop through established 3-tuples */
4063 
4064 			ij = 3 * jt;
4065 
4066 			/* Test if new data is within the jt circumcircle */
4067 
4068 			dx = x[nuc] - x_circum[jt];
4069 			if ((dsq = r2_circum[jt] - dx * dx) < 0.0) continue;
4070 			dy = y[nuc] - y_circum[jt];
4071 			if ((dsq -= dy * dy) < 0.0) continue;
4072 
4073 			/* Delete this 3-tuple but save its edges */
4074 
4075 			id--;
4076 			istack[id] = jt;
4077 
4078 			/* Add edges to x/y_tmp but delete if already listed */
4079 
4080 			for (i = 0; i < 3; i++) {
4081 				l1 = ix[i];
4082 				l2 = iy[i];
4083 				if (km > 0) {
4084 					kmt = km;
4085 					for (j = 0, done = false; !done && j < kmt; j++) {
4086 						if (index[ij+l1] != x_tmp[j]) continue;
4087 						if (index[ij+l2] != y_tmp[j]) continue;
4088 						km--;
4089 						if (j >= km) {
4090 							done = true;
4091 							continue;
4092 						}
4093 						for (k = j; k < km; k++) {
4094 							k1 = k + 1;
4095 							x_tmp[k] = x_tmp[k1];
4096 							y_tmp[k] = y_tmp[k1];
4097 							done = true;
4098 						}
4099 					}
4100 				}
4101 				else
4102 					done = false;
4103 				if (!done) {
4104 					x_tmp[km] = index[ij+l1];
4105 					y_tmp[km] = index[ij+l2];
4106 					km++;
4107 				}
4108 			}
4109 		}
4110 
4111 		/* Form new 3-tuples */
4112 
4113 		for (i = 0; i < km; i++) {
4114 			kt = istack[id];
4115 			ij = 3 * kt;
4116 			id++;
4117 
4118 			/* Calculate the circumcircle center and radius
4119 			 * squared of points ktetr[i,*] and place in tetr[kt,*] */
4120 
4121 			for (jz = 0; jz < 2; jz++) {
4122 				i2 = (jz == 0) ? x_tmp[i] : y_tmp[i];
4123 				det[jz][0] = x[i2] - x[nuc];
4124 				det[jz][1] = y[i2] - y[nuc];
4125 				det[jz][2] = 0.5 * (det[jz][0] * (x[i2] + x[nuc]) + det[jz][1] * (y[i2] + y[nuc]));
4126 			}
4127 			dd = 1.0 / (det[0][0] * det[1][1] - det[0][1] * det[1][0]);
4128 			x_circum[kt] = (det[0][2] * det[1][1] - det[1][2] * det[0][1]) * dd;
4129 			y_circum[kt] = (det[0][0] * det[1][2] - det[1][0] * det[0][2]) * dd;
4130 			dx = x[nuc] - x_circum[kt];
4131 			dy = y[nuc] - y_circum[kt];
4132 			r2_circum[kt] = dx * dx + dy * dy;
4133 			index[ij++] = (int)x_tmp[i];
4134 			index[ij++] = (int)y_tmp[i];
4135 			index[ij] = (int)nuc;
4136 		}
4137 		isp += 2;
4138 	}
4139 
4140 	for (jt = i = 0; jt < isp; jt++) {
4141 		ij = 3 * jt;
4142 		if (index[ij] < 3 || r2_circum[jt] > 1.0) continue;
4143 		index[i++] = index[ij++] - 3;
4144 		index[i++] = index[ij++] - 3;
4145 		index[i++] = index[ij++] - 3;
4146 	}
4147 
4148 	index = gmt_M_memory (GMT, index, i, int);
4149 	*link = index;
4150 
4151 	gmt_M_free (GMT, istack);
4152 	gmt_M_free (GMT, x_tmp);
4153 	gmt_M_free (GMT, y_tmp);
4154 	gmt_M_free (GMT, x_circum);
4155 	gmt_M_free (GMT, y_circum);
4156 	gmt_M_free (GMT, r2_circum);
4157 	gmt_M_free (GMT, x);
4158 	gmt_M_free (GMT, y);
4159 
4160 	return (i/3);
4161 }
4162 
4163 /*! . */
gmtsupport_voronoi_watson(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n,double * wesn,unsigned int mode)4164 GMT_LOCAL struct GMT_DATASET * gmtsupport_voronoi_watson (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n, double *wesn, unsigned int mode) {
4165 	gmt_M_unused(x_in); gmt_M_unused(y_in); gmt_M_unused(n); gmt_M_unused(wesn); gmt_M_unused(mode);
4166 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "No Voronoi unless you select Shewchuk's triangle option during GMT installation\n");
4167 	return (0);
4168 }
4169 
4170 /*! . */
gmtsupport_ensure_new_mapinset_syntax(struct GMT_CTRL * GMT,char option,char * in_text,char * text,char * panel_txt)4171 GMT_LOCAL int gmtsupport_ensure_new_mapinset_syntax (struct GMT_CTRL *GMT, char option, char *in_text, char *text, char *panel_txt) {
4172 	/* Recasts any old syntax using new syntax and gives a warning.
4173  	   Assumes text and panel_text are blank and have adequate space */
4174 	if (gmt_found_modifier (GMT, in_text, "cgp")) {	/* Tell-tale sign of old syntax */
4175 		char p[GMT_BUFSIZ] = {""}, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""};
4176 		unsigned int pos = 0, start = 0, i;
4177 		int n;
4178 		bool center = false;
4179 		for (i = 0; start == 0 && in_text[i]; i++) {	/* Find the first valid modifier */
4180 			if (in_text[i] == '+') {
4181 				if (strchr ("cgp", in_text[i+1])) start = i;	/* Found start of the modifiers */
4182 			}
4183 		}
4184 		while ((gmt_strtok (&in_text[start], "+", &pos, p))) {
4185 			switch (p[0]) {
4186 				case 'c':	/* Got inset center */
4187 					center = true;
4188 					if ((n = sscanf (&p[1], "%[^/]/%s", txt_a, txt_b)) != 2) {
4189 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Must specify +c<lon>/<lat> for center [Also note this is obsolete syntax]\n", option);
4190 						return (1);
4191 					}
4192 					break;
4193 				case 'g':	/* Fill specification [Obsolete, now done via panel attributes ] */
4194 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -%c:  Insert fill attributes now given with panel setting (-F)\n", option);
4195 					strcat (panel_txt, "+g");
4196 					strcat (panel_txt, &p[1]);
4197 					break;
4198 				case 'p':	/* Pen specification [Obsolete, now done via panel attributes ] */
4199 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -%c:  Insert pen attributes now given with panel setting (-F)\n", option);
4200 					strcat (panel_txt, "+p");
4201 					strcat (panel_txt, &p[1]);
4202 					break;
4203 				default:
4204 					break;
4205 			}
4206 		}
4207 		in_text[start] = '\0';	/* Chop off modifiers for now */
4208 		if (center) {	/* Must extract dimensions of map inset */
4209 			char unit[2] = {0, 0};
4210 			sprintf (text, "g%s/%s/", txt_a, txt_b);	/* -Dg<lon>/<lat> is the new reference point */
4211 			n = sscanf (in_text, "%[^/]/%s", txt_a, txt_b);	/* Read dimensions */
4212 			if (strchr (GMT_LEN_UNITS2, txt_a[0])) {	/* Dimensions in allowable distance units, with unit in front of distance */
4213 				unit[0] = txt_a[0];		/* Extract the unit */
4214 				strcat (text, "+w");		/* Append width modifier */
4215 				strcat (text, &txt_a[1]);	/* Append width to new option */
4216 				strcat (text, unit);		/* Append unit to new option */
4217 				if (n == 2) {			/* Got separate height */
4218 					strcat (text, "/");		/* Separate width from height */
4219 					strcat (text, txt_b);		/* Append height or duplicate */
4220 					strcat (text, unit);		/* Append unit */
4221 				}
4222 			}
4223 			else
4224 				strcat (text, in_text);		/* Append h/w as is */
4225 			strcat (text, "+jCM");	/* Append justification */
4226 		}
4227 		else	/* Just keep as is, minus the modifiers */
4228 			strcpy (text, in_text);
4229 		in_text[start] = '+';	/* Restore modifiers */
4230 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Converted %s to %s and %s\n", in_text, text, panel_txt);
4231 	}
4232 	else	/* New syntax or no +g,+p given so old syntax is same as new */
4233 		strcpy (text, in_text);
4234 	return (0);
4235 }
4236 
4237 /*! . */
gmtsupport_getscale_old(struct GMT_CTRL * GMT,char option,char * text,struct GMT_MAP_SCALE * ms)4238 GMT_LOCAL int gmtsupport_getscale_old (struct GMT_CTRL *GMT, char option, char *text, struct GMT_MAP_SCALE *ms) {
4239 	/* This function parses the -L map scale syntax:
4240 	 * 	-L[f][x]<lon0>/<lat0>[/<slon>]/<slat>/<length>[e|f|M|n|k|u][+u]
4241 	 * The function is also backwards compatible with the previous map scale syntax:
4242 	 * 	-L [f][x]<lon0>/<lat0>[/<slon>]/<slat>/<length>[e|f|M|n|k|u][+l<label>][+j<just>][+p<pen>][+g<fill>][+u]
4243 	 * The old syntax is recognized by the optional +p and +g modifiers.  If either of these are present then we know
4244 	 * we are looking at the old syntax and we parse those options but give warning of obsolesnce; otherwise
4245 	 * we do the parsing of the newer format where the background panel is handled by another options (e.g. -F in psbasemap). */
4246 
4247 	int j = 0, i, n_slash, error = 0, k = 0, options;
4248 	bool gave_xy = false;
4249 	char txt_cpy[GMT_BUFSIZ] = {""}, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""};
4250 	char txt_sx[GMT_LEN256] = {""}, txt_sy[GMT_LEN256] = {""}, txt_len[GMT_LEN256] = {""}, string[GMT_LEN256] = {""};
4251 
4252 	if (!text) {
4253 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No argument given\n", option);
4254 		return GMT_PARSE_ERROR;
4255 	}
4256 
4257 	gmt_M_memset (ms, 1, struct GMT_MAP_SCALE);
4258 	ms->measure = 'k';	/* Default distance unit is km */
4259 	ms->alignment = 't';
4260 	ms->justify = PSL_TC;
4261 
4262 	/* First deal with possible prefixes f and x (i.e., f, x, xf, fx) */
4263 	if (text[j] == 'f') ms->fancy = true, j++;
4264 	if (text[j] == 'x') gave_xy = true, j++;
4265 	if (text[j] == 'f') ms->fancy = true, j++;	/* in case we got xf instead of fx */
4266 
4267 	/* Determine if we have the optional longitude component specified by counting slashes.
4268 	 * We stop counting if we reach a + sign since the fill or label might have a slash in them */
4269 
4270 	for (n_slash = 0, i = j; text[i] && text[i] != '+'; i++) if (text[i] == '/') n_slash++;
4271 	options = (text[i] == '+') ? i : -1;	/* -1, or starting point of first option */
4272 	if (options > 0) {	/* Have optional args, make a copy and truncate text */
4273 		strncpy (txt_cpy, &text[options], GMT_BUFSIZ-1);
4274 		text[options] = '\0';
4275 		for (i = 0; txt_cpy[i]; i++) {	/* Unless +fgjlpu, change other + to ASCII 1 to bypass strtok trouble later [f is now deprecated] */
4276 			if (txt_cpy[i] == '+' && !strchr ("fgjlpu", (int)txt_cpy[i+1])) txt_cpy[i] = 1;
4277 		}
4278 	}
4279 
4280 	if (n_slash == 4) {		/* -L[f][x]<x0>/<y0>/<lon>/<lat>/<length>[m|n|k][+l<label>][+j<just>][+p<pen>][+g<fill>][+u] */
4281 		k = sscanf (&text[j], "%[^/]/%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_sx, txt_sy, txt_len);
4282 	}
4283 	else if (n_slash == 3) {	/* -L[f][x]<x0>/<y0>/<lat>/<length>[m|n|k][+l<label>][+j<just>][+p<pen>][+g<fill>][+u] */
4284 		k = sscanf (&text[j], "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_sy, txt_len);
4285 	}
4286 	else {	/* Wrong number of slashes */
4287 		error++;
4288 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Not enough arguments given, including no width\n", option);
4289 		return (error);
4290 	}
4291 	i = (int)strlen (txt_len) - 1;
4292 	if (isalpha ((int)txt_len[i])) {	/* Letter at end of distance value */
4293 		ms->measure = txt_len[i];
4294 		if (gmt_M_compat_check (GMT, 4) && ms->measure == 'm') {
4295 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Distance unit m is deprecated; use M for statute miles\n");
4296 			ms->measure = 'M';
4297 		}
4298 		if (strchr (GMT_LEN_UNITS2, ms->measure))	/* Gave a valid distance unit */
4299 			txt_len[i] = '\0';
4300 		else {
4301 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Valid distance units are %s\n", option, GMT_LEN_UNITS2_DISPLAY);
4302 			error++;
4303 		}
4304 	}
4305 	ms->length = atof (txt_len);
4306 
4307 	if (gave_xy)	/* Set up anchor in plot units */
4308 		snprintf (string, GMT_LEN256, "x%s/%s", txt_a, txt_b);
4309 	else	/* Set up ancher in geographical coordinates */
4310 		snprintf (string, GMT_LEN256, "g%s/%s", txt_a, txt_b);
4311 	if ((ms->refpoint = gmt_get_refpoint (GMT, string, option)) == NULL) {
4312 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Scale reference point was not accepted\n", option);
4313 		gmt_refpoint_syntax (GMT, "L", NULL, GMT_ANCHOR_MAPSCALE, 3);
4314 		return (1);	/* Failed basic parsing */
4315 	}
4316 
4317 	error += gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, txt_sy, GMT_IS_LAT, &ms->origin[GMT_Y]), txt_sy);
4318 	if (fabs (ms->origin[GMT_Y]) > 90.0) {
4319 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Latitude where scale should apply is out of range\n", option);
4320 		error++;
4321 	}
4322 	if (k == 5)	{/* Must also decode longitude of scale */
4323 		error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_sx, GMT_IS_LON, &ms->origin[GMT_X]), txt_sx);
4324 		if (fabs (ms->origin[GMT_X]) > 360.0) {
4325 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Scale longitude is out of range\n", option);
4326 			error++;
4327 		}
4328 	}
4329 	else		/* Default to central meridian [this will be the result if we assign NaN here] */
4330 		ms->origin[GMT_X] = GMT->session.d_NaN;
4331 	if (ms->length <= 0.0) {
4332 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Length must be positive\n", option);
4333 		error++;
4334 	}
4335 
4336 	ms->old_style = gmt_found_modifier (GMT, txt_cpy, "fgp");
4337 
4338 	if (options > 0) {	/* Gave +?<args> which now must be processed */
4339 		char p[GMT_BUFSIZ], oldshit[GMT_LEN128] = {""};
4340 		unsigned int pos = 0, bad = 0;
4341 		while ((gmt_strtok (txt_cpy, "+", &pos, p))) {
4342 			switch (p[0]) {
4343 				case 'f':	/* Fill specification */
4344 					if (ms->old_style && gmt_M_compat_check (GMT, 4)) {	/*  Warn about old GMT 4 syntax */
4345 						GMT_Report (GMT->parent, GMT_MSG_COMPAT, "+f<fill> in map scale is deprecated, use -F panel settings instead\n");
4346 						strncat (oldshit, "+g", GMT_LEN128-1);
4347 						strncat (oldshit, &p[1], GMT_LEN128-3);
4348 					}
4349 					else
4350 						bad++;
4351 					break;
4352 				case 'g':	/* Fill specification */
4353 					if (ms->old_style && gmt_M_compat_check (GMT, 5)) {	/* Warn about old GMT 5 syntax */
4354 						GMT_Report (GMT->parent, GMT_MSG_COMPAT, "+g<fill> in map scale is deprecated, use -F panel settings instead\n");
4355 						strncat (oldshit, "+", GMT_LEN128-1);
4356 						strncat (oldshit, p, GMT_LEN128-2);
4357 					}
4358 					else
4359 						bad++;
4360 					break;
4361 
4362 				case 'j':	/* Label justification */
4363 					ms->alignment = p[1];
4364 					if (!(ms->alignment == 'l' || ms->alignment == 'r' || ms->alignment == 't' || ms->alignment == 'b')) bad++;
4365 					break;
4366 
4367 				case 'p':	/* Pen specification */
4368 					if (ms->old_style && gmt_M_compat_check (GMT, 5)) {	/* Warn about old syntax */
4369 						GMT_Report (GMT->parent, GMT_MSG_COMPAT, "+p<pen> in map scale is deprecated, use -F panel settings instead\n");
4370 						strncat (oldshit, "+", GMT_LEN128-1);
4371 						strncat (oldshit, p, GMT_LEN128-2);
4372 					}
4373 					else
4374 						bad++;
4375 					break;
4376 
4377 				case 'l':	/* Label specification */
4378 					if (p[1]) strncpy (ms->label, &p[1], GMT_LEN64-1);
4379 					ms->do_label = true;
4380 					for (i = 0; ms->label[i]; i++) if (ms->label[i] == 1) ms->label[i] = '+';	/* Change back ASCII 1 to + */
4381 					break;
4382 
4383 				case 'u':	/* Add units to annotations */
4384 					ms->unit = true;
4385 					break;
4386 
4387 				default:
4388 					bad++;
4389 					break;
4390 			}
4391 		}
4392 		error += bad;
4393 		text[options] = '+';	/* Restore original string */
4394 		if (ms->old_style && gmt_getpanel (GMT, 'F', oldshit, &(ms->panel))) {
4395 			gmt_mappanel_syntax (GMT, 'F', "Specify a rectangular panel behind the scale", 3);
4396 			error++;
4397 		}
4398 	}
4399 	if (error)
4400 		gmt_mapscale_syntax (GMT, 'L', "Draw a map scale centered on <lon0>/<lat0>.");
4401 
4402 	ms->plot = true;
4403 	return (error);
4404 }
4405 
4406 /*! . */
gmtsupport_getrose_old(struct GMT_CTRL * GMT,char option,char * text,struct GMT_MAP_ROSE * ms)4407 GMT_LOCAL int gmtsupport_getrose_old (struct GMT_CTRL *GMT, char option, char *text, struct GMT_MAP_ROSE *ms) {
4408 	int plus;
4409 	unsigned int j = 0, i, error = 0, colon, slash, k, pos, order[4] = {3,1,0,2};
4410 	bool gave_xy = false;
4411 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, txt_d[GMT_LEN256] = {""};
4412 	char tmpstring[GMT_LEN256] = {""}, string[GMT_LEN256] = {""}, p[GMT_LEN256] = {""};
4413 
4414 	/* SYNTAX is -?[f|m][x]<lon0>/<lat0>/<size>[/<info>][:label:][+<aint>/<fint>/<gint>[/<aint>/<fint>/<gint>]], where <info> is
4415 	 * 1)  -?f: <info> is <kind> = 1,2,3 which is the level of directions [1].
4416 	 * 2)  -?m: <info> is <dec>/<dlabel>, where <Dec> is magnetic declination and dlabel its label [no mag].
4417 	 * If -?m, optionally set annotation interval with +
4418 	 */
4419 
4420 	if (!text[0]) {
4421 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No argument given\n", option);
4422 		return GMT_PARSE_ERROR;
4423 	}
4424 
4425 	ms->type = GMT_ROSE_DIR_PLAIN;
4426 	ms->size = 0.0;
4427 	ms->a_int[0] = 30.0;	ms->f_int[0] = 5.0;	ms->g_int[0] = 1.0;
4428 	ms->a_int[1] = 30.0;	ms->f_int[1] = 5.0;	ms->g_int[1] = 1.0;
4429 	strcpy (ms->label[0], GMT->current.language.cardinal_name[2][2]);
4430 	strcpy (ms->label[1], GMT->current.language.cardinal_name[2][1]);
4431 	strcpy (ms->label[2], GMT->current.language.cardinal_name[2][3]);
4432 	strcpy (ms->label[3], GMT->current.language.cardinal_name[2][0]);
4433 
4434 	/* First deal with possible prefixes f and x (i.e., f|m, x, xf|m, f|mx) */
4435 	if (text[j] == 'f') ms->type = GMT_ROSE_DIR_FANCY, j++;
4436 	if (text[j] == 'm') ms->type = GMT_ROSE_MAG, j++;
4437 	if (text[j] == 'x') gave_xy = true, j++;
4438 	if (text[j] == 'f') ms->type = GMT_ROSE_DIR_FANCY, j++;		/* in case we got xf instead of fx */
4439 	if (text[j] == 'm') ms->type = GMT_ROSE_MAG, j++;		/* in case we got xm instead of mx */
4440 
4441 	/* Determine if we have the optional label components specified */
4442 
4443 	for (i = j, slash = 0; text[i] && slash < 2; i++) if (text[i] == '/') slash++;	/* Move i until the 2nd slash is reached */
4444 	for (k = (unsigned int)strlen(text) - 1, colon = 0; text[k] && k > i && colon < 2; k--)
4445 		if (text[k] == ':') colon++;	/* Move k to starting colon of :label: */
4446 	if (colon == 2 && k > i)
4447 		colon = k + 2;	/* Beginning of label */
4448 	else
4449 		colon = 0;	/* No labels given */
4450 
4451 	for (plus = -1, i = slash; text[i] && plus < 0; i++) if (text[i] == '+') plus = i+1;	/* Find location of + */
4452 	if (plus > 0) {		/* Get annotation interval(s) */
4453 		k = sscanf (&text[plus], "%lf/%lf/%lf/%lf/%lf/%lf", &ms->a_int[1], &ms->f_int[1], &ms->g_int[1], &ms->a_int[0], &ms->f_int[0], &ms->g_int[0]);
4454 		if (k < 1) {
4455 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Give annotation interval(s)\n", option);
4456 			error++;
4457 		}
4458 		if (k == 3) ms->a_int[0] = ms->a_int[1], ms->f_int[0] = ms->f_int[1], ms->g_int[0] = ms->g_int[1];
4459 		text[plus-1] = '\0';	/* Break string so sscanf won't get confused later */
4460 	}
4461 	if (colon > 0) {	/* Get labels in string :w,e,s,n: */
4462 		for (k = colon; text[k] && text[k] != ':'; k++);	/* Look for terminating colon */
4463 		if (text[k] != ':') { /* Ran out, missing terminating colon */
4464 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Labels must be given in format :w,e,s,n:\n", option);
4465 			error++;
4466 			return (error);
4467 		}
4468 		strncpy (tmpstring, &text[colon], (size_t)(k-colon));
4469 		tmpstring[k-colon] = '\0';
4470 		k = pos = 0;
4471 		while (k < 4 && (gmt_strtok (tmpstring, ",", &pos, p))) {	/* Get the four labels */
4472 			if (strcmp (p, "-")) strncpy (ms->label[order[k]], p, GMT_LEN64-1);
4473 			k++;
4474 		}
4475 		ms->do_label = true;
4476 		if (k == 0) {	/* No labels wanted */
4477 			ms->label[0][0] = ms->label[1][0] = ms->label[2][0] = ms->label[3][0] = '\0';
4478 			ms->do_label = false;
4479 		}
4480 		else if (k != 4) {	/* Ran out of labels */
4481 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Labels must be given in format :w,e,s,n:\n", option);
4482 			error++;
4483 		}
4484 		text[colon-1] = '\0';	/* Break string so sscanf won't get confused later */
4485 	}
4486 
4487 	/* -?[f][x]<x0>/<y0>/<size>[/<kind>][:label:] OR -L[m][x]<x0>/<y0>/<size>[/<dec>/<declabel>][:label:][+gint[/mint]] */
4488 	if (ms->type == GMT_ROSE_MAG) {	/* Magnetic rose */
4489 		k = sscanf (&text[j], "%[^/]/%[^/]/%[^/]/%[^/]/%[^/]", txt_a, txt_b, txt_c, txt_d, ms->dlabel);
4490 		if (! (k == 3 || k == 5)) {	/* Wrong number of parameters */
4491 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Correct syntax\n", option);
4492 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "\t-%c[f|m][x]<x0>/<y0>/<size>[/<info>][:wesnlabels:][+<gint>[/<mint>]]\n", option);
4493 			error++;
4494 		}
4495 		if (k == 3) {	/* No magnetic north directions */
4496 			ms->kind = 1;
4497 			ms->declination = 0.0;
4498 			ms->dlabel[0] = '\0';
4499 		}
4500 		else {
4501 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_d, GMT_IS_LON, &ms->declination), txt_d);
4502 			ms->kind = 2;
4503 		}
4504 	}
4505 	else {
4506 		k = sscanf (&text[j], "%[^/]/%[^/]/%[^/]/%d", txt_a, txt_b, txt_c, &ms->kind);
4507 		if (k == 3) ms->kind = 1;
4508 		if (k < 3 || k > 4) {	/* Wrong number of parameters */
4509 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Correct syntax\n", option);
4510 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "\t-%c[f|m][x]<x0>/<y0>/<size>[/<info>][:wesnlabels:][+<gint>[/<mint>]]\n", option);
4511 			error++;
4512 		}
4513 	}
4514 	if (colon > 0) text[colon-1] = ':';	/* Put it back */
4515 	if (plus > 0) text[plus-1] = '+';	/* Put it back */
4516 	if (gave_xy)	/* Set up ancher in plot units */
4517 		snprintf (string, GMT_LEN256, "x%s/%s", txt_a, txt_b);
4518 	else	/* Set up ancher in geographical coordinates */
4519 		snprintf (string, GMT_LEN256, "g%s/%s", txt_a, txt_b);
4520 	if ((ms->refpoint = gmt_get_refpoint (GMT, string, option)) == NULL) {
4521 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Map rose reference point was not accepted\n", option);
4522 		gmt_refpoint_syntax (GMT, "Td|m", NULL, GMT_ANCHOR_MAPROSE, 3);
4523 		return (1);	/* Failed basic parsing */
4524 	}
4525 	ms->justify = PSL_MC;	/* Center it is */
4526 	ms->size = gmt_M_to_inch (GMT, txt_c);
4527 	if (ms->size <= 0.0) {
4528 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Size must be positive\n", option);
4529 		error++;
4530 	}
4531 	if (ms->kind < 1 || ms->kind > 3) {
4532 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  <kind> must be 1, 2, or 3\n", option);
4533 		error++;
4534 	}
4535 
4536 	ms->plot = true;
4537 	return (error);
4538 }
4539 
4540 /* Here lies GMT Crossover core functions that previously was in X2SYS only */
4541 /* gmtsupport_ysort must be an int since it is passed to qsort! */
4542 /*! . */
gmtsupport_ysort(const void * p1,const void * p2,void * arg)4543 GMT_LOCAL int gmtsupport_ysort (const void *p1, const void *p2, void *arg) {
4544 	const struct GMT_XSEGMENT *a = p1, *b = p2;
4545 	double *x2sys_y = arg;
4546 
4547 	if (x2sys_y[a->start] < x2sys_y[b->start]) return -1;
4548 	if (x2sys_y[a->start] > x2sys_y[b->start]) return  1;
4549 
4550 	/* Here they have the same low y-value, now sort on other y value */
4551 	if (x2sys_y[a->stop] < x2sys_y[b->stop]) return -1;
4552 	if (x2sys_y[a->stop] > x2sys_y[b->stop]) return  1;
4553 
4554 	/* Identical */
4555 	return (0);
4556 }
4557 
4558 /*! . */
gmtsupport_x_alloc(struct GMT_CTRL * GMT,struct GMT_XOVER * X,size_t nx_alloc,bool first)4559 GMT_LOCAL void gmtsupport_x_alloc (struct GMT_CTRL *GMT, struct GMT_XOVER *X, size_t nx_alloc, bool first) {
4560 	if (first) {	/* Initial allocation */
4561 		X->x = gmt_M_memory (GMT, NULL, nx_alloc, double);
4562 		X->y = gmt_M_memory (GMT, NULL, nx_alloc, double);
4563 		X->xnode[0] = gmt_M_memory (GMT, NULL, nx_alloc, double);
4564 		X->xnode[1] = gmt_M_memory (GMT, NULL, nx_alloc, double);
4565 	}
4566 	else {	/* Increment */
4567 		X->x = gmt_M_memory (GMT, X->x, nx_alloc, double);
4568 		X->y = gmt_M_memory (GMT, X->y, nx_alloc, double);
4569 		X->xnode[0] = gmt_M_memory (GMT, X->xnode[0], nx_alloc, double);
4570 		X->xnode[1] = gmt_M_memory (GMT, X->xnode[1], nx_alloc, double);
4571 	}
4572 }
4573 
gmtsupport_x_overlap(double * xa,double * xb,uint64_t * xa_start,uint64_t * xa_stop,uint64_t * xb_start,uint64_t * xb_stop,bool geo,double * dx)4574 GMT_LOCAL bool gmtsupport_x_overlap (double *xa, double *xb, uint64_t *xa_start, uint64_t *xa_stop, uint64_t *xb_start, uint64_t *xb_stop, bool geo, double *dx) {
4575 	/* Return true if the two x-ranges overlap */
4576 	if (geo) {	/* More complicated, and may change both the start/stop indices and the array longitudes */
4577 		int k;
4578 		double del_x = xa[*xa_stop] - xa[*xa_start];	/* Check if line A has a longitude jump */
4579 		if (del_x > 180.0) {xa[*xa_start] += 360.0; gmt_M_uint64_swap(*xa_start, *xa_stop);}	/* Deal with 360 and swap start and stop indices */
4580 		del_x = xb[*xb_stop] - xb[*xb_start];		/* Check if line B has a longitude jump */
4581 		if (del_x > 180.0) {xb[*xb_start] += 360.0; gmt_M_uint64_swap(*xb_start, *xb_stop);}	/* Deal with 360 and swap start and stop indices */
4582 		/* Here we have fixed any 360 jumps in A and B and reassign what is start and stop. We must now look for overlaps
4583 		 * between the segments by considering that A may be off by -360, 0, or +360 degrees in longitude relative to B. */
4584 
4585 		for (k = -1; k <= 1; k++) {	/* Try offsets of k * 360. If we find overlap then *dx returns the shift we must add to the xa coordinates later */
4586 			*dx = k * 360.0;
4587 			if (!(((xa[*xa_stop] + (*dx)) < xb[*xb_start]) || ((xa[*xa_start] + (*dx)) > xb[*xb_stop]))) return true;
4588 		}
4589 		return false;	/* No overlap */
4590 	}
4591 	else {	/* Cartesian is simple since xa_start <= xa_stop and xb_start <= xb_stop */
4592 		return (!((xa[*xa_stop] < xb[*xb_start]) || (xa[*xa_start] > xb[*xb_stop])));
4593 	}
4594 }
4595 
gmtsupport_polar_adjust(struct GMT_CTRL * GMT,int side,double angle,double x,double y)4596 GMT_LOCAL int gmtsupport_polar_adjust (struct GMT_CTRL *GMT, int side, double angle, double x, double y) {
4597 	int justify, left, right, top, bottom, low;
4598 	double x0, y0, f_angle = 180.0;
4599 
4600 	/* gmt_geo_to_xy (GMT->current.proj.central_meridian, GMT->current.proj.pole, &x0, &y0); */
4601 
4602 	x0 = GMT->current.proj.c_x0;
4603 	y0 = GMT->current.proj.c_y0;
4604 	if (GMT->current.proj.north_pole) {
4605 		low = 0;
4606 		left = 7;
4607 		right = 5;
4608 	}
4609 	else {
4610 		low = 2;
4611 		left = 5;
4612 		right = 7;
4613 	}
4614 	if ((y - y0 - GMT_CONV4_LIMIT) > 0.0) { /* i.e., y > y0 */
4615 		top = 2;
4616 		bottom = 10;
4617 	}
4618 	else {
4619 		top = 10;
4620 		bottom = 2;
4621 	}
4622 	if (GMT->current.proj.projection_GMT == GMT_POLAR) {	/* Special concerns with r/theta systems */
4623 		if (GMT->current.proj.got_azimuths) gmt_M_int_swap (left, right);	/* Because with azimuths we get confused... */
4624 		if (GMT->current.proj.flip) {
4625 			gmt_M_int_swap (top, bottom);
4626 			gmt_M_int_swap (left, right);
4627 			low = 2 - low;
4628 			//f_angle = 0.0;
4629 		}
4630 	}
4631 	if (side%2) {	/* W and E border */
4632 		if ((y - y0 + GMT_CONV4_LIMIT) > 0.0)
4633 			justify = (side == 1) ? left : right;
4634 		else
4635 			justify = (side == 1) ? right : left;
4636 	}
4637 	else {	/* S and N border */
4638 		if (GMT->current.map.frame.horizontal) {
4639 			if (side == low)
4640 				justify = (doubleAlmostEqualZero (angle, f_angle)) ? bottom : top;
4641 			else
4642 				justify = (gmt_M_is_zero (angle)) ? top : bottom;
4643 			if (GMT->current.proj.flip && !GMT->current.proj.got_elevations) justify = (justify + 8) % 16;
4644 			if (GMT->current.proj.got_elevations && (doubleAlmostEqualZero (angle, f_angle) || gmt_M_is_zero (angle)))
4645 				justify = (justify + 8) % 16;
4646 		}
4647 		else {
4648 			if (x >= x0)
4649 				justify = (side == 2) ? left : right;
4650 			else
4651 				justify = (side == 2) ? right : left;
4652 		}
4653 	}
4654 	return (justify);
4655 }
4656 
4657 /*! . */
gmtsupport_get_label_parameters(struct GMT_CTRL * GMT,int side,double line_angle,int type,double * text_angle,unsigned int * justify)4658 GMT_LOCAL bool gmtsupport_get_label_parameters (struct GMT_CTRL *GMT, int side, double line_angle, int type, double *text_angle, unsigned int *justify) {
4659 	bool ok;
4660 
4661 	*text_angle = line_angle;
4662 #ifdef OPT_WORKS_BADLY
4663 	if (*text_angle < -90.0) *text_angle += 360.0;
4664 	if (GMT->current.map.frame.horizontal && !(side%2)) *text_angle += 90.0;
4665 	if (*text_angle > 270.0 ) *text_angle -= 360.0;
4666 	else if (*text_angle > 90.0) *text_angle -= 180.0;
4667 #else
4668 	if ( (*text_angle + 90.0) < GMT_CONV8_LIMIT) *text_angle += 360.0;
4669 	if (GMT->current.map.frame.horizontal && !(side%2)) *text_angle += 90.0;
4670 	if ( (*text_angle - 270.0) > GMT_CONV8_LIMIT ) *text_angle -= 360.0;
4671 	else if ( (*text_angle - 90.0) > GMT_CONV8_LIMIT) *text_angle -= 180.0;
4672 #endif
4673 
4674 	if (type == 0 && GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_LON_HORIZONTAL) *text_angle = 0.0;	/* Force horizontal lon annotation */
4675 	if (type == 1 && GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_LAT_HORIZONTAL) *text_angle = 0.0;	/* Force horizontal lat annotation */
4676 
4677 	switch (side) {
4678 		case 0:		/* S */
4679 			if (GMT->current.map.frame.horizontal)
4680 				*justify = (GMT->current.proj.got_elevations) ? 2 : 10;
4681 			else
4682 				*justify = ((*text_angle) < 0.0) ? 5 : 7;
4683 			break;
4684 		case 1:		/* E */
4685 			if (type == 1 && GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_LAT_PARALLEL) {
4686 				*text_angle = 90.0;	/* Force parallel lat annotation */
4687 				*justify = 10;
4688 			}
4689 			else
4690 				*justify = 5;
4691 			break;
4692 		case 2:		/* N */
4693 			if (GMT->current.map.frame.horizontal)
4694 				*justify = (GMT->current.proj.got_elevations) ? 10 : 2;
4695 			else
4696 				*justify = ((*text_angle) < 0.0) ? 7 : 5;
4697 			break;
4698 		default:	/* W */
4699 			if (type == 1 && GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_LAT_PARALLEL) {
4700 				*text_angle = 90.0;	/* Force parallel lat annotation */
4701 				*justify = 2;
4702 			}
4703 			else
4704 				*justify = 7;
4705 			break;
4706 	}
4707 
4708 	if (GMT->current.map.frame.horizontal) return (true);
4709 
4710 	switch (side) {
4711 		case 0:		/* S */
4712 		case 2:		/* N */
4713 			ok = (fabs ((*text_angle)) >= GMT->current.setting.map_annot_min_angle);
4714 			break;
4715 		default:	/* E or W */
4716 			ok = (fabs ((*text_angle)) <= (90.0 - GMT->current.setting.map_annot_min_angle));
4717 			break;
4718 	}
4719 	return (ok);
4720 }
4721 
4722 /*! . */
gmtsupport_gnomonic_adjust(struct GMT_CTRL * GMT,double angle,double x,double y)4723 GMT_LOCAL int gmtsupport_gnomonic_adjust (struct GMT_CTRL *GMT, double angle, double x, double y) {
4724 	/* Called when GNOMONIC and global region.  angle has been fixed to the +- 90 range */
4725 	/* This is a kludge until we rewrite the entire justification stuff */
4726 	bool inside;
4727 	double xp, yp, r;
4728 
4729 	/* Create a point a small step away from (x,y) along the angle baseline
4730 	 * If it is inside the circle the we want right-justify, else left-justify. */
4731 	sincosd (angle, &yp, &xp);
4732 	xp = xp * GMT->current.setting.map_line_step + x;
4733 	yp = yp * GMT->current.setting.map_line_step + y;
4734 	r = hypot (xp - GMT->current.proj.r, yp - GMT->current.proj.r);
4735 	inside = (r < GMT->current.proj.r);
4736 	return ((inside) ? 7 : 5);
4737 }
4738 
4739 /*! . */
gmtsupport_free_one_custom_symbol(struct GMT_CTRL * GMT,struct GMT_CUSTOM_SYMBOL * sym)4740 GMT_LOCAL void gmtsupport_free_one_custom_symbol (struct GMT_CTRL *GMT, struct GMT_CUSTOM_SYMBOL *sym) {
4741 	/* Free one allocated custom symbol */
4742 	struct GMT_CUSTOM_SYMBOL_ITEM *s = NULL, *current = NULL;
4743 
4744 	if (sym == NULL) return;
4745 	s = sym->first;
4746 	while (s) {
4747 		current = s;
4748 		s = s->next;
4749 		gmt_M_free (GMT, current->fill);
4750 		gmt_M_free (GMT, current->pen);
4751 		gmt_M_free (GMT, current->string);
4752 		if (current->eps) {
4753 			gmt_M_free (GMT, current->eps->macro);
4754 			gmt_M_str_free (current->eps->name);
4755 			gmt_M_free (GMT, current->eps);
4756 		}
4757 		gmt_M_free (GMT, current);
4758 	}
4759 	gmt_M_free (GMT, sym->type);
4760 	gmt_M_free (GMT, sym);
4761 }
4762 
4763 /*! . */
gmtsupport_decode_arg(char * txt,int column,struct GMT_CUSTOM_SYMBOL_ITEM * s)4764 GMT_LOCAL int gmtsupport_decode_arg (char *txt, int column, struct GMT_CUSTOM_SYMBOL_ITEM *s) {
4765 	/* Look for $<var> arguments which means supply input at runtime from data record.
4766 	 * Otherwise it is a value to be used as is.  One exception are values that end with
4767 	 * 'a' which means azimuth.  We return revised actions for the two rotation actions.
4768 	 * Other symbols will ignore the returned action. */
4769 	int new_action = s->action;	/* This output is only used for the ROTATE symbol */
4770 	size_t k = strlen (txt) - 1;	/* Index of last character */
4771 	assert (column < CUSTOM_SYMBOL_MAXVAR);	/* Otherwise we want to know */
4772 	if (txt[0] == '$') {	/* Got a variable as angle */
4773 		s->var[column] = atoi (&txt[1]);
4774 		s->is_var[column] = true;
4775 		s->var_sign[column] = 1.0;
4776 		new_action = GMT_SYMBOL_VARROTATE;	/* Mark as a different rotate action */
4777 	}
4778 	else if (txt[0] == '-' && txt[1] == '$') {	/* Got a variable as negative angle */
4779 		s->var[column] = atoi (&txt[2]);
4780 		s->is_var[column] = true;
4781 		s->var_sign[column] = -1.0;
4782 		new_action = GMT_SYMBOL_VARROTATE;	/* Mark as a different rotate action */
4783 	}
4784 	else if (txt[k] == 'a') {	/* Got a fixed azimuth and must flag via a different action */
4785 		txt[k] = '\0';	/* Temporarily remove the trailing 'a' */
4786 		s->p[column] = atof (txt);	/* Get azimuth */
4787 		new_action = GMT_SYMBOL_AZIMROTATE;	/* Mark as a different rotate action */
4788 		txt[k] = 'a';	/* Restore the trailing 'a' */
4789 		s->angular = GMT_IS_AZIMUTH;
4790 	}
4791 	else	/* Got a fixed Cartesian plot angle */
4792 		s->p[column] = atof (txt);
4793 	return (new_action);
4794 }
4795 
gmt_locate_custom_symbol(struct GMT_CTRL * GMT,const char * in_name,char * name,char * path,unsigned int * pos)4796 int gmt_locate_custom_symbol (struct GMT_CTRL *GMT, const char *in_name, char *name, char *path, unsigned int *pos) {
4797 	/* Return codes are 0 = not found, 1 = def, 2 = eps */
4798 	bool try[3] = {false, true, true};	/* Try to look for either .def or .eps (index [0] is not used) */
4799 	bool try_remote = false;
4800 	size_t k, length;
4801 	char file[PATH_MAX] = {""}, *ext[3] = {"", ".def", ".eps"};
4802 	/* Because we do not necessarily know if the custom symbol requested is an EPS or DEF file, we must check
4803 	 * for both unless an extension is given and then we know.  To minimize checking on the remote server, we
4804 	 * decide to look for one or both files only locally first, and if that fails then we start looking in the\
4805 	 * remote cache. */
4806 
4807 	/* Determine if in_name ends in ".def", ".eps" or neither */
4808 	length = strlen (in_name);
4809 	if (length > 4 && !strcmp (&in_name[length-4], ext[GMT_CUSTOM_DEF])) {	/* User gave trailing .def extension */
4810 		strncpy (name, in_name, length-4);	/* Eliminate extension in name */
4811 		try[GMT_CUSTOM_EPS] = false;	/* No need looking for eps files */
4812 	}
4813 	else if (length > 4 && !strcmp (&in_name[length-4], ext[GMT_CUSTOM_EPS])) {	/* User gave trailing .eps extension */
4814 		strncpy (name, in_name, length-4);	/* Eliminate extension in name */
4815 		try[GMT_CUSTOM_DEF] = false;	/* No need looking for def files */
4816 	}
4817 	else	/* Use as is, extension remains unknown */
4818 		strcpy (name, in_name);
4819 
4820 	if (gmt_file_is_cache (GMT->parent, name)) {
4821 		try_remote = true;	/* Is a cache file */
4822 		*pos = 1;
4823 	}
4824 	for (k = GMT_CUSTOM_DEF; k <= GMT_CUSTOM_EPS; k++) {
4825 		if (!try[k]) continue;	/* No need to check for this type of custom symbol*/
4826 		snprintf (file, PATH_MAX, "%s%s", name, ext[k]);	/* Full name of potential def|eps file */
4827 		if (gmt_getsharepath (GMT, "custom", &name[*pos], ext[k], path, R_OK) || gmtlib_getuserpath (GMT, &file[*pos], path)) {
4828 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found custom symbol %s in %s\n", name, path);
4829 			return k;	/* Found local *.def or *.eps file */
4830 		}
4831 	}
4832 	if (!try_remote) {
4833 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find either custom symbol or EPS macro %s\n", name);
4834 		return 0;	/* Not found, and since not a cache file we don't need to look further */
4835 	}
4836 
4837 	/* Here we failed to find the cache file locally.  Now try remote cache */
4838 	for (k = GMT_CUSTOM_DEF; k <= GMT_CUSTOM_EPS; k++) {
4839 		if (!try[k]) continue;	/* No need to check this on */
4840 		snprintf (file, PATH_MAX, "%s%s", name, ext[k]);	/* Full name of potential def|eps file */
4841 		/* Look for remote files.  Passing 4 to avoid hearing about missing remote files
4842 		 * which can happen when we look for *.def but the file is actually a *.eps [Example 46] */
4843 		*pos = gmt_download_file_if_not_found (GMT, file, 4);
4844 		/* Here, pos is position of first character in the name after any leading URLs or @ [0] */
4845 		if (gmt_getsharepath (GMT, "custom", &name[*pos], ext[k], path, R_OK) || gmtlib_getuserpath (GMT, &file[*pos], path)) {
4846 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found custom symbol %s in %s\n", name, path);
4847 			return k;	/* Found local *.def or *.eps file */
4848 		}
4849 	}
4850 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find either custom symbol or EPS macro %s\n", name);
4851 	return (0);
4852 
4853 }
4854 
gmtsupport_load_eps_symbol(struct GMT_CTRL * GMT,char * name,char * path)4855 GMT_LOCAL struct GMT_CUSTOM_SYMBOL_EPS * gmtsupport_load_eps_symbol (struct GMT_CTRL *GMT, char *name, char *path) {
4856 	unsigned int bb;
4857 	bool got_BB[2] = {false, false};
4858 	static char *BB_string[2] = {"%%HiResBoundingBox:", "%%BoundingBox:"};
4859 	char buffer[GMT_BUFSIZ] = {""};
4860 	struct stat buf;
4861 	struct GMT_CUSTOM_SYMBOL_EPS *E = NULL;
4862 	FILE *fp = NULL;
4863 
4864 	if (stat (path, &buf)) {
4865 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not determine size of EPS symbol %s\n", path);
4866 		return NULL;
4867 	}
4868 	if ((fp = fopen (path, "r")) == NULL) {
4869 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find custom symbol %s\n", path);
4870 		return NULL;
4871 	}
4872 	E = gmt_M_memory (GMT, NULL, 1U, struct GMT_CUSTOM_SYMBOL_EPS);
4873 	E->macro = gmt_M_memory (GMT, NULL, (size_t)buf.st_size, char);
4874 	E->name = strdup (name);
4875 	while (fgets (buffer, GMT_BUFSIZ, fp)) {
4876 		for (bb = 0; bb < 2; bb++) {	/* Check for both flavors of BoundingBox unless found */
4877 			if (!got_BB[bb] && (strstr (buffer, BB_string[bb]))) {
4878 				char c1[GMT_VF_LEN] = {""}, c2[GMT_VF_LEN] = {""}, c3[GMT_VF_LEN] = {""}, c4[GMT_VF_LEN] = {""};
4879 				sscanf (&buffer[strlen(BB_string[bb])], "%s %s %s %s", c1, c2, c3, c4);
4880 				E->BB[0] = atof (c1);	E->BB[2] = atof (c2);
4881 				E->BB[1] = atof (c3);	E->BB[3] = atof (c4);
4882 				got_BB[bb] = true;
4883 				if (bb == 0) got_BB[1] = true;	/* If we find HighRes BB then we don't need to look for LowRes BB */
4884 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Custom EPS symbol %s has width %g and height %g inches [%s]\n",
4885 					path, (E->BB[1] - E->BB[0]) / 72, (E->BB[3] - E->BB[2]) / 72, &BB_string[bb][2]);
4886 			}
4887 		}
4888 		if (buffer[0] == '%' && (buffer[1] == '%' || buffer[1] == '!')) continue;	/* Skip comments */
4889 		strcat (E->macro, buffer);	/* Keep appending to the macro until EOF */
4890 	}
4891 	gmt_fclose (GMT, fp);
4892 	if (strstr (E->macro, "%%Creator: GMT")) {	/* Check if a GMT EPS or not */
4893 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "EPS symbol %s is a GMT-produced macro\n", path);
4894 		E->GMT_made = true;	/* So we know to scale the EPS by 1200/72 or not in gmt_plot.c */
4895 	}
4896 	return (E);
4897 }
4898 
gmtsupport_make_template(struct GMTAPI_CTRL * API,char * stem,char path[])4899 GMT_LOCAL void gmtsupport_make_template (struct GMTAPI_CTRL *API, char *stem, char path[]) {
4900 	/* Create a template for making a unique temporary file or directory name on the system.
4901 	 * The 6 XXXXXX will be replaced by mktemp or mkstemp with a unique 6-char token (62^6 unique tokens).
4902 	 * If a temp dir is designated then we use that in the path else we assume current directory.
4903 	 * If stem is NULL we use "gmttemp" as file prefix.  If extension is not NULL we append it as is.
4904 	 * Note: path is expected to have a length of PATH_MAX.
4905 	 */
4906 	if (API->tmp_dir)	/* Use the established temp directory */
4907 		snprintf (path, PATH_MAX, "%s/%s_XXXXXX", API->tmp_dir, stem ? stem : "gmttemp");
4908 	else	/* Must dump it in current directory */
4909 		snprintf (path, PATH_MAX, "%s_XXXXXX", stem ? stem : "gmttemp");
4910 }
4911 
gmt_get_tempname(struct GMTAPI_CTRL * API,char * stem,char * extension,char path[])4912 int gmt_get_tempname (struct GMTAPI_CTRL *API, char *stem, char *extension, char path[]) {
4913 	/* Create a unique temporary file or directory name on the system;
4914 	 * If stem is NULL we use "gmttemp" as file prefix.  If extension is not NULL we append it.
4915 	 * Note: path is expected to have a length of PATH_MAX.
4916 	 */
4917 
4918 	gmtsupport_make_template (API, stem, path);	/* Readying the name template */
4919 
4920 	if (mktemp (path) == NULL) {
4921 		GMT_Report (API, GMT_MSG_ERROR, "Could not create a temporary name from template %s.\n", path);
4922 		return (GMT_RUNTIME_ERROR);
4923 	}
4924 	if (extension) strcat (path, extension);	/* Appending a file extension is unlikely to change uniqueness */
4925 	return (GMT_NOERROR);
4926 }
4927 
gmt_create_tempfile(struct GMTAPI_CTRL * API,char * stem,char * extension,char path[])4928 FILE *gmt_create_tempfile (struct GMTAPI_CTRL *API, char *stem, char *extension, char path[]) {
4929 	/* Returns file pointer to the desired temp file open for writing */
4930 	FILE *fp = NULL;
4931 #ifndef _WIN32
4932 	/* Here we can use the race-safe mkstemp function which returns a file descriptor */
4933 	int fd = 0;
4934 	gmt_M_unused (extension);
4935 	gmtsupport_make_template (API, stem, path);	/* Readying the name template */
4936 	if ((fd = mkstemp (path)) == -1) {	/* Create filename AND open in one go to avoid race condition */
4937 		GMT_Report (API, GMT_MSG_ERROR, "gmt_create_tempfile: Could not create temporary file name and open it %s.\n", path);
4938 		API->error = GMT_RUNTIME_ERROR;
4939 		return NULL;
4940 	}
4941 	if ((fp = fdopen (fd, API->GMT->current.io.w_mode)) == NULL) {	/* Get FILE pointer from file descriptor */
4942 		API->error = GMT_RUNTIME_ERROR;
4943 		GMT_Report (API, GMT_MSG_ERROR, "gmt_create_tempfile: Could not fdopen the temporary file %s.\n", path);
4944 		return NULL;
4945 	}
4946 #else	/* Must create file name first, then open file - susceptible to race condition if another process gets there first */
4947 	if (gmt_get_tempname (API, stem, extension, path)) {
4948 		GMT_Report (API, GMT_MSG_ERROR, "gmt_create_tempfile: Could not create temporary file name %s.\n", path);
4949 		API->error = GMT_RUNTIME_ERROR;
4950 		return NULL;
4951 	}
4952 	if ((fp = fopen (path, API->GMT->current.io.w_mode)) == NULL) {
4953 		GMT_Report (API, GMT_MSG_ERROR, "gmtlib_get_tile_list: Unable to open the temporary file: %s.\n", path);
4954 		API->error = GMT_RUNTIME_ERROR;
4955 		return NULL;
4956 	}
4957 #endif
4958 	return (fp);
4959 }
4960 
4961 /*! . */
gmtsupport_init_custom_symbol(struct GMT_CTRL * GMT,char * in_name,struct GMT_CUSTOM_SYMBOL ** S)4962 GMT_LOCAL int gmtsupport_init_custom_symbol (struct GMT_CTRL *GMT, char *in_name, struct GMT_CUSTOM_SYMBOL **S) {
4963 	/* Load in an initialize a new custom symbol.  These files can live in many places:
4964 	 * 1. In the current directory
4965 	 * 2. In the user dir [~/.gmt]
4966 	 * 3. In the user cache dir [~/.gmt/cache]
4967 	 * 4. In the user data dir [~/.gmt/data]
4968 	 * 5. In the system share/custom dir
4969 	 * THus we must use both gmt_getsharepath and gmtlib_getuserpath when looking */
4970 	unsigned int k, nc = 0, nv, error = 0, var_symbol = 0, pos = 0, n_txt = 0, type;
4971 	int last;
4972 	bool do_fill, do_pen, first = true;
4973 	char name[GMT_BUFSIZ] = {""}, path[PATH_MAX] = {""}, buffer[GMT_BUFSIZ] = {""}, col[8][GMT_LEN128], OP[GMT_LEN8] = {""}, right[GMT_LEN64] = {""};
4974 	char arg[3][GMT_LEN64] = {"", "", ""}, *fill_p = NULL, *pen_p = NULL, *c = NULL;
4975 	FILE *fp = NULL;
4976 	struct GMT_CUSTOM_SYMBOL *head = NULL;
4977 	struct GMT_CUSTOM_SYMBOL_ITEM *s = NULL, *previous = NULL;
4978 
4979 	if ((type = gmt_locate_custom_symbol (GMT, in_name, name, path, &pos)) == 0) return GMT_RUNTIME_ERROR;
4980 
4981 	if (type == GMT_CUSTOM_EPS) {	/* Must replace EPS symbol with a simple 1-action custom symbl EPS command */
4982 		/* Single action with x=y=0, set size=1, and add in EPS loaded */
4983 		head = gmt_M_memory (GMT, NULL, 1, struct GMT_CUSTOM_SYMBOL);
4984 		strncpy (head->name, basename (&name[pos]), GMT_LEN64-1);
4985 		s = gmt_M_memory (GMT, NULL, 1, struct GMT_CUSTOM_SYMBOL_ITEM);
4986 		head->first = s;
4987 		s->action = GMT_SYMBOL_EPS;
4988 		s->p[0] = 1.0;
4989 		if ((s->eps = gmtsupport_load_eps_symbol (GMT, name, path)) == NULL) return GMT_RUNTIME_ERROR;
4990 		*S = head;
4991 		return (GMT_NOERROR);
4992 	}
4993 
4994 	if ((fp = fopen (path, "r")) == NULL) {
4995 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find custom symbol %s\n", &name[pos]);
4996 		return GMT_ERROR_ON_FOPEN;
4997 	}
4998 
4999 	head = gmt_M_memory (GMT, NULL, 1, struct GMT_CUSTOM_SYMBOL);
5000 	strncpy (head->name, basename (&name[pos]), GMT_LEN64-1);
5001 	while (fgets (buffer, GMT_BUFSIZ, fp)) {
5002 		if (buffer[0] == '#') continue;	/* Skip comments */
5003 		gmt_chop (buffer);	/* Get rid of \n \r */
5004 		if ((c = strrchr (buffer, '#')))	/* Has trailing comments */
5005 			c[0] = '\0';	/* Chop it off */
5006 		if (gmt_is_a_blank_line (buffer)) continue;	/* Skip blank lines */
5007 		if (buffer[0] == 'N' && buffer[1] == ':') {	/* Got extra parameter specs. This is # of data columns expected beyond the x,y[,z] stuff */
5008 			char flags[GMT_LEN64] = {""};
5009 			nc = sscanf (&buffer[2], "%d %s", &head->n_required, flags);
5010 			head->type = gmt_M_memory (GMT, NULL, head->n_required, unsigned int);
5011 			if (nc == 2) {	/* Got optional types argument */
5012 				if (strlen (flags) != head->n_required) {
5013 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Custom symbol %s has inconsistent N: <npar> [<types>] declaration\n", &name[pos]);
5014 					fclose (fp);
5015 					gmtsupport_free_one_custom_symbol (GMT, head);
5016 					return GMT_PARSE_ERROR;
5017 				}
5018 				for (k = 0; k < head->n_required; k++) {	/* Determine the argument types */
5019 					switch (flags[k]) {
5020 						case 'a':	head->type[k] = GMT_IS_AZIMUTH; break;		/* Azimuth that needs to be converted via the map projection */
5021 						case 'r':	head->type[k] = GMT_IS_ANGLE; break;		/* Angles, we enforce 0-360 range */
5022 						case 'l':	head->type[k] = GMT_IS_DIMENSION; break;	/* Length that will be in the current measure unit */
5023 						case 'o':	head->type[k] = GMT_IS_FLOAT; break;		/* Other, i.e, non-dimensional quantity not to be changed */
5024 						case 's':	head->type[k] = GMT_IS_STRING; n_txt++;	break;		/* A text string */
5025 						default:
5026 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Custom symbol %s has unrecognized <types> declaration in %s\n", &name[pos], flags);
5027 							fclose (fp);
5028 							return GMT_PARSE_ERROR;
5029 							break;
5030 					}
5031 				}
5032 			}
5033 			else {
5034 				for (k = 0; k < head->n_required; k++) head->type[k] = GMT_IS_DIMENSION;	/* Default is lengths */
5035 			}
5036 			continue;
5037 		}
5038 		s = gmt_M_memory (GMT, NULL, 1, struct GMT_CUSTOM_SYMBOL_ITEM);
5039 		if (first) head->first = s;
5040 		first = false;
5041 		for (k = 0; k < 8; k++) col[k][0] = '\0';	/* Reset col array */
5042 		if (strstr (buffer, "if $")) {	/* Parse a logical if-test or elseif here */
5043 			if (strstr (buffer, "} elseif $")) {	/* Actually, it is an elseif-branch [skip { elseif]; nc -=3 means we count the cols only */
5044 				nc = sscanf (buffer, "%*s %*s %s %s %s %*s %s %s %s %s %s %s %s %s", arg[0], OP, right, col[0], col[1], col[2], col[3], col[4], col[5], col[6], col[7]) - 3;
5045 				s->conditional = GMT_BEGIN_ELSEIF;	/* elseif test */
5046 			}
5047 			else {	/* Starting if-branch [skip if] */
5048 				nc = sscanf (buffer, "%*s %s %s %s %*s %s %s %s %s %s %s %s %s", arg[0], OP, right, col[0], col[1], col[2], col[3], col[4], col[5], col[6], col[7]) - 3;
5049 				s->conditional = (col[nc-1][0] == '{') ? GMT_BEGIN_BLOCK_IF : GMT_BEGIN_SINGLE_IF;		/* If block or single if command */
5050 			}
5051 			for (k = 0; right[k]; k++) if (right[k] == ':') right[k] = ' ';	/* Remove any colon in case right side has two range values */
5052 			nv = sscanf (right, "%s %s", arg[1], arg[2]);	/* Get one [or two] constants or variables on right hand side */
5053 			for (k = 0; k < nv + 1; k++) {
5054 				if (arg[k][0] == '$') {	/* Left or right hand side value is a variable */
5055 					s->is_var[k] = true;
5056 					if (arg[k][1] == 'x')	/* Test on x or longitude */
5057 						s->var[k] = GMT_VAR_IS_X;
5058 					else if (arg[k][1] == 'y')	/* Test on y or latitude */
5059 						s->var[k] = GMT_VAR_IS_Y;
5060 					else if (arg[k][1] == 's')	/* Test on symbol size */
5061 						s->var[k] = GMT_VAR_SIZE;
5062 					else if (arg[k][1] == 't') {	/* Test on trailing text or word */
5063 						if (arg[k][2] == '\0')	/* The whole text */
5064 							s->var[k] = GMT_VAR_STRING;
5065 						else	/* Use a single word */
5066 							s->var[k] = GMT_VAR_WORD + atoi (&arg[k][2]);	/* Get the word number $t<varno> plus GMT_VAR_WORD */
5067 					}
5068 					else
5069 						s->var[k] = atoi (&arg[k][1]);	/* Get the variable number $<varno> */
5070 					s->const_val[k] = 0.0;
5071 				}
5072 				else {	/* A constant */
5073 					size_t len = strlen (arg[k]) - 1;
5074 					s->is_var[k] = false;
5075 					if (gmt_not_numeric (GMT, arg[k])) {	/* Got a text item for string comparison */
5076 						size_t len = strlen (arg[k]);
5077 						s->var[k] = GMT_CONST_STRING;
5078 						s->string = gmt_M_memory (GMT, NULL, len + 1, char);
5079 						if (len > 2 && ((arg[k][0] == '\"' && arg[k][len-1] == '\"') || (arg[k][0] == '\'' && arg[k][len-1] == '\'')))	/* Get rid of quotes */
5080 							strncpy (s->string, &arg[k][1], len-2);
5081 						else
5082 							strcpy (s->string, arg[k]);
5083 					}
5084 					else {	/* Numerical value */
5085 						s->var[k] = GMT_CONST_VAR;
5086 						s->const_val[k] = (strchr (GMT_DIM_UNITS, arg[k][len])) ? gmt_M_to_inch (GMT, arg[k]) : atof (arg[k]);	/* A constant, possibly a length unit */
5087 					}
5088 				}
5089 			}
5090 			k = 0;
5091 			if (OP[0] == '!') s->negate = true, k = 1;	/* Reverse any test that follows */
5092 			s->operator = OP[k];	/* Use a 1-char code for operator (<, =, >, etc.); modify below where ambiguous */
5093 			switch (OP[k]) {
5094 				case '<':	/* Less than [Default] */
5095 				 	if (OP[k+1] == '=')	/* Let <= be called operator L */
5096 						s->operator = 'L';
5097 					else if (OP[k+1] == '>')	/* Let <> be called operator i (exclusive in-range not including the boundaries) */
5098 						s->operator = 'i';
5099 					else if (OP[k+1] == ']')	/* Let <] be called operator r (in-range but only include right boundary) */
5100 						s->operator = 'r';
5101 					break;
5102 				case '[':	/* Inclusive range */
5103 				 	if (OP[k+1] == ']')	/* Let [] be called operator I (inclusive range) */
5104 						s->operator = 'I';
5105 					else if (OP[k+1] == '>')	/* Let [> be called operator l (in range but only include left boundary) */
5106 						s->operator = 'l';
5107 					break;
5108 				case '>':	/* Greater than [Default] */
5109 				 	if (OP[k+1] == '=') s->operator = 'G';	/* Let >= be called operator G */
5110 					break;
5111 				case '=':	/* Equal [Default] */
5112 					if (OP[k+1] != '=') GMT_Report (GMT->parent, GMT_MSG_WARNING, "Please use == to indicate equality operator\n");
5113 					if (gmt_M_is_dnan (s->const_val[0])) s->operator = 'E';	/* Let var == NaN be called operator E (equals NaN) */
5114 					break;
5115 			}
5116 		}
5117 		else if (strstr (buffer, "} else {"))	/* End of if-block and start of else-block */
5118 			s->conditional = GMT_END_IF_ELSE;
5119 		else if (strchr (buffer, '}'))		/* End of if-block */
5120 			s->conditional = GMT_END_IF;
5121 		else					/* Regular command, no conditional test prepended */
5122 			nc = sscanf (buffer, "%s %s %s %s %s %s %s %s", col[0], col[1], col[2], col[3], col[4], col[5], col[6], col[7]);
5123 
5124 		last = nc - 1;	/* Index of last col argument */
5125 
5126 		/* Check if command has any trailing -Gfill -Wpen specifiers */
5127 
5128 		do_fill = do_pen = false;
5129 		if (col[last][0] == '-' && col[last][1] == 'G') fill_p = &col[last][2], do_fill = true, last--;
5130 		if (col[last][0] == '-' && col[last][1] == 'W') pen_p = &col[last][2], do_pen = true, last--;
5131 		if (col[last][0] == '-' && col[last][1] == 'G') fill_p = &col[last][2], do_fill = true, last--;	/* Check again for -G since perhaps -G -W was given */
5132 		if (last < 0) error++;
5133 
5134 		s->action = col[last][0];
5135 		if (s->conditional == GMT_END_IF) s->action = '}';	/* The {, }, E, and F are dummy actions */
5136 		if (s->conditional == GMT_END_IF_ELSE) s->action = 'E';
5137 		if (s->conditional == GMT_BEGIN_ELSEIF) s->action = 'F';
5138 		if (last >= 2 && s->conditional < GMT_BEGIN_BLOCK_IF) {	/* OK to parse x,y coordinates */
5139 			s->x = atof (col[GMT_X]);
5140 			s->y = atof (col[GMT_Y]);
5141 		}
5142 		/* Unfortunately, "R" was used for two things: General rotation and Rounded rectangle symbol.  We now use "O" for rotation
5143 		 * but for backwards compatibility we must fix any old-style "R" for rotation here by replacing with "O" */
5144 		if (s->action == 'R' && last == 1)	/* Got the deprecated R for rotate the coordinate system, not R for rounded rectangle */
5145 			s->action = GMT_SYMBOL_ROTATE;
5146 
5147 		switch (s->action) {
5148 
5149 			/* M, D, S, and A allows for arbitrary lines or polygons to be designed - these may be painted or filled with pattern */
5150 
5151 			case GMT_SYMBOL_MOVE:		/* Set a new anchor point */
5152 				if (last != 2) error++;
5153 				break;
5154 
5155 			case GMT_SYMBOL_DRAW:		/* Draw to next point */
5156 				if (last != 2) error++;
5157 				break;
5158 
5159 			case GMT_SYMBOL_STROKE:		/* Stroke current path as line, not polygon */
5160 				if (last != 0) error++;
5161 				break;
5162 
5163 			case GMT_SYMBOL_ARC:		/* Draw arc of a circle */
5164 				if (last != 5) error++;
5165 				s->p[0] = atof (col[2]);
5166 				gmtsupport_decode_arg (col[3], 1, s);	/* angle1 could be a variable or constant degrees */
5167 				gmtsupport_decode_arg (col[4], 2, s);	/* angle2 could be a variable or constant degrees */
5168 				break;
5169 
5170 			case GMT_SYMBOL_ROTATE:		/* Rotate coordinate system about (0,0) */
5171 				if (last != 1) error++;
5172 				s->action = gmtsupport_decode_arg (col[0], 0, s);	/* angle could be a variable or constant heading or azimuth in degrees */
5173 				break;
5174 
5175 			case GMT_SYMBOL_TEXTURE:		/* Texture changes only (modify pen, fill settings) */
5176 				if (last != 0) error++;
5177 				break;
5178 
5179 			case '{':		/* Start of block */
5180 			case '}':		/* End of block */
5181 			case 'E':		/* End of if-block and start of else block */
5182 			case 'F':		/* End of if-block and start of another if block */
5183 				break;	/* Nothing to do but move on */
5184 
5185 			/* These are standard psxy-type symbols */
5186 
5187 			case '?':		/* Any one of these standard types, obtained from last item in data record */
5188 				var_symbol++;
5189 				if (var_symbol == 1) {	/* First time we augment the type array by one to avoid bad memory access in psxy */
5190 					head->type = gmt_M_memory (GMT, head->type, head->n_required+1, unsigned int);
5191 					head->type[head->n_required] = GMT_IS_DIMENSION;	/* It is actually a symbol code but this gets us passed the test */
5192 				}
5193 				/* Intentionally fall through */
5194 			case PSL_STAR:			/* Draw star symbol */
5195 			case PSL_CIRCLE:		/* Draw complete circle */
5196 			case PSL_DIAMOND:		/* Draw diamond symbol */
5197 			case PSL_OCTAGON:		/* Draw octagon symbol */
5198 			case PSL_HEXAGON:		/* Draw hexagon symbol */
5199 			case PSL_INVTRIANGLE:	/* Draw inverted triangle symbol */
5200 			case PSL_PENTAGON:		/* Draw pentagon symbol */
5201 			case PSL_DOT:			/* Draw solid dot */
5202 			case PSL_SQUARE:		/* Draw square symbol */
5203 			case PSL_TRIANGLE:		/* Draw triangle symbol */
5204 			case PSL_CROSS:			/* Draw cross symbol */
5205 			case PSL_YDASH:			/* Draw vertical dash symbol */
5206 			case PSL_PLUS:			/* Draw plus symbol */
5207 			case PSL_XDASH:			/* Draw horizontal dash symbol */
5208 				if (last != 3) error++;
5209 				s->p[0] = atof (col[2]);
5210 				break;
5211 
5212 			case GMT_SYMBOL_TEXT:		/* Draw letter/text symbol [ expect x, y, size, string l] */
5213 				if (last != 4) error++;	/* Did not get the expected arguments */
5214 				s->p[0] = atof (col[2]);	/* Text size is either (1) fixed point size of (2) fractional size relative to the 1x1 box */
5215 				if (col[2][strlen(col[2])-1] == 'p')	/* Gave font size as a fixed point size that will not scale with symbol size */
5216 					s->p[0] = -s->p[0];	/* Mark fixed size via a negative point size */
5217 				s->string = gmt_M_memory (GMT, NULL, strlen (col[3]) + 1, char);
5218 				strcpy (s->string, col[3]);
5219 				if ((c = strchr (s->string, '$'))) {	/* Got a text string variable */
5220 					s->action = GMT_SYMBOL_VARTEXT;
5221 					if (c[1] == 't' && isdigit (c[2]))	/* Select this word from trailing text */
5222 						s->var[0] = atoi (&c[2]);	/* Word number is 0-(n-1) */
5223 				}
5224 				s->font = GMT->current.setting.font_annot[GMT_PRIMARY];	/* Default font for symbols */
5225 				s->justify = PSL_MC;				/* Default justification of text */
5226 				head->text = 1;	/* We will be typesetting text so fonts are required */
5227 				if (s->action == GMT_SYMBOL_VARTEXT && c[1] == 't') head->text = 2;	/* Flag that trailing text will be used */
5228 				k = 1;
5229 				while (col[last][k] && col[last][k] != '+') k++;
5230 				if (col[last][k]) {	/* Gave modifiers */
5231 					unsigned int pos = 0;
5232 					char p[GMT_BUFSIZ] = {""};
5233 					while ((gmt_strtok (&col[last][k], "+", &pos, p))) {	/* Parse any +<modifier> statements */
5234 						switch (p[0]) {
5235 							case 'f':	/* Change font [Note: font size is ignored as the size argument take precedent] */
5236 								if (gmt_getfont (GMT, &p[1], &s->font))
5237 									GMT_Report (GMT->parent, GMT_MSG_WARNING, "Macro code l contains bad +<font> modifier (set to %s)\n", gmt_putfont (GMT, &s->font));
5238 								break;
5239 							case 'j':	s->justify = gmt_just_decode (GMT, &p[1], PSL_NO_DEF);	break;	/* text justification */
5240 							default:
5241 								GMT_Report (GMT->parent, GMT_MSG_ERROR, "Macro code l contains bad modifier +%c\n", p[0]);
5242 								error++;
5243 								break;
5244 						}
5245 					}
5246 				}
5247 				/* Fit in GMT 4 compatibility mode we must check for obsolete %font suffix in the text */
5248 				if (gmt_M_compat_check (GMT, 4) && (c = strchr (s->string, '%')) && !(c[1] == 'X' || c[1] == 'Y')) {	/* Gave font name or number, too, using GMT 4 style */
5249 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Warning in macro l: <string>[%%<font>] is deprecated syntax, use +f<font> instead\n");
5250 					*c = 0;		/* Replace % with the end of string NUL indicator */
5251 					c++;		/* Go to next character */
5252 					if (gmt_getfont (GMT, c, &s->font)) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Custom symbol subcommand l contains bad GMT4-style font information (set to %s)\n", gmt_putfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]));
5253 				}
5254 
5255 				break;
5256 
5257 			case PSL_RECT:		/* Draw rect symbol */
5258 				if (last != 4) error++;
5259 				s->p[0] = atof (col[2]);
5260 				s->p[1] = atof (col[3]);
5261 				break;
5262 
5263 			case PSL_RNDRECT:		/* Draw rounded rect symbol */
5264 				if (last != 5) error++;
5265 				s->p[0] = atof (col[2]);
5266 				s->p[1] = atof (col[3]);
5267 				s->p[2] = atof (col[4]);
5268 				break;
5269 
5270 			case PSL_ELLIPSE:		/* Draw ellipse symbol */
5271 			case PSL_ROTRECT:		/* Draw rotated rect symbol */
5272 				if (last != 5) error++;
5273 				gmtsupport_decode_arg (col[2], 0, s);	/* angle could be a variable or constant degrees */
5274 				s->p[1] = atof (col[3]);
5275 				s->p[2] = atof (col[4]);
5276 				break;
5277 
5278 			case PSL_MARC:		/* Draw mathangle symbol */
5279 			case PSL_WEDGE:		/* Draw wedge (pie) symbol */
5280 				if (last != 5) error++;
5281 				s->p[0] = atof (col[2]);
5282 				gmtsupport_decode_arg (col[3], 1, s);	/* angle1 could be a variable or constant degrees */
5283 				gmtsupport_decode_arg (col[4], 2, s);	/* angle2 could be a variable or constant degrees */
5284 				break;
5285 
5286 			case PSL_VECTOR:		/* Draw a vector symbol. Vector head is hardwired and scales with pen thickness */
5287 				if (last != 4) error++;
5288 				gmtsupport_decode_arg (col[2], 0, s);	/* angle could be a variable or constant degrees */
5289 				s->p[1] = atof (col[3]);	/* Non-dimensional length */
5290 				break;
5291 
5292 			case GMT_SYMBOL_EPS:		/* Place EPS file */
5293 				if (last != 4) error++;
5294 				s->p[0] = atof (col[2]);
5295 				if ((type = gmt_locate_custom_symbol (GMT, col[3], name, path, &pos)) == 0) return GMT_RUNTIME_ERROR;
5296 				if ((s->eps = gmtsupport_load_eps_symbol (GMT, name, path)) == NULL) return GMT_RUNTIME_ERROR;
5297 				break;
5298 
5299 			default:
5300 				error++;
5301 				break;
5302 		}
5303 
5304 		if (error) {
5305 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to parse symbol commands in file %s\n", path);
5306 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Offending line: %s\n", buffer);
5307 			gmt_M_free (GMT, head);
5308 			fclose (fp);
5309 			return GMT_PARSE_ERROR;
5310 		}
5311 
5312 		if (do_fill) {	/* Update current fill */
5313 			s->fill = gmt_M_memory (GMT, NULL, 1, struct GMT_FILL);
5314 			if ((c = strstr (fill_p, "+p")))	/* Want to replace this fills's color with that of the current pen color */
5315 				s->var_pen -= GMT_USE_PEN_RGB;	/* Flag so we can replace fill color at run-time */
5316 			else if (fill_p[0] == '-')	/* Do not want to fill this polygon */
5317 				s->fill->rgb[0] = -1.0;
5318 			else if (gmt_getfill (GMT, fill_p, s->fill)) {
5319 				gmt_fill_syntax (GMT, 'G', NULL, " ");
5320 				fclose (fp);
5321 				gmt_M_free (GMT, head);
5322 				return GMT_PARSE_ERROR;
5323 			}
5324 		}
5325 		else
5326 			s->fill = NULL;
5327 		if (do_pen) {	/* Update current pen */
5328 			s->pen = gmt_M_memory (GMT, NULL, 1, struct GMT_PEN);
5329 			if (pen_p[0] == '-')	/* Do not want to draw outline */
5330 				s->pen->rgb[0] = -1.0;
5331 			else {	/* Pen of some sort */
5332 				bool p_normal = false;
5333 				if ((c = strstr (pen_p, "+g"))) {	/* Want to replace this pen's color with that of the current fill color */
5334 					s->var_pen -= GMT_USE_FILL_RGB;	/* Flag so we can replace pen color at run-time */
5335 					c[0] = '\0';	/* Chop off the "g+" suffix */
5336 				}
5337 				if (pen_p[0] == '$')	{	/* Variable pen thickness obtained at run-time via data column */
5338 					unsigned int k = 0;
5339 					s->var_pen = atoi (&pen_p[1]);	/* Remember variable column number */
5340 					/* Replace ${var} by "01" (number of zeros depends on size of var), scale pen by the indicated variable later */
5341 					while (pen_p[k] && pen_p[k] != ',')
5342 						pen_p[k++] = '0';
5343 					if (k) pen_p[k-1] = '1';	/* Now we have a unit pen thickness for later scaling */
5344 				}
5345 				if (gmt_getpen (GMT, pen_p, s->pen)) {
5346 					gmt_pen_syntax (GMT, 'W', NULL, " ", NULL, 0);
5347 					fclose (fp);
5348 					return GMT_PARSE_ERROR;
5349 				}
5350 				if ((c = strchr (pen_p, ','))) c[0] = '\0';	/* Chop off anything after pen width so we can check for pen units */
5351 				if (strchr (pen_p, 'c') == NULL && strchr (pen_p, 'i') == NULL && strchr (pen_p, 'p') == NULL) {
5352 					/* No unit means normalized pen thickness in 0-1 range to be scaled by symbol size later */
5353 					p_normal = true;
5354 				}
5355 				if (p_normal) s->pen->width = -s->pen->width;	/* Negative pen means normalized 0-1 */
5356 				if (c) c[0] = ',';	/* Restore the pen argument */
5357 			}
5358 		}
5359 		else
5360 			s->pen = NULL;
5361 
5362 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Code %c Conditional = %d OP = %c negate = %d var = %d/%d/%d do_pen = %d do_fill = %d\n", \
5363 			(int)s->action, s->conditional, (int)s->operator, s->negate, s->var[0], s->var[1], s->var[2], do_pen, do_fill);
5364 		if (previous) previous->next = s;
5365 		previous = s;
5366 	}
5367 	fclose (fp);
5368 	head->n_required -= n_txt;	/* Words from trailing text is not part of required arguments (which are all numerical) */
5369 	*S = head;
5370 	return (GMT_NOERROR);
5371 }
5372 
5373 /*! . */
gmtsupport_polygon_area(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n)5374 GMT_LOCAL double gmtsupport_polygon_area (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n) {
5375 	uint64_t i, last;
5376 	double area, xold, yold;
5377 
5378 	/* Sign will be +ve if polygon is CW, negative if CCW */
5379 
5380 	last = (n > 2 && gmt_polygon_is_open (GMT, x, y, n)) ? n : n - 1;	/* Skip repeating vertex */
5381 
5382 	area = yold = 0.0;
5383 	xold = x[last-1];
5384 	yold = y[last-1];
5385 
5386 	for (i = 0; i < last; i++) {
5387 		area += (xold - x[i]) * (yold + y[i]);
5388 		xold = x[i];
5389 		yold = y[i];
5390 	}
5391 	return (0.5 * area);
5392 }
5393 
5394 #if 0
5395 GMT_LOCAL void gmtsupport_dataset_detrend (struct GMT_CTRL *GMT, struct GMT_DATASET *D, unsigned int mode, double *coeff) {
5396 	/* Will detrend the x [and y if not NULL] columns separately. */
5397 	unsigned id = 0, tbl, col, n_cols;
5398 	uint64_t row, seg;
5399 	double sumt, sumt2, sumx, sumtx, xmin, xmax, t, t_factor;
5400 	struct GMT_DATASEGMENT *S = NULL;
5401 
5402 	/* mode = 0 (GMT_FFT_REMOVE_NOTHING):  Do nothing.
5403 	   mode = 1 (GMT_FFT_REMOVE_MEAN):  Remove the mean value (returned via coeff[0], coeff[2])
5404 	   mode = 2 (GMT_FFT_REMOVE_MID):   Remove the mid value value (returned via coeff[0], coeff[2])
5405 	   mode = 3 (GMT_FFT_REMOVE_TREND): Remove the best-fitting line by least squares (returned via coeff[0-4])
5406 	*/
5407 	if (mode == GMT_FFT_REMOVE_NOTHING) {	/* Do nothing */
5408 		GMT_Report (API, GMT_MSG_WARNING, "No detrending selected\n");
5409 		return;
5410 	}
5411 	t_factor =  2.0 / (n - 1);
5412 	for (tbl = seg_no = 0; tbl < D->n_tables; tbl++) {
5413 		for (seg = 0; seg < D->n_segments; seg++) {	/* For each segment to modify */
5414 			S = D->table[tbl]->segment[seg];
5415 			for (col = 1; col < n_cols; col++) {
5416 				sumt = sumt2 = sumx = sumtx = 0.0;
5417 				xmin = DBL_MAX, xmax = -DBL_MAX;
5418 				for (row = 0; row < S->n_rows; row++) {
5419 					t = row * t_factor - 1.0;
5420 					sumt += t;
5421 					sumt2 += (t * t);
5422 					sumx += S->data[col][row];
5423 					sumtx += (t * S->data[col][row]);
5424 					if (S->data[col][row] < xmin) xmin = S->data[col][row];
5425 					if (S->data[col][row] > xmax) xmax = S->data[col][row];
5426 				}
5427 				id = 2 * (col - 1);
5428 				coeff[id] = (mode == GMT_FFT_REMOVE_MID) ? 0.5 * (xmin + xmax) : sumx / S->n_rows;
5429 				coeff[id+1] = (mode == GMT_FFT_REMOVE_TREND) ? sumtx / sumt2 : 0.0;
5430 				/* Remove the desired trend */
5431 				for (row = 0; row < S->n_rows; row++) {
5432 					t = row * t_factor - 1.0;
5433 					S->data[col][row] -= (coeff[id] + t * coeff[id+1]);
5434 				}
5435 			}
5436 		}
5437 	}
5438 }
5439 
5440 GMT_LOCAL void gmtsupport_cols_detrend (struct GMT_CTRL *GMT, double *t, double *x, double *y, uint64_t n, unsigned int mode, double *coeff) {
5441 	/* Will detrend the x [and y if not NULL] columns separately. */
5442 	unsigned id = 0, tbl, col, n_cols;
5443 	uint64_t row, seg;
5444 	double sumt, sumt2, sumx, sumtx, xmin, xmax, t, t_factor, *C[2] = {NULL, NULL};
5445 	struct GMT_DATASEGMENT *S = NULL;
5446 
5447 	/* mode = 0 (GMT_FFT_REMOVE_NOTHING):  Do nothing.
5448 	   mode = 1 (GMT_FFT_REMOVE_MEAN):  Remove the mean value (returned via coeff[0], coeff[2])
5449 	   mode = 2 (GMT_FFT_REMOVE_MID):   Remove the mid value value (returned via coeff[0], coeff[2])
5450 	   mode = 3 (GMT_FFT_REMOVE_TREND): Remove the best-fitting line by least squares (returned via coeff[0-4])
5451 	*/
5452 	if (mode == GMT_FFT_REMOVE_NOTHING) {	/* Do nothing */
5453 		GMT_Report (API, GMT_MSG_WARNING, "No detrending selected\n");
5454 		return;
5455 	}
5456 	t_factor =  2.0 / (n - 1);
5457 	n_cols = (y == NULL) ? 1 : 2;
5458 	C[0] = x;	C[1] = y;	/* So we can loop over these columns */
5459 	for (col = 0; col < n_cols; col++) {
5460 		sumt = sumt2 = sumx = sumtx = 0.0;
5461 		xmin = DBL_MAX, xmax = -DBL_MAX;
5462 		for (row = 0; row < S->n_rows; row++) {
5463 			t = row * t_factor - 1.0;
5464 			sumt += t;
5465 			sumt2 += (t * t);
5466 			sumx += C[col][row];
5467 			sumtx += (t * C[col][row]);
5468 			if (C[col][row] < xmin) xmin = C[col][row];
5469 			if (C[col][row] > xmax) xmax = C[col][row];
5470 		}
5471 		id = 2 * (col - 1);
5472 		coeff[id] = (mode == GMT_FFT_REMOVE_MID) ? 0.5 * (xmin + xmax) : sumx / S->n_rows;
5473 		coeff[id+1] = (mode == GMT_FFT_REMOVE_TREND) ? sumtx / sumt2 : 0.0;
5474 		/* Remove the desired trend */
5475 		for (row = 0; row < S->n_rows; row++) {
5476 			t = row * t_factor - 1.0;
5477 			C[col][row] -= (coeff[id] + t * coeff[id+1]);
5478 		}
5479 	}
5480 }
5481 #endif
5482 
5483 #define SEG_DIST 2
5484 #define SEG_AZIM 3
5485 
5486 /*! . */
gmtsupport_resample_data_spherical(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,double along_ds,unsigned int mode,unsigned int ex_cols,enum GMT_enum_track smode)5487 GMT_LOCAL struct GMT_DATASET * gmtsupport_resample_data_spherical (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, double along_ds, unsigned int mode, unsigned int ex_cols, enum GMT_enum_track smode) {
5488 	/* Spherical version; see gmt_resample_data for details */
5489 	int ndig;
5490 	bool resample;
5491 	uint64_t tbl, col, n_cols;
5492 	uint64_t row, seg, seg_no;
5493 	char buffer[GMT_BUFSIZ] = {""}, ID[GMT_BUFSIZ] = {""};
5494 	double along_dist, azimuth, dist_inc;
5495 	struct GMT_DATASET *D = NULL;
5496 	struct GMT_DATATABLE *Tin = NULL, *Tout = NULL;
5497 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
5498 
5499 	resample = (!gmt_M_is_zero(along_ds));
5500 	n_cols = 2 + mode + ex_cols;
5501 	D = gmt_alloc_dataset (GMT, Din, 0, n_cols, GMT_ALLOC_NORMAL);	/* Same table length as Din, but with up to 4 columns (lon, lat, dist, az) */
5502 	ndig = irint (floor (log10 ((double)Din->n_segments))) + 1;	/* Determine how many decimals are needed for largest segment id */
5503 
5504 	for (tbl = seg_no = 0; tbl < Din->n_tables; tbl++) {
5505 		Tin  = Din->table[tbl];
5506 		Tout = D->table[tbl];
5507 		for (seg = Tout->n_records = 0; seg < Tin->n_segments; seg++, seg_no++) {	/* For each segment to resample */
5508 			gmt_M_memcpy (Tout->segment[seg]->data[GMT_X], Tin->segment[seg]->data[GMT_X], Tin->segment[seg]->n_rows, double);	/* Duplicate longitudes */
5509 			gmt_M_memcpy (Tout->segment[seg]->data[GMT_Y], Tin->segment[seg]->data[GMT_Y], Tin->segment[seg]->n_rows, double);	/* Duplicate latitudes */
5510 			SH = gmt_get_DS_hidden (Tout->segment[seg]);
5511 			/* Resample lines as per smode */
5512 			if (resample) {	/* Resample lon/lat path and also reallocate more space for all other columns */
5513 				Tout->segment[seg]->n_rows = SH->n_alloc = gmt_resample_path (GMT, &Tout->segment[seg]->data[GMT_X], &Tout->segment[seg]->data[GMT_Y], Tout->segment[seg]->n_rows, along_ds, smode);
5514 				for (col = 2; col < n_cols; col++)	/* Also realloc the other columns */
5515 					Tout->segment[seg]->data[col] = gmt_M_memory (GMT, Tout->segment[seg]->data[col], Tout->segment[seg]->n_rows, double);
5516 			}
5517 			Tout->n_records += Tout->segment[seg]->n_rows;	/* Update record count */
5518 			if (mode == 0) continue;	/* No dist/az needed */
5519 			for (row = 0, along_dist = 0.0; row < Tout->segment[seg]->n_rows; row++) {	/* Process each point along resampled FZ trace */
5520 				dist_inc = (row) ? gmt_distance (GMT, Tout->segment[seg]->data[GMT_X][row], Tout->segment[seg]->data[GMT_Y][row], Tout->segment[seg]->data[GMT_X][row-1], Tout->segment[seg]->data[GMT_Y][row-1]) : 0.0;
5521 				along_dist += dist_inc;
5522 				Tout->segment[seg]->data[SEG_DIST][row] = along_dist;
5523 				if (mode == 1) continue;	/* No az needed */
5524 				if (row)
5525 					azimuth = gmt_az_backaz (GMT, Tout->segment[seg]->data[GMT_X][row-1], Tout->segment[seg]->data[GMT_Y][row-1], Tout->segment[seg]->data[GMT_X][row], Tout->segment[seg]->data[GMT_Y][row], false);
5526 				else	/* Special deal for first point */
5527 					azimuth = gmt_az_backaz (GMT, Tout->segment[seg]->data[GMT_X][0], Tout->segment[seg]->data[GMT_Y][0], Tout->segment[seg]->data[GMT_X][1], Tout->segment[seg]->data[GMT_Y][1], false);
5528 				Tout->segment[seg]->data[SEG_AZIM][row] = azimuth;
5529 			}
5530 			ID[0] = 0;
5531 			if (Tout->segment[seg]->label) strncpy (ID, Tout->segment[seg]->label, GMT_BUFSIZ-1);	/* Look for label in header */
5532 			else if (Tout->segment[seg]->header) gmt_parse_segment_item (GMT, Tout->segment[seg]->header, "-L", ID);	/* Look for label in header */
5533 			if (!ID[0]) snprintf (ID, GMT_BUFSIZ, "%*.*" PRIu64, ndig, ndig, seg_no);	/* Must assign a label from running numbers */
5534 			if (!Tout->segment[seg]->label) Tout->segment[seg]->label = strdup (ID);
5535 			gmt_M_str_free (Tout->segment[seg]->header);
5536 			snprintf (buffer, GMT_BUFSIZ, "Segment label -L%s", ID);
5537 			Tout->segment[seg]->header = strdup (buffer);
5538 		}
5539 	}
5540 	return (D);
5541 }
5542 
5543 /*! . */
gmtsupport_resample_data_cartesian(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,double along_ds,unsigned int mode,unsigned int ex_cols,enum GMT_enum_track smode)5544 GMT_LOCAL struct GMT_DATASET * gmtsupport_resample_data_cartesian (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, double along_ds, unsigned int mode, unsigned int ex_cols, enum GMT_enum_track smode) {
5545 	/* Cartesian version; see gmt_resample_data for details */
5546 
5547 	int ndig;
5548 	bool resample;
5549 	uint64_t tbl, col, n_cols;
5550 	uint64_t row, seg, seg_no;
5551 	char buffer[GMT_BUFSIZ] = {""}, ID[GMT_BUFSIZ] = {""};
5552 	double along_dist, azimuth, dist_inc;
5553 	struct GMT_DATASET *D = NULL;
5554 	struct GMT_DATATABLE *Tin = NULL, *Tout = NULL;
5555 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
5556 
5557 	resample = (!gmt_M_is_zero(along_ds));
5558 	n_cols = 2 + mode + ex_cols;
5559 	D = gmt_alloc_dataset (GMT, Din, 0, n_cols, GMT_ALLOC_NORMAL);	/* Same table length as Din, but with up to 4 columns (lon, lat, dist, az) */
5560 	ndig = irint (floor (log10 ((double)Din->n_segments))) + 1;	/* Determine how many decimals are needed for largest segment id */
5561 
5562 	for (tbl = seg_no = 0; tbl < Din->n_tables; tbl++) {
5563 		Tin  = Din->table[tbl];
5564 		Tout = D->table[tbl];
5565 		for (seg = Tout->n_records = 0; seg < Tin->n_segments; seg++, seg_no++) {	/* For each segment to resample */
5566 			gmt_M_memcpy (Tout->segment[seg]->data[GMT_X], Tin->segment[seg]->data[GMT_X], Tin->segment[seg]->n_rows, double);	/* Duplicate x */
5567 			gmt_M_memcpy (Tout->segment[seg]->data[GMT_Y], Tin->segment[seg]->data[GMT_Y], Tin->segment[seg]->n_rows, double);	/* Duplicate y */
5568 			SH = gmt_get_DS_hidden (Tout->segment[seg]);
5569 			/* Resample lines as per smode */
5570 			if (resample) {	/* Resample x/y path and also reallocate more space for all other columns */
5571 				Tout->segment[seg]->n_rows = SH->n_alloc = gmt_resample_path (GMT, &Tout->segment[seg]->data[GMT_X], &Tout->segment[seg]->data[GMT_Y], Tout->segment[seg]->n_rows, along_ds, smode);
5572 				for (col = 2; col < n_cols; col++)	/* Also realloc the other columns */
5573 					Tout->segment[seg]->data[col] = gmt_M_memory (GMT, Tout->segment[seg]->data[col], Tout->segment[seg]->n_rows, double);
5574 			}
5575 			Tout->n_records += Tout->segment[seg]->n_rows;	/* Update record count */
5576 			if (mode == 0) continue;	/* No dist/az needed */
5577 			for (row = 0, along_dist = 0.0; row < Tout->segment[seg]->n_rows; row++) {	/* Process each point along resampled FZ trace */
5578 				dist_inc = (row) ? gmt_distance (GMT, Tout->segment[seg]->data[GMT_X][row], Tout->segment[seg]->data[GMT_Y][row], Tout->segment[seg]->data[GMT_X][row-1], Tout->segment[seg]->data[GMT_Y][row-1]) : 0.0;
5579 				along_dist += dist_inc;
5580 				Tout->segment[seg]->data[SEG_DIST][row] = along_dist;
5581 				if (mode == 1) continue;	/* No az needed */
5582 				if (row)
5583 					azimuth = gmt_az_backaz (GMT, Tout->segment[seg]->data[GMT_X][row-1], Tout->segment[seg]->data[GMT_Y][row-1], Tout->segment[seg]->data[GMT_X][row], Tout->segment[seg]->data[GMT_Y][row], false);
5584 				else	/* Special deal for first point */
5585 					azimuth = gmt_az_backaz (GMT, Tout->segment[seg]->data[GMT_X][0], Tout->segment[seg]->data[GMT_Y][0], Tout->segment[seg]->data[GMT_X][1], Tout->segment[seg]->data[GMT_Y][1], false);
5586 				Tout->segment[seg]->data[SEG_AZIM][row] = azimuth;
5587 			}
5588 			ID[0] = 0;
5589 			if (Tout->segment[seg]->label) strncpy (ID, Tout->segment[seg]->label, GMT_BUFSIZ-1);	/* Look for label in header */
5590 			else if (Tout->segment[seg]->header) gmt_parse_segment_item (GMT, Tout->segment[seg]->header, "-L", ID);	/* Look for label in header */
5591 			if (!ID[0]) snprintf (ID, GMT_BUFSIZ, "%*.*" PRIu64, ndig, ndig, seg_no);	/* Must assign a label from running numbers */
5592 			if (!Tout->segment[seg]->label) Tout->segment[seg]->label = strdup (ID);
5593 			gmt_M_str_free (Tout->segment[seg]->header);
5594 			snprintf (buffer, GMT_BUFSIZ, "Segment label -L%s", ID);
5595 			Tout->segment[seg]->header = strdup (buffer);
5596 		}
5597 	}
5598 	return (D);
5599 
5600 }
5601 
5602 /*! . */
gmtsupport_crosstracks_spherical(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,double cross_length,double across_ds,double deviation,uint64_t n_cols,unsigned int mode,char unit)5603 GMT_LOCAL struct GMT_DATASET * gmtsupport_crosstracks_spherical (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, double cross_length, double across_ds, double deviation, uint64_t n_cols, unsigned int mode, char unit) {
5604 	/* Din is a data set with at least two columns (lon/lat);
5605 	 * it can contain any number of tables and segments.
5606 	 * cross_length is the desired length of cross-profiles, in meters.
5607 	 * across_ds is the sampling interval to use along the cross-profiles.
5608 	 * n_cols sets how many data columns beyond x,y,d should be allocated.
5609 	 * Dout is the new data set with all the crossing profiles; it will
5610 	 * have 4 + n_cols columns, where the first 4 are x,y,d,az.
5611 	 */
5612 
5613 	int k, ndig, sdig, n_cross, n_half_cross, k_start, k_stop;
5614 
5615 	unsigned int ii, np_cross;
5616 
5617 	uint64_t dim[GMT_DIM_SIZE] = {0, 0, 0, 0};
5618 
5619 	uint64_t tbl, row, left, right, seg, seg_no, n_tot_cols;
5620 	size_t n_x_seg = 0, n_x_seg_alloc = 0;
5621 
5622 	bool skip_seg_no, set_fixed_azim = false;
5623 
5624 	char buffer[GMT_BUFSIZ] = {""}, seg_name[GMT_BUFSIZ] = {""}, ID[GMT_BUFSIZ] = {""};
5625 
5626 	double dist_inc, d_shift, orientation, sign = 1.0, az_cross, x, y, fixed_azim, dist_to_end;
5627 	double dist_across_seg, angle_radians, across_ds_radians, ds_phase = 0.0, n_cross_float;
5628 	double Rot[3][3], Rot0[3][3], E[3], P[3], L[3], R[3], T[3], X[3];
5629 
5630 	struct GMT_DATASET *Xout = NULL;
5631 	struct GMT_DATATABLE *Tin = NULL, *Tout = NULL;
5632 	struct GMT_DATASEGMENT *S = NULL;
5633 
5634 	if (Din->n_columns < 2) {	/* Trouble */
5635 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Dataset does not have at least 2 columns with coordinates\n");
5636 		return (NULL);
5637 	}
5638 
5639 	if (mode & GMT_ALTERNATE) sign = -1.0;	/* Survey layout */
5640 	if (mode & GMT_FIXED_AZIM) {	/* Got fixed azimuth */
5641 		fixed_azim = deviation;
5642 		deviation = 0.0;	/* To prevent any deviation below */
5643 		set_fixed_azim = true;
5644 	}
5645 
5646 	/* Get resampling step size and zone width in degrees */
5647 
5648 	n_cross_float = cross_length / across_ds;	/* Number of points in a cross profile may be a fraction if badly chosen increments */
5649 	n_cross = irint (n_cross_float);	/* Number of integer points in a cross profile */
5650 	if (fabs (n_cross_float - n_cross) > (0.05 * across_ds))	/* Use a 5% slop */
5651 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your crossprofile length (%g) is not an integer multiple of your along-profile-spacing (%g); %g will be rounded to nearest integer %d\n", cross_length, across_ds, n_cross_float, n_cross);
5652 	cross_length = cross_length / GMT->current.map.dist[GMT_MAP_DIST].scale;	/* Now in meters [or degrees] */
5653 	n_cross++;	/* Since one more node than increments */
5654 	n_half_cross = (n_cross % 2) ? (n_cross - 1) / 2 : n_cross / 2;	/* Half-width of points in a cross profile depending on odd/even */
5655 	if (unit && strchr (GMT_ARC_UNITS, unit)) {	/* Gave increments in arc units (already in degrees at this point) */
5656 		across_ds_radians = D2R * cross_length / (n_cross - 1);	/* Angular change from point to point */
5657 		dist_to_end = 0.5 * cross_length;	/* Distance from center to end of profile in degrees */
5658 	}
5659 	else {	/* Must convert distances to degrees */
5660 		dist_to_end = 0.5 * cross_length / GMT->current.proj.DIST_M_PR_DEG;		/* Distance from center to end of profile in degrees */
5661 		across_ds_radians = D2R * (cross_length / GMT->current.proj.DIST_M_PR_DEG) / (n_cross - 1);	/* Angular change from point to point */
5662 	}
5663 	if ((n_cross % 2) == 0) ds_phase = 0.5;
5664 	k_start = -n_half_cross;
5665 	k_stop = k_start + n_cross - 1;
5666 	if (mode & GMT_LEFT_ONLY)	/* Only want left side of profiles */
5667 		k_stop = 0;
5668 	else if (mode & GMT_RIGHT_ONLY)	/* Only want right side of profiles */
5669 		k_start = 0;
5670 	np_cross = k_stop - k_start + 1;/* Total cross-profile length */
5671 
5672 	n_tot_cols = 4 + n_cols;	/* Total number of columns in the resulting data set */
5673 	dim[GMT_TBL] = Din->n_tables;	dim[GMT_COL] = n_tot_cols;	dim[GMT_ROW] = np_cross;
5674 	if ((Xout = GMT_Create_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_LINE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) return (NULL);	/* An empty dataset of n_tot_cols columns and np_cross rows */
5675 	sdig = irint (floor (log10 ((double)Din->n_segments))) + 1;	/* Determine how many decimals are needed for largest segment id */
5676 	skip_seg_no = (Din->n_tables == 1 && Din->table[0]->n_segments == 1);
5677 	for (tbl = seg_no = 0; tbl < Din->n_tables; tbl++) {	/* Process all tables */
5678 		Tin  = Din->table[tbl];
5679 		Tout = Xout->table[tbl];
5680 		for (seg = 0; seg < Tin->n_segments; seg++, seg_no++) {	/* Process all segments */
5681 
5682 			ndig = irint (floor (log10 ((double)Tin->segment[seg]->n_rows))) + 1;	/* Determine how many decimals are needed for largest id */
5683 
5684 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Process Segment %s [segment %" PRIu64 "] which has %" PRIu64 " crossing profiles\n", Tin->segment[seg]->label, seg, Tin->segment[seg]->n_rows);
5685 
5686 			/* Resample control point track along great circle paths using specified sampling interval */
5687 
5688 			for (row = 0; row < Tin->segment[seg]->n_rows; row++) {	/* Process each point along segment */
5689 				/* Compute segment line orientation (-90/90) from azimuths */
5690 				orientation = 0.5 * fmod (2.0 * Tin->segment[seg]->data[SEG_AZIM][row], 360.0);
5691 				if (orientation > 90.0) orientation -= 180.0;
5692 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Working on cross profile %" PRIu64 " [local line orientation = %06.1f]\n", row, orientation);
5693 
5694 				x = Tin->segment[seg]->data[GMT_X][row];	y = Tin->segment[seg]->data[GMT_Y][row];	/* Reset since now we want lon/lat regardless of grid format */
5695 				gmt_geo_to_cart (GMT, y, x, P, true);		/* 3-D vector of current point P */
5696 				if (set_fixed_azim) {	/* Need 2nd point in azim direction to cross with P */
5697 					double b_x, b_y;
5698 					gmt_translate_point (GMT, x, y, fixed_azim, dist_to_end, &b_x, &b_y, NULL);
5699 					gmt_geo_to_cart (GMT, b_y, b_x, R, true);		/* 3-D vector of end point R */
5700 					gmt_cross3v (GMT, P, R, E);			/* Get pole E to plane trough P and R */
5701 				}
5702 				else {	/* Erect an orthogonal (- deviation) profile */
5703 					left = (row) ? row - 1 : row;			/* Left point (if there is one) */
5704 					x = Tin->segment[seg]->data[GMT_X][left];	y = Tin->segment[seg]->data[GMT_Y][left];
5705 					gmt_geo_to_cart (GMT, y, x, L, true);		/* 3-D vector of left point L */
5706 					right = (row < (Tin->segment[seg]->n_rows-1)) ? row + 1 : row;	/* Right point (if there is one) */
5707 					x = Tin->segment[seg]->data[GMT_X][right];	y = Tin->segment[seg]->data[GMT_Y][right];
5708 					gmt_geo_to_cart (GMT, y, x, R, true);		/* 3-D vector of right point R */
5709 					gmt_cross3v (GMT, L, R, T);			/* Get pole T of plane trough L and R (and center of Earth) */
5710 					gmt_normalize3v (GMT, T);			/* Make sure T has unit length */
5711 					gmt_cross3v (GMT, T, P, E);			/* Get pole E to plane trough P normal to L,R (hence going trough P) */
5712 				}
5713 				gmt_normalize3v (GMT, E);			/* Make sure E has unit length */
5714 				if (!gmt_M_is_zero (deviation)) {	/* Must now rotate E about P by the deviation first */
5715 					gmtlib_load_rot_matrix (-D2R * deviation, Rot0, P);	/* Negative so that rotation goes clockwise */
5716 					gmt_matrix_vect_mult (GMT, 3U, Rot0, E, X);				/* Rotate E about P by deviation */
5717 					gmt_M_memcpy (E, X, 3u, double);	/* Copy the result to E */
5718 					gmt_normalize3v (GMT, E);			/* Make sure E has unit length */
5719 				}
5720 				gmtlib_init_rot_matrix (Rot0, E);			/* Get partial rotation matrix since no actual angle is applied yet */
5721 				az_cross = fmod (Tin->segment[seg]->data[SEG_AZIM][row] + 270.0, 360.0);	/* Azimuth of cross-profile in 0-360 range */
5722 				if (mode & GMT_ALTERNATE)
5723 					sign = -sign;
5724 				else if (mode & GMT_EW_SN)
5725 					sign = (az_cross >= 315.0 || az_cross < 135.0) ? -1.0 : 1.0;	/* We want profiles to be either ~E-W or ~S-N */
5726 				dist_across_seg = 0.0;
5727 				S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, np_cross, n_tot_cols, NULL, NULL);
5728 				for (k = k_start, ii = 0; k <= k_stop; k++, ii++) {	/* For each point along normal to FZ */
5729 					angle_radians = sign * (k + ds_phase) * across_ds_radians;		/* The required rotation for this point relative to FZ origin */
5730 					gmt_M_memcpy (Rot, Rot0, 9, double);			/* Get a copy of the "0-angle" rotation matrix */
5731 					gmtlib_load_rot_matrix (angle_radians, Rot, E);		/* Build the actual rotation matrix for this angle */
5732 					gmt_matrix_vect_mult (GMT, 3U, Rot, P, X);				/* Rotate the current FZ point along the normal */
5733 					gmt_cart_to_geo (GMT, &S->data[GMT_Y][ii], &S->data[GMT_X][ii], X, true);	/* Get lon/lat of this point along crossing profile */
5734 					dist_inc = (ii) ? gmt_distance (GMT, S->data[GMT_X][ii], S->data[GMT_Y][ii], S->data[GMT_X][ii-1], S->data[GMT_Y][ii-1]) : 0.0;
5735 					dist_across_seg += dist_inc;
5736 					S->data[SEG_DIST][ii] = dist_across_seg;	/* Store distances across the profile */
5737 					if (ii) S->data[SEG_AZIM][ii] = gmt_az_backaz (GMT, S->data[GMT_X][ii-1], S->data[GMT_Y][ii-1], S->data[GMT_X][ii], S->data[GMT_Y][ii], false);
5738 				}
5739 				S->data[SEG_AZIM][0] = gmt_az_backaz (GMT, S->data[GMT_X][0], S->data[GMT_Y][0], S->data[GMT_X][1], S->data[GMT_Y][1], false);	/* Special deal for first point */
5740 				S->n_rows = ii;
5741 				/* Reset distance origin for cross profile */
5742 
5743 				d_shift = S->data[SEG_DIST][n_half_cross];	/* d_shift is here the distance at the center point (i.e., where crossing the guide FZ) */
5744 				for (ii = 0; ii < np_cross; ii++) S->data[SEG_DIST][ii] -= d_shift;	/* We reset the origin for distances to where this profile crosses the trial FZ */
5745 
5746 				orientation = 0.5 * fmod (2.0 * (Tin->segment[seg]->data[SEG_AZIM][row]+90.0), 360.0);	/* Orientation of cross-profile at zero distance */
5747 				if (orientation > 90.0) orientation -= 180.0;
5748 				ID[0] = seg_name[0] = 0;
5749 				if (Tin->segment[seg]->label) {	/* Use old segment label and append crossprofile number */
5750 					snprintf (ID, GMT_BUFSIZ, "%s-%*.*" PRIu64, Tin->segment[seg]->label, ndig, ndig, row);
5751 				}
5752 				else if (Tin->segment[seg]->header) {	/* Look for label in header */
5753 					gmt_parse_segment_item (GMT, Tin->segment[seg]->header, "-L", seg_name);
5754 					if (seg_name[0]) snprintf (ID, GMT_BUFSIZ, "%s-%*.*" PRIu64, seg_name, ndig, ndig, row);
5755 				}
5756 				if (!ID[0]) {	/* Must assign a label from running numbers */
5757 					if (skip_seg_no)	/* Single track, just list cross-profile no */
5758 						snprintf (ID, GMT_BUFSIZ, "%*.*" PRIu64, ndig, ndig, row);
5759 					else	/* Segment number and cross-profile no */
5760 						snprintf (ID, GMT_BUFSIZ, "%*.*" PRIu64 "-%*.*" PRIu64, sdig, sdig, seg_no, ndig, ndig, row);
5761 				}
5762 				S->label = strdup (ID);
5763 				if (strchr (ID, ' ')) {	/* Has spaces */
5764 					char tmp[GMT_BUFSIZ] = {""};
5765 					snprintf (tmp, GMT_BUFSIZ, "\"%s\"", ID);
5766 					strcpy (ID, tmp);
5767 				}
5768 				snprintf (buffer, GMT_BUFSIZ, "Cross profile number -L%s at %8.3f/%07.3f az=%05.1f",
5769 					ID, Tin->segment[seg]->data[GMT_X][row], Tin->segment[seg]->data[GMT_Y][row], orientation);
5770 				S->header = strdup (buffer);
5771 
5772 				if (n_x_seg == n_x_seg_alloc) {
5773 					size_t old_n_x_seg_alloc = n_x_seg_alloc;
5774 					Tout->segment = gmt_M_memory (GMT, Tout->segment, (n_x_seg_alloc += GMT_SMALL_CHUNK), struct GMT_DATASEGMENT *);
5775 					gmt_M_memset (&(Tout->segment[old_n_x_seg_alloc]), n_x_seg_alloc - old_n_x_seg_alloc, struct GMT_DATASEGMENT *);	/* Set to NULL */
5776 				}
5777 				Tout->segment[n_x_seg++] = S;
5778 				Tout->n_segments++;	Xout->n_segments++;
5779 				Tout->n_records += np_cross;	Xout->n_records += np_cross;
5780 			}
5781 		}
5782 	}
5783 
5784 	return (Xout);
5785 }
5786 
5787 /*! . */
gmtsupport_crosstracks_cartesian(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,double cross_length,double across_ds,double deviation,uint64_t n_cols,unsigned int mode,char unit)5788 GMT_LOCAL struct GMT_DATASET * gmtsupport_crosstracks_cartesian (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, double cross_length, double across_ds, double deviation, uint64_t n_cols, unsigned int mode, char unit) {
5789 	/* Din is a data set with at least two columns (x,y);
5790 	 * it can contain any number of tables and segments.
5791 	 * cross_length is the desired length of cross-profiles, in Cartesian units.
5792 	 * across_ds is the sampling interval to use along the cross-profiles.
5793 	 * n_cols sets how many data columns beyond x,y,d should be allocated.
5794 	 * Dout is the new data set with all the crossing profiles;
5795 	*/
5796 
5797 	bool set_fixed_azim = false;
5798 
5799 	int k, ndig, sdig, n_half_cross, k_start, k_stop;
5800 
5801 	unsigned int ii, np_cross;
5802 
5803 	uint64_t tbl, row, seg, seg_no, n_tot_cols;
5804 
5805 	uint64_t dim[GMT_DIM_SIZE] = {0, 0, 0, 0};
5806 	size_t n_x_seg = 0, n_x_seg_alloc = 0;
5807 
5808 	char buffer[GMT_BUFSIZ] = {""}, seg_name[GMT_BUFSIZ] = {""}, ID[GMT_BUFSIZ] = {""};
5809 
5810 	double dist_across_seg, orientation, sign = 1, az_cross, x, y, sa, ca;
5811 
5812 	struct GMT_DATASET *Xout = NULL;
5813 	struct GMT_DATATABLE *Tin = NULL, *Tout = NULL;
5814 	struct GMT_DATASEGMENT *S = NULL;
5815 	gmt_M_unused (unit);	/* For now */
5816 
5817 	if (Din->n_columns < 2) {	/* Trouble */
5818 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Dataset does not have at least 2 columns with coordinates\n");
5819 		return (NULL);
5820 	}
5821 
5822 	if (mode & GMT_ALTERNATE) sign = -1.0;	/* Survey mode */
5823 	if (mode & GMT_FIXED_AZIM) {	/* Got fixed azimuth */
5824 		az_cross = deviation;
5825 		deviation = 0.0;	/* To prevent any deviation below */
5826 		set_fixed_azim = true;
5827 	}
5828 
5829 	/* Get resampling step size and zone width in degrees */
5830 
5831 	cross_length *= 0.5;					/* Now half-length in user's units */
5832 	n_half_cross = irint (cross_length / across_ds);	/* Half-width of points in a cross profile */
5833 	across_ds = cross_length / n_half_cross;		/* Exact increment (recalculated in case of roundoff) */
5834 	n_tot_cols = 4 + n_cols;				/* Total number of columns in the resulting data set */
5835 	k_start = -n_half_cross;
5836 	k_stop = n_half_cross;
5837 	if (mode & GMT_RIGHT_ONLY)	/* Only want left side of profiles */
5838 		k_stop = 0;
5839 	else if (mode & GMT_LEFT_ONLY)	/* Only want right side of profiles */
5840 		k_start = 0;
5841 	np_cross = k_stop - k_start + 1;			/* Total cross-profile length */
5842 	dim[GMT_TBL] = Din->n_tables;	dim[GMT_COL] = n_tot_cols;	dim[GMT_ROW] = np_cross;
5843 	if ((Xout = GMT_Create_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_LINE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) return (NULL);	/* An empty dataset of n_tot_cols columns and np_cross rows */
5844 	sdig = irint (floor (log10 ((double)Din->n_segments))) + 1;	/* Determine how many decimals are needed for largest segment id */
5845 
5846 	for (tbl = seg_no = 0; tbl < Din->n_tables; tbl++) {	/* Process all tables */
5847 		Tin  = Din->table[tbl];
5848 		Tout = Xout->table[tbl];
5849 		for (seg = 0; seg < Tin->n_segments; seg++, seg_no++) {	/* Process all segments */
5850 
5851 			ndig = irint (floor (log10 ((double)Tin->segment[seg]->n_rows))) + 1;	/* Determine how many decimals are needed for largest id */
5852 
5853 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Process Segment %s [segment %" PRIu64 "] which has %" PRIu64 " crossing profiles\n", Tin->segment[seg]->label, seg, Tin->segment[seg]->n_rows);
5854 
5855 			for (row = 0; row < Tin->segment[seg]->n_rows; row++) {	/* Process each point along segment */
5856 				/* Compute segment line orientation (-90/90) from azimuths */
5857 				orientation = 0.5 * fmod (2.0 * Tin->segment[seg]->data[SEG_AZIM][row], 360.0);
5858 				if (orientation > 90.0) orientation -= 180.0;
5859 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Working on cross profile %" PRIu64 " [local line orientation = %06.1f]\n", row, orientation);
5860 
5861 				x = Tin->segment[seg]->data[GMT_X][row];	y = Tin->segment[seg]->data[GMT_Y][row];	/* Reset since now we want lon/lat regardless of grid format */
5862 				if (!set_fixed_azim) az_cross = fmod (Tin->segment[seg]->data[SEG_AZIM][row] + 270.0, 360.0);	/* Azimuth of cross-profile in 0-360 range */
5863 				if (mode & GMT_ALTERNATE)
5864 					sign = -sign;
5865 				else if (mode & GMT_EW_SN)
5866 					sign = (az_cross >= 315.0 || az_cross < 135.0) ? -1.0 : 1.0;	/* We want profiles to be either ~E-W or ~S-N */
5867 				S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, np_cross, n_tot_cols, NULL, NULL);
5868 				sincosd (90.0 - az_cross - deviation, &sa, &ca);	/* Trig on the direction */
5869 				for (k = k_start, ii = 0; k <= k_stop; k++, ii++) {	/* For each point along normal to FZ */
5870 					dist_across_seg = sign * k * across_ds;		/* The current distance along this profile */
5871 					S->data[GMT_X][ii] = x + dist_across_seg * ca;
5872 					S->data[GMT_Y][ii] = y + dist_across_seg * sa;
5873 					S->data[SEG_DIST][ii] = dist_across_seg;	/* Store distances across the profile */
5874 					if (ii) S->data[SEG_AZIM][ii] = gmt_az_backaz (GMT, S->data[GMT_X][ii-1], S->data[GMT_Y][ii-1], S->data[GMT_X][ii], S->data[GMT_Y][ii], false);
5875 				}
5876 				S->data[SEG_AZIM][0] = gmt_az_backaz (GMT, S->data[GMT_X][0], S->data[GMT_Y][0], S->data[GMT_X][1], S->data[GMT_Y][1], false);	/* Special deal for first point */
5877 
5878 				orientation = 0.5 * fmod (2.0 * (Tin->segment[seg]->data[SEG_AZIM][row]+90.0), 360.0);	/* Orientation of cross-profile at zero distance */
5879 				if (orientation > 90.0) orientation -= 180.0;
5880 				ID[0] = seg_name[0] = 0;
5881 				if (Tin->segment[seg]->label) {	/* Use old segment label and append crossprofile number */
5882 					snprintf (ID, GMT_BUFSIZ, "%s-%*.*" PRIu64, Tin->segment[seg]->label, ndig, ndig, row);
5883 				}
5884 				else if (Tin->segment[seg]->header) {	/* Look for label in header */
5885 					gmt_parse_segment_item (GMT, Tin->segment[seg]->header, "-L", seg_name);
5886 					if (seg_name[0]) snprintf (ID, GMT_BUFSIZ, "%s-%*.*" PRIu64, seg_name, ndig, ndig, row);
5887 				}
5888 				if (!ID[0]) {	/* Must assign a label from running numbers */
5889 					if (Tin->n_segments == 1 && Din->n_tables == 1)	/* Single track, just list cross-profile no */
5890 						snprintf (ID, GMT_BUFSIZ, "%*.*" PRIu64, ndig, ndig, row);
5891 					else	/* Segment number and cross-profile no */
5892 						snprintf (ID, GMT_BUFSIZ, "%*.*" PRIu64 "%*.*" PRIu64, sdig, sdig, seg_no, ndig, ndig, row);
5893 				}
5894 				S->label = strdup (ID);
5895 				snprintf (buffer, GMT_BUFSIZ, "Cross profile number -L\"%s\" at %g/%g az=%05.1f",
5896 					ID, Tin->segment[seg]->data[GMT_X][row], Tin->segment[seg]->data[GMT_Y][row], orientation);
5897 				S->header = strdup (buffer);
5898 
5899 				if (n_x_seg == n_x_seg_alloc) {
5900 					size_t old_n_x_seg_alloc = n_x_seg_alloc;
5901 					Tout->segment = gmt_M_memory (GMT, Tout->segment, (n_x_seg_alloc += GMT_SMALL_CHUNK), struct GMT_DATASEGMENT *);
5902 					gmt_M_memset (&(Tout->segment[old_n_x_seg_alloc]), n_x_seg_alloc - old_n_x_seg_alloc, struct GMT_DATASEGMENT *);	/* Set to NULL */
5903 				}
5904 				Tout->segment[n_x_seg++] = S;
5905 				Tout->n_segments++;	Xout->n_segments++;
5906 				Tout->n_records += np_cross;	Xout->n_records += np_cross;
5907 			}
5908 		}
5909 	}
5910 
5911 	return (Xout);
5912 }
5913 
5914 /*! . */
gmtsupport_straddle_dateline(double x0,double x1)5915 GMT_LOCAL bool gmtsupport_straddle_dateline (double x0, double x1) {
5916 	if (fabs (x0 - x1) > 90.0) return (false);	/* Probably Greenwhich crossing with 0/360 discontinuity */
5917 	if ((x0 < 180.0 && x1 > 180.0) || (x0 > 180.0 && x1 < 180.0)) return (true);	/* Crossed Dateline */
5918 	return (false);
5919 }
5920 
5921 /*! . */
gmtsupport_guess_surface_time(struct GMT_CTRL * GMT,unsigned int factors[],unsigned int n_columns,unsigned int n_rows)5922 GMT_LOCAL double gmtsupport_guess_surface_time (struct GMT_CTRL *GMT, unsigned int factors[], unsigned int n_columns, unsigned int n_rows) {
5923 	/* Routine to guess a number proportional to the operations
5924 	 * required by surface working on a user-desired grid of
5925 	 * size n_columns by n_rows, where n_columns = (x_max - x_min)/dx, and same for
5926 	 * n_rows.  (That is, one less than actually used in routine.)
5927 	 *
5928 	 * This is based on the following untested conjecture:
5929 	 * 	The operations are proportional to T = nxg*nyg*L,
5930 	 *	where L is a measure of the distance that data
5931 	 *	constraints must propagate, and nxg, nyg are the
5932 	 * 	current size of the grid.
5933 	 *	For n_columns,n_rows relatively prime, we will go through only
5934 	 * 	one grid cycle, L = max(n_columns,n_rows), and T = n_columns*n_rows*L.
5935 	 *	But for n_columns,n_rows whose greatest common divisor is a highly
5936 	 * 	composite number, we will have L equal to the division
5937 	 * 	step made at each new grid cycle, and nxg,nyg will
5938 	 * 	also be smaller than n_columns,n_rows.  Thus we can hope to find
5939 	 *	some n_columns,n_rows for which the total value of T is C->small.
5940 	 *
5941 	 * The above is pure speculation and has not been derived
5942 	 * empirically.  In actual practice, the distribution of the
5943 	 * data, both spatially and in terms of their values, will
5944 	 * have a strong effect on convergence.
5945 	 *
5946 	 * W. H. F. Smith, 26 Feb 1992.  */
5947 
5948 	unsigned int gcd;		/* Current value of the gcd  */
5949 	unsigned int nxg, nyg;	/* Current value of the grid dimensions  */
5950 	unsigned int nfactors = 0;	/* Number of prime factors of current gcd  */
5951 	unsigned int factor;	/* Currently used factor  */
5952 	/* Doubles are used below, even though the values will be integers,
5953 		because the multiplications might reach sizes of O(n**3)  */
5954 	double t_sum;		/* Sum of values of T at each grid cycle  */
5955 	double length;		/* Current propagation distance.  */
5956 
5957 	gcd = gmt_gcd_euclid (n_columns, n_rows);
5958 	if (gcd > 1) {
5959 		nfactors = gmt_get_prime_factors (GMT, gcd, factors);
5960 		nxg = n_columns/gcd;
5961 		nyg = n_rows/gcd;
5962 		if (nxg < 3 || nyg < 3) {
5963 			factor = factors[nfactors - 1];
5964 			nfactors--;
5965 			gcd /= factor;
5966 			nxg *= factor;
5967 			nyg *= factor;
5968 		}
5969 	}
5970 	else {
5971 		nxg = n_columns;
5972 		nyg = n_rows;
5973 	}
5974 	length = (double)MAX(nxg, nyg);
5975 	t_sum = nxg * (nyg * length);	/* Make it double at each multiply  */
5976 
5977 	/* Are there more grid cycles ?  */
5978 	while (gcd > 1) {
5979 		factor = factors[nfactors - 1];
5980 		nfactors--;
5981 		gcd /= factor;
5982 		nxg *= factor;
5983 		nyg *= factor;
5984 		length = (double)factor;
5985 		t_sum += nxg * (nyg * length);
5986 	}
5987 	return (t_sum);
5988 }
5989 
5990 /*! . */
gmtsupport_compare_sugs(const void * point_1,const void * point_2)5991 GMT_LOCAL int gmtsupport_compare_sugs (const void *point_1, const void *point_2) {
5992 	/* Sorts sugs into DESCENDING order!  */
5993 	if (((struct GMT_SURFACE_SUGGESTION *)point_1)->factor < ((struct GMT_SURFACE_SUGGESTION *)point_2)->factor) return (1);
5994 	if (((struct GMT_SURFACE_SUGGESTION *)point_1)->factor > ((struct GMT_SURFACE_SUGGESTION *)point_2)->factor) return(-1);
5995 	return (0);
5996 }
5997 
5998 /*! . */
gmt_read_list(struct GMT_CTRL * GMT,char * file,char *** list)5999 uint64_t gmt_read_list (struct GMT_CTRL *GMT, char *file, char ***list) {
6000 	/* Reads a file with one string per line. Returns number of strings an
6001 	 * allocated pointer to the array */
6002 	uint64_t n = 0;
6003 	size_t n_alloc = GMT_CHUNK;
6004 	char **p = NULL, line[GMT_BUFSIZ] = {""};
6005 	FILE *fp = NULL;
6006 
6007 	if ((fp = gmt_fopen (GMT, file, "r")) == NULL) {
6008   		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot find/open list file %s\n", file);
6009 		return (0);
6010 	}
6011 
6012 	p = gmt_M_memory (GMT, NULL, n_alloc, char *);
6013 
6014 	while (fgets (line, GMT_BUFSIZ, fp)) {
6015 		gmt_chop (line);	/* Remove trailing CR or LF */
6016 		p[n++] = strdup (line);
6017 		if (n == n_alloc) p = gmt_M_memory (GMT, p, n_alloc <<= 1, char *);
6018 	}
6019 	gmt_fclose (GMT, fp);
6020 	if (n > 0)
6021 		*list = gmt_M_memory (GMT, p, n, char *);
6022 	else {
6023 		gmt_M_free (GMT, p);
6024 		*list = NULL;
6025 	}
6026 
6027 	return (n);
6028 }
6029 
6030 /*! . */
gmtsupport_sort_order_descend(const void * p_1,const void * p_2)6031 GMT_LOCAL int gmtsupport_sort_order_descend (const void *p_1, const void *p_2) {
6032 	/* Returns -1 if point_1 is < that point_2,
6033 	   +1 if point_2 > point_1, and 0 if they are equal
6034 	*/
6035 	bool bad_1, bad_2;
6036 	const struct GMT_ORDER *point_1 = p_1, *point_2 = p_2;
6037 
6038 	bad_1 = gmt_M_is_dnan (point_1->value);
6039 	bad_2 = gmt_M_is_dnan (point_2->value);
6040 
6041 	if (bad_1 && bad_2) return (0);
6042 	if (bad_1) return (+1);
6043 	if (bad_2) return (-1);
6044 
6045 	if (point_1->value > point_2->value) return (-1);
6046 	if (point_1->value < point_2->value) return (+1);
6047 	return (0);
6048 }
6049 
6050 /*! . */
gmtsupport_sort_order_ascend(const void * p_1,const void * p_2)6051 GMT_LOCAL int gmtsupport_sort_order_ascend (const void *p_1, const void *p_2) {
6052 	/* Returns -1 if point_1 is < that point_2,
6053 	   +1 if point_2 > point_1, and 0 if they are equal
6054 	*/
6055 	bool bad_1, bad_2;
6056 	const struct GMT_ORDER *point_1 = p_1, *point_2 = p_2;
6057 
6058 	bad_1 = gmt_M_is_dnan (point_1->value);
6059 	bad_2 = gmt_M_is_dnan (point_2->value);
6060 
6061 	if (bad_1 && bad_2) return (0);
6062 	if (bad_1) return (+1);
6063 	if (bad_2) return (-1);
6064 
6065 	if (point_1->value < point_2->value) return (-1);
6066 	if (point_1->value > point_2->value) return (+1);
6067 	return (0);
6068 }
6069 
6070 /*! . */
gmt_free_list(struct GMT_CTRL * GMT,char ** list,uint64_t n)6071 void gmt_free_list (struct GMT_CTRL *GMT, char **list, uint64_t n) {
6072 	/* Properly free memory allocated by gmt_read_list */
6073 	uint64_t i;
6074 	for (i = 0; i < n; i++) gmt_M_str_free (list[i]);
6075 	gmt_M_free (GMT, list);
6076 }
6077 
6078 #ifndef WIN32
6079 /*! . */
gmtsupport_globerr(const char * path,int eerrno)6080 GMT_LOCAL int gmtsupport_globerr (const char *path, int eerrno) {
6081 	fprintf (stderr, "gmtlib_glob_list: %s: %s\n", path, strerror(eerrno));
6082 	return 0;	/* let glob() keep going */
6083 }
6084 #else
6085 /* Build our own glob for Windows, using tips and code from
6086    http://www.thecodingforums.com/threads/globing-on-windows-in-c-c-language.739310/
6087 
6088   match a character.
6089   Parmas: target - target string, pat - pattern string.
6090   Returns: number of pat character matched.
6091   Notes: means that a * in pat will return zero
6092 */
gmtsupport_chmatch(const char * target,const char * pat)6093 static int gmtsupport_chmatch (const char *target, const char *pat) {
6094 	char *end = NULL, *ptr = NULL;
6095 	if (*pat == '[' && (end = strchr (pat, ']')) ) {
6096 		/* treat close bracket following open bracket as character */
6097 		if (end == pat + 1) {
6098 			end = strchr (pat+2, ']');
6099 			/* make "[]" with no close mismatch all */
6100 			if (end == 0) return 0;
6101 		}
6102 		/* allow [A-Z] and like syntax */
6103 		if (end - pat == 4 && pat[2] == '-' && pat[1] <= pat[3]) {
6104 			if(*target >= pat[1] && *target <= pat[3])
6105 				return 5;
6106 			else
6107 				return 0;
6108 		}
6109 
6110 		/* search for character list contained within brackets */
6111 		ptr = strchr (pat+1, *target);
6112 		if (ptr != 0 && ptr < end)
6113 			return (int)(end - pat + 1);
6114 		else
6115 			return 0;
6116 	}
6117 	if (*pat == '?' && *target != 0)return 1;
6118 	if (*pat == '*') return 0;
6119 	if (*target == 0 || *pat == 0)return 0;
6120 	if (*target == *pat) return 1;
6121 	return 0;
6122 }
6123 /* wildcard matcher.  Params: str - the target string
6124    pattern - pattern to match
6125    Returns: 1 if match, 0 if not.
6126    Notes: ? - match any character
6127    * - match zero or more characters
6128    [?], [*], escapes,
6129    [abc], match a, b or c.
6130    [A-Z] [0-9] [*-x], match range.
6131    [[] - match '['.
6132    [][abc] match ], [, a, b or c
6133 */
gmtsupport_matchwild(const char * str,const char * pattern)6134 GMT_LOCAL int gmtsupport_matchwild (const char *str, const char *pattern) {
6135 	const char *target = str;
6136 	const char *pat = pattern;
6137 	int gobble;
6138 
6139 	while( (gobble = gmtsupport_chmatch(target, pat)) ) {
6140 		target++;
6141 		pat += gobble;
6142 	}
6143 	if (*target == 0 && *pat == 0)
6144 		return 1;
6145 	else if (*pat == '*') {
6146 		while (pat[1] == '*') pat++;
6147 		if (pat[1] == 0) return 1;
6148 		while (*target)
6149 			if (gmtsupport_matchwild (target++, pat+1)) return 1;
6150 	}
6151 	return 0;
6152 }
6153 #endif
6154 
6155 /*! . */
gmtlib_glob_list(struct GMT_CTRL * GMT,const char * pattern,char *** list)6156 uint64_t gmtlib_glob_list (struct GMT_CTRL *GMT, const char *pattern, char ***list) {
6157 #ifdef WIN32
6158 	uint64_t k = 0, n = 0;
6159 	size_t n_alloc = GMT_SMALL_CHUNK;
6160 	char **p = NULL, **file = NULL;
6161 	if ((p = gmtlib_get_dir_list (GMT, ".", NULL)) == NULL) return 0;
6162 
6163 	file = gmt_M_memory (GMT, NULL, n_alloc, char *);
6164 
6165 	while (p[k]) {	/* A NULL marks the end for us */
6166 		if (gmtsupport_matchwild (p[k], pattern)) {	/* Found a match */
6167 			file[n++] = strdup (p[k]);
6168 			if (n == n_alloc) {
6169 				n_alloc <<= 1;
6170 				file = gmt_M_memory (GMT, file, n_alloc, char *);
6171 			}
6172 		}
6173 		k++;
6174 	}
6175 	gmtlib_free_dir_list (GMT, &p);
6176 	if (n < n_alloc) file = gmt_M_memory (GMT, file, n, char *);
6177 	*list = file;
6178 	return n;
6179 }
6180 #else	/* Standard UNIX glob use */
6181 	unsigned int pos = 0, k = 0;
6182 	int ret, flags = 0;
6183 	char **p = NULL, item[GMT_LEN256] = {""};
6184 	glob_t results;
6185 
6186 	if (!pattern || pattern[0] == '\0') return 0;	/* Nothing passed */
6187 
6188 	while ((gmt_strtok (pattern, " \t", &pos, item))) {	/* For all separate arguments */
6189 		flags |= (k > 1 ? GLOB_APPEND : 0);
6190 		ret = glob (item, flags, gmtsupport_globerr, &results);
6191 		if (ret != 0 && ret != GLOB_NOMATCH) {
6192 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtlib_glob_list: problem with wildcard expansion of (%s), stopping early [%s]\n",
6193 				item,
6194 		/* ugly: */	(ret == GLOB_ABORTED ? "filesystem problem" :
6195 				 ret == GLOB_NOMATCH ? "no match of pattern" :
6196 				 ret == GLOB_NOSPACE ? "no dynamic memory" :
6197 				 "unknown problem"));
6198 			break;
6199 		}
6200 		k++;
6201 	}
6202 
6203 	if (results.gl_pathc) p = gmt_M_memory (GMT, NULL, results.gl_pathc, char *);
6204 	for (k = 0; k < results.gl_pathc; k++)
6205 		p[k] = strdup (results.gl_pathv[k]);
6206 
6207 	globfree (&results);
6208 
6209 	*list = p;
6210 	return (uint64_t)results.gl_pathc;
6211 }
6212 #endif
6213 
gmtsupport_find_mod_syntax_start(char * arg,int k)6214 GMT_LOCAL int gmtsupport_find_mod_syntax_start (char *arg, int k) {
6215 	/* Either arg[n] == '+' or not found so arg[n] == 0 */
6216 	bool look = true;
6217 	int n = k;
6218 	while (look && arg[n]) {
6219 		if (arg[n] == '+' && islower (arg[n+1])) look = false;
6220 		else n++;
6221 	}
6222 	return n;
6223 }
6224 
6225 /* There are many tools which requires grid x/y or cpt z to be in meters but the user may have these
6226  * data in km or miles.  Appending +u<unit> to the file addresses this conversion. */
6227 
6228 /*! . */
gmtlib_cptfile_unitscale(struct GMTAPI_CTRL * API,char * name)6229 char *gmtlib_cptfile_unitscale (struct GMTAPI_CTRL *API, char *name) {
6230 	/* Determine if this file ends in +u|U<unit>, with <unit> one of the valid Cartesian distance units */
6231 	char *c = NULL, *f = NULL;
6232 	size_t len = strlen (name);	/* Get length of the file name */
6233 	if (len < 4) return NULL;	/* Not enough space for name and modifier */
6234 	if ((f = gmt_strrstr (name, GMT_CPT_EXTENSION)))
6235 		c = gmtlib_last_valid_file_modifier (API, f, "uU");
6236 	else
6237 		c = gmtlib_last_valid_file_modifier (API, name, "uU");
6238 	if (c == NULL) return NULL;	/* No modifiers */
6239 	if (c && (f = strstr (c, "+u")) == NULL && (f = strstr (c, "+U")) == NULL) return NULL;	/* No +u or +U modifier */
6240 	if (strchr (GMT_LEN_UNITS2, f[2]) == NULL) return NULL;		/* Does no have a valid unit at the end */
6241 	return f;	/* Return valid modifier */
6242 }
6243 
6244 /*! . */
6245 /* Used externally, e.g. GSFML */
gmtlib_detrend(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,double increment,double * intercept,double * slope,int mode)6246 int gmtlib_detrend (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, double increment, double *intercept, double *slope, int mode) {
6247 	/* Deals with linear trend in a dataset, depending on mode:
6248 	 * -1: Determine trend, and remove it from x,y. Return slope and intercept
6249 	 * 0 : Just determine trend. Return slope and intercept
6250 	 * +1: Restore trend in x,y based on given slope/intercept.
6251 	 * (x,y) is the data.  If x == NULL then data is equidistant with increment as the spacing.
6252 	 */
6253 	uint64_t i;
6254 	bool equidistant;
6255 	double xx;
6256 
6257 	equidistant = (x == NULL);	/* If there are no x-values we assume dx is passed via intercept */
6258 	if (mode < 1) {	/* Must determine trend */
6259 		uint64_t m;
6260 		double sum_x = 0.0, sum_xx = 0.0, sum_y = 0.0, sum_xy = 0.0;
6261 		for (i = m = 0; i < n; i++) {
6262 			if (gmt_M_is_dnan (y[i])) continue;
6263 			xx = (equidistant) ? increment*i : x[i];
6264 			sum_x  += xx;
6265 			sum_xx += xx*xx;
6266 			sum_y  += y[i];
6267 			sum_xy += xx*y[i];
6268 			m++;
6269 		}
6270 		if (m > 1) {	/* Got enough points to compute the trend */
6271 			*intercept = (sum_y*sum_xx - sum_x*sum_xy) / (m*sum_xx - sum_x*sum_x);
6272 			*slope = (m*sum_xy - sum_x*sum_y) / (m*sum_xx - sum_x*sum_x);
6273 		}
6274 		else {
6275 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "called with less than 2 points, return NaNs\n");
6276 			*intercept = (m) ? sum_y : GMT->session.d_NaN;	/* Value of single y-point or NaN */
6277 			*slope = GMT->session.d_NaN;
6278 		}
6279 	}
6280 
6281 	if (mode) {	/* Either remove or restore trend from/to the data */
6282 		if (gmt_M_is_dnan (*slope)) {
6283 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "called with slope = NaN - skipped\n");
6284 			return (-1);
6285 		}
6286 		if (gmt_M_is_dnan (*intercept)) {
6287 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "called with intercept = NaN - skipped\n");
6288 			return (-1);
6289 		}
6290 		for (i = 0; i < n; i++) {
6291 			xx = (equidistant) ? increment*i : x[i];
6292 			y[i] += (mode * (*intercept + (*slope) * xx));
6293 		}
6294 	}
6295 	return (GMT_NOERROR);
6296 }
6297 
6298 /*! . */
gmt_is_fill(struct GMT_CTRL * GMT,char * word)6299 bool gmt_is_fill (struct GMT_CTRL *GMT, char *word) {
6300 	/* Return true if argument is an image fill or color or no fill (-) */
6301 	if (!strcmp (word,"-")) return true;	/* Or skip */
6302 	if (gmtsupport_is_pattern (GMT, word)) return true;
6303 	if (gmtlib_is_color (GMT, word)) return true;
6304 	return false;
6305 }
6306 
6307 //#define gmt_is_fill(GMT,word) (!strcmp(word,"-") || gmtsupport_is_pattern (GMT,word) || gmtlib_is_color (GMT, word))
6308 
6309 /* The two flip_angle functions are needed when vectors given by angle/length is to be plotted
6310  * using Cartesian projections in which the direction of positive x and/or y-axis might have
6311  * been reversed.  Thus we flip the vector angle accordingly.
6312  */
6313 
6314 /*! . */
gmt_flip_azim_d(struct GMT_CTRL * GMT,double * azim)6315 void gmt_flip_azim_d (struct GMT_CTRL *GMT, double *azim) {
6316 	/* Adjust an azimuth for Cartesian axes pointing backwards/downwards */
6317 	if (GMT->current.proj.projection_GMT != GMT_LINEAR) return;	/* This only applies to Cartesian scaling */
6318 	/* Must check if negative scales were used */
6319 	if (!GMT->current.proj.xyz_pos[GMT_X]) {	/* Negative x scale */
6320 		if (!GMT->current.proj.xyz_pos[GMT_Y])	/* Negative y-scale too */
6321 			*azim += 180.0;
6322 		else
6323 			*azim = -*azim;
6324 	}
6325 	else if (!GMT->current.proj.xyz_pos[GMT_Y])	/* Negative y-scale only */
6326 		*azim = 180.0 - (*azim);
6327 }
6328 
6329 /*! . */
gmt_flip_angle_d(struct GMT_CTRL * GMT,double * angle)6330 void gmt_flip_angle_d (struct GMT_CTRL *GMT, double *angle) {
6331 	/* Adjust an angle for Cartesian axes pointing backwards/downwards */
6332 	if (GMT->current.proj.projection_GMT != GMT_LINEAR) return;	/* This only applies to Cartesian scaling */
6333 	/* Must check if negative scales were used */
6334 	if (!GMT->current.proj.xyz_pos[GMT_X]) {	/* Negative x scale */
6335 		if (!GMT->current.proj.xyz_pos[GMT_Y])	/* Negative y-scale too */
6336 			*angle += 180.0;
6337 		else
6338 			*angle = 180.0 - (*angle);
6339 	}
6340 	else if (!GMT->current.proj.xyz_pos[GMT_Y])	/* Negative y-scale only */
6341 		*angle = -*angle;
6342 }
6343 
6344 /*! . */
gmt_get_prime_factors(struct GMT_CTRL * GMT,uint64_t n,unsigned int * f)6345 unsigned int gmt_get_prime_factors (struct GMT_CTRL *GMT, uint64_t n, unsigned int *f) {
6346 	/* Fills the integer array f with the prime factors of n.
6347 	 * Returns the number of locations filled in f, which is
6348 	 * one if n is prime.
6349 	 *
6350 	 * f[] should have been malloc'ed to enough space before
6351 	 * calling prime_factors().  We can be certain that f[32]
6352 	 * is enough space, for if n fits in a long, then n < 2**32,
6353 	 * and so it must have fewer than 32 prime factors.  I think
6354 	 * that in general, ceil(log2((double)n)) is enough storage
6355 	 * space for f[].
6356 	 *
6357 	 * Tries 2,3,5 explicitly; then alternately adds 2 or 4
6358 	 * to the previously tried factor to obtain the next trial
6359 	 * factor.  This is done with the variable two_four_toggle.
6360 	 * With this method we try 7,11,13,17,19,23,25,29,31,35,...
6361 	 * up to a maximum of sqrt(n).  This shortened list results
6362 	 * in 1/3 fewer divisions than if we simply tried all integers
6363 	 * between 5 and sqrt(n).  We can reduce the size of the list
6364 	 * of trials by an additional 20% by removing the multiples
6365 	 * of 5, which are equal to 30m +/- 5, where m >= 1.  Starting
6366 	 * from 25, these are found by alternately adding 10 or 20.
6367 	 * To do this, we use the variable ten_twenty_toggle.
6368 	 *
6369 	 * W. H. F. Smith, 26 Feb 1992, after D.E. Knuth, vol. II  */
6370 
6371 	unsigned int current_factor = 0;	/* The factor currently being tried  */
6372 	unsigned int max_factor;		/* Don't try any factors bigger than this  */
6373 	unsigned int n_factors = 0;		/* Returned; one if n is prime  */
6374 	unsigned int two_four_toggle = 0;	/* Used to add 2 or 4 to get next trial factor  */
6375 	unsigned int ten_twenty_toggle = 0;	/* Used to add 10 or 20 to skip_five  */
6376 	unsigned int skip_five = 25;	/* Used to skip multiples of 5 in the list  */
6377 	unsigned int base_factor[3] = {2, 3, 5};	/* Standard factors to try */
6378 	uint64_t m = n;			/* Used to keep a working copy of n  */
6379 	unsigned int k;			/* counter */
6380 	gmt_M_unused(GMT);
6381 
6382 	/* Initialize max_factor  */
6383 
6384 	if (m < 2) return (0);
6385 	max_factor = urint (floor(sqrt((double)m)));
6386 
6387 	/* First find the 2s, 3s, and 5s */
6388 	for (k = 0; k < 3; k++) {
6389 		current_factor = base_factor[k];
6390 		while (!(m % current_factor)) {
6391 			m /= current_factor;
6392 			f[n_factors++] = current_factor;
6393 		}
6394 		if (m == 1) return (n_factors);
6395 	}
6396 
6397 	/* Unless we have already returned we now try all the rest  */
6398 
6399 	while (m > 1 && current_factor <= max_factor) {
6400 
6401 		/* Current factor is either 2 or 4 more than previous value  */
6402 
6403 		if (two_four_toggle) {
6404 			current_factor += 4;
6405 			two_four_toggle = 0;
6406 		}
6407 		else {
6408 			current_factor += 2;
6409 			two_four_toggle = 1;
6410 		}
6411 
6412 		/* If current factor is a multiple of 5, skip it.  But first,
6413 			set next value of skip_five according to 10/20 toggle:  */
6414 
6415 		if (current_factor == skip_five) {
6416 			if (ten_twenty_toggle) {
6417 				skip_five += 20;
6418 				ten_twenty_toggle = 0;
6419 			}
6420 			else {
6421 				skip_five += 10;
6422 				ten_twenty_toggle = 1;
6423 			}
6424 			continue;
6425 		}
6426 
6427 		/* Get here when current_factor is not a multiple of 2,3 or 5:  */
6428 
6429 		while (!(m % current_factor)) {
6430 			m /= current_factor;
6431 			f[n_factors++] = current_factor;
6432 		}
6433 	}
6434 
6435 	/* Get here when all factors up to floor(sqrt(n)) have been tried.  */
6436 
6437 	if (m > 1) f[n_factors++] = (unsigned int)m;	/* m is an additional prime factor of n  */
6438 
6439 	return (n_factors);
6440 }
6441 
6442 /*! . */
gmt_sort_array(struct GMT_CTRL * GMT,void * base,uint64_t n,unsigned int type)6443 void gmt_sort_array (struct GMT_CTRL *GMT, void *base, uint64_t n, unsigned int type) {
6444 	/* Front function to call qsort on all <type> array into ascending order */
6445 	size_t width[GMT_N_TYPES] = {
6446 		sizeof(uint8_t),      /* GMT_UCHAR */
6447 		sizeof(int8_t),       /* GMT_CHAR */
6448 		sizeof(uint16_t),     /* GMT_USHORT */
6449 		sizeof(int16_t),      /* GMT_SHORT */
6450 		sizeof(uint32_t),     /* GMT_UINT */
6451 		sizeof(int32_t),      /* GMT_INT */
6452 		sizeof(uint64_t),     /* GMT_ULONG */
6453 		sizeof(int64_t),      /* GMT_LONG */
6454 		sizeof(float),        /* GMT_FLOAT */
6455 		sizeof(double)};      /* GMT_DOUBLE */
6456 	int (*compare[GMT_N_TYPES]) (const void *, const void *) = {
6457 		/* Array of function pointers */
6458 		gmtsupport_comp_uchar_asc,   /* GMT_CHAR */
6459 		gmtsupport_comp_char_asc,    /* GMT_UCHAR */
6460 		gmtsupport_comp_ushort_asc,  /* GMT_USHORT */
6461 		gmtsupport_comp_short_asc,   /* GMT_SHORT */
6462 		gmtsupport_comp_uint_asc,    /* GMT_UINT */
6463 		gmtsupport_comp_int_asc,     /* GMT_INT */
6464 		gmtsupport_comp_ulong_asc,   /* GMT_ULONG */
6465 		gmtsupport_comp_long_asc,    /* GMT_LONG */
6466 		gmtsupport_comp_float_asc,   /* GMT_FLOAT */
6467 		gmtsupport_comp_double_asc}; /* GMT_DOUBLE */
6468 	gmt_M_unused(GMT);
6469 
6470 	qsort (base, n, width[type], compare[type]);
6471 }
6472 
6473 /*! . */
gmt_sort_order(struct GMT_CTRL * GMT,struct GMT_ORDER * base,uint64_t n,int dir)6474 void gmt_sort_order (struct GMT_CTRL *GMT, struct GMT_ORDER *base, uint64_t n, int dir) {
6475 	/* Sort the order array in ascending or descending direction */
6476 	if (dir == -1)
6477 		qsort (base, n, sizeof (struct GMT_ORDER), gmtsupport_sort_order_descend);
6478 	else if (dir == +1)
6479 		qsort (base, n, sizeof (struct GMT_ORDER), gmtsupport_sort_order_ascend);
6480 	else
6481 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_sort_order: Given illegal direction %d\n", dir);
6482 }
6483 
6484 /*! . */
GMT_strerror(int err)6485 const char * GMT_strerror (int err) {
6486 	/* Returns the error string for a given error code "err"
6487 	 * Passes "err" on to nc_strerror if the error code is not one we defined */
6488 	if (err < 0) return nc_strerror (err);	/* Negative error codes come from netCDF */
6489 	return (gmt_error_string[err]);		/* Other errors are internal GMT errors */
6490 }
6491 
6492 /*! . */
gmt_err_func(struct GMT_CTRL * GMT,int err,bool fail,char * file,const char * where)6493 int gmt_err_func (struct GMT_CTRL *GMT, int err, bool fail, char *file, const char *where) {
6494 	if (err == GMT_NOERROR) return (err);
6495 
6496 	/* When error code is non-zero: print error message */
6497 	if (file && file[0])
6498 		gmtlib_report_func (GMT, GMT_MSG_ERROR, where, "%s [%s]\n", GMT_strerror(err), file);
6499 	else
6500 		gmtlib_report_func (GMT, GMT_MSG_ERROR, where, "%s\n", GMT_strerror(err));
6501 	/* Pass error code on or exit */
6502 	if (fail)
6503 		return GMT_RUNTIME_ERROR;
6504 	else
6505 		return (err);
6506 }
6507 
6508 /*! . */
gmt_init_fill(struct GMT_CTRL * GMT,struct GMT_FILL * fill,double r,double g,double b)6509 void gmt_init_fill (struct GMT_CTRL *GMT, struct GMT_FILL *fill, double r, double g, double b) {
6510 	/* Initialize FILL structure */
6511 
6512 	/* Set whole structure to null (0, 0.0) */
6513 	gmt_M_unused(GMT);
6514 	gmt_M_memset (fill, 1, struct GMT_FILL);
6515 	/* Non-null values: */
6516 	fill->b_rgb[0] = fill->b_rgb[1] = fill->b_rgb[2] = 1.0;
6517 	fill->rgb[0] = r, fill->rgb[1] = g, fill->rgb[2] = b;
6518 }
6519 
6520 /*! . */
gmt_getfill(struct GMT_CTRL * GMT,char * line,struct GMT_FILL * fill)6521 bool gmt_getfill (struct GMT_CTRL *GMT, char *line, struct GMT_FILL *fill) {
6522 	bool error = false;
6523 
6524 	assert (fill);	/* Ffill needs to not point to NULL */
6525 	if (!line) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "No argument given to gmt_getfill\n"); GMT->parent->error = GMT_PARSE_ERROR; return true; }
6526 
6527 	/* Syntax:   -G<gray>, -G<rgb>, -G<cmyk>, -G<hsv> or -Gp|P<image>[+b<rgb>][+f<rgb>][+r<dpi>]   */
6528 	/* Note, <rgb> can be r/g/b, gray, or - for masks.  optionally, append @<transparency> [0] */
6529 
6530 	gmt_init_fill (GMT, fill, -1.0, -1.0, -1.0);	/* Initialize fill structure */
6531 	gmt_chop (line);	/* Remove trailing CR, LF and properly NULL-terminate the string */
6532 	if (!line[0]) return (false);	/* No argument given: we are out of here */
6533 
6534 	if (gmt_M_is_pattern (line))	/* Image specified */
6535 		error = gmtsupport_parse_pattern (GMT, line, fill);
6536 	else	/* Plain color or shade */
6537 		error = gmt_getrgb (GMT, line, fill->rgb);
6538 
6539 	return (error);
6540 }
6541 
6542 /*! . */
gmtlib_getrgb_index(struct GMT_CTRL * GMT,double rgb[])6543 int gmtlib_getrgb_index (struct GMT_CTRL *GMT, double rgb[]) {
6544 	/* Find the index of preset RGB triplets (those with names)
6545 	   Return -1 if none found */
6546 
6547 	int i;
6548 	unsigned char irgb[3];
6549 	gmt_M_unused(GMT);
6550 
6551 	/* First convert from 0-1 to 0-255 range */
6552 	for (i = 0; i < 3; i++) irgb[i] = gmt_M_u255(rgb[i]);
6553 
6554 	/* Compare all RGB codes */
6555 	for (i = 0; i < GMT_N_COLOR_NAMES; i++) {
6556 		if (gmt_M_color_rgb[i][0] == irgb[0] && gmt_M_color_rgb[i][1] == irgb[1] && gmt_M_color_rgb[i][2] == irgb[2]) return (i);
6557 	}
6558 
6559 	/* Nothing found */
6560 	return (-1);
6561 }
6562 
6563 /*! . */
gmt_colorname2index(struct GMT_CTRL * GMT,char * name)6564 int gmt_colorname2index (struct GMT_CTRL *GMT, char *name) {
6565 	/* Return index into structure with colornames and r/g/b */
6566 
6567 	int k;
6568 	char Lname[GMT_LEN64] = {""};
6569 
6570 	strncpy (Lname, name, GMT_LEN64-1);
6571 	gmt_str_tolower (Lname);
6572 	k = gmt_hash_lookup (GMT, Lname, GMT->session.rgb_hashnode, GMT_N_COLOR_NAMES, GMT_N_COLOR_NAMES);
6573 
6574 	return (k);
6575 }
6576 
6577 /*! . */
gmt_getrgb(struct GMT_CTRL * GMT,char * line,double rgb[])6578 bool gmt_getrgb (struct GMT_CTRL *GMT, char *line, double rgb[]) {
6579 	int n, i, count, irgb[3], c = 0;
6580 	double hsv[4], cmyk[5];
6581 	char buffer[GMT_LEN64] = {""}, *t = NULL;
6582 
6583 	if (!line) {
6584 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No argument given to gmt_getrgb\n");
6585 		GMT->parent->error = GMT_PARSE_ERROR;
6586 		return true;
6587 	}
6588 	if (!line[0]) return (false);	/* Nothing to do - accept default action */
6589 
6590 	rgb[3] = hsv[3] = cmyk[4] = 0.0;	/* Default is no transparency */
6591 
6592 	strncpy (buffer, line, GMT_LEN64-1);	/* Make local copy */
6593 	if ((t = strstr (buffer, "@")) && strlen (t) > 1) {	/* User requested transparency via @<transparency> */
6594 		double transparency = atof (&t[1]);
6595 		if (transparency < 0.0 || transparency > 100.0)
6596 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of transparency (%s) not recognized. Using default [0 or opaque].\n", line);
6597 		else
6598 			rgb[3] = hsv[3] = cmyk[4] = transparency / 100.0;	/* Transparency is in 0-1 range */
6599 		t[0] = '\0';	/* Chop off transparency for the rest of this function */
6600 	}
6601 
6602 	if (strstr (buffer, "auto")) {	/* Will select sequential colors from a list - flag via -5 or -6 */
6603 		/* Let auto[-segment] be GMT_COLOR_AUTO_SEGMENT and auto-table be GMT_COLOR_AUTO_TABLE */
6604 		if (strstr (buffer, "table"))
6605 			rgb[0] = rgb[1] = rgb[2] = GMT_COLOR_AUTO_TABLE - 7;
6606 		else
6607 			rgb[0] = rgb[1] = rgb[2] = GMT_COLOR_AUTO_SEGMENT - 7;
6608 		return (false);
6609 	}
6610 
6611 	if (buffer[0] == '-') {
6612 		rgb[0] = rgb[1] = rgb[2] = -1.0;
6613 		return (false);
6614 	}
6615 
6616 	if (buffer[0] == '#') {	/* #rrggbb */
6617 		n = sscanf (buffer, "#%2x%2x%2x", (unsigned int *)&irgb[0], (unsigned int *)&irgb[1], (unsigned int *)&irgb[2]);
6618 		return (n != 3 || gmtsupport_check_irgb (irgb, rgb));
6619 	}
6620 
6621 	/* If it starts with a letter, then it could be a name */
6622 
6623 	if (isalpha ((unsigned char) buffer[0])) {
6624 		if ((n = (int)gmt_colorname2index (GMT, buffer)) < 0) {
6625 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Colorname %s not recognized!\n", buffer);
6626 			return (true);
6627 		}
6628 		for (i = 0; i < 3; i++) rgb[i] = gmt_M_is255 (gmt_M_color_rgb[n][i]);
6629 		return (false);
6630 	}
6631 
6632 	/* Definitely wrong, at this point, is something that does not end in a number */
6633 
6634 	if (strlen(buffer) < 1) return (true);	/* Nothing, which is bad */
6635 	c = buffer[strlen(buffer)-1];
6636 	if (c <= 0 || !(isdigit (c) || c == '.')) return (true);
6637 
6638 	count = (int)gmt_char_count (buffer, '/');
6639 
6640 	if (count == 3) {	/* c/m/y/k */
6641 		n = sscanf (buffer, "%lf/%lf/%lf/%lf", &cmyk[0], &cmyk[1], &cmyk[2], &cmyk[3]);
6642 		if (n != 4 || gmtsupport_check_cmyk (cmyk)) return (true);
6643 		gmtsupport_cmyk_to_rgb (rgb, cmyk);
6644 		return (false);
6645 	}
6646 
6647 	if (count == 2) {	/* r/g/b */
6648 		n = sscanf (buffer, "%lf/%lf/%lf", &rgb[0], &rgb[1], &rgb[2]);
6649 		rgb[0] /= 255.0 ; rgb[1] /= 255.0 ; rgb[2] /= 255.0;
6650 		return (n != 3 || gmtsupport_check_rgb (rgb));
6651 	}
6652 
6653 	if (gmt_char_count (buffer, '-') == 2) {	/* h-s-v despite pretending to be r/g/b */
6654 		n = sscanf (buffer, "%lf-%lf-%lf", &hsv[0], &hsv[1], &hsv[2]);
6655 		if (n != 3 || gmtsupport_check_hsv (hsv)) return (true);
6656 		gmt_hsv_to_rgb (rgb, hsv);
6657 		return (false);
6658 	}
6659 
6660 	if (count == 0) {	/* gray */
6661 		n = sscanf (buffer, "%lf", &rgb[0]);
6662 		rgb[0] /= 255.0 ; rgb[1] = rgb[2] = rgb[0];
6663 		return (n != 1 || gmtsupport_check_rgb (rgb));
6664 	}
6665 
6666 	/* Get here if there is a problem */
6667 
6668 	return (true);
6669 }
6670 
6671 /*! . */
gmtlib_enforce_rgb_triplets(struct GMT_CTRL * GMT,char * text,unsigned int size)6672 void gmtlib_enforce_rgb_triplets (struct GMT_CTRL *GMT, char *text, unsigned int size) {
6673 	/* Purpose is to replace things like @;lightgreen; with @r/g/b; which PSL_plottext understands.
6674 	 * Likewise, if @transparency is appended we change it to =transparency so PSL_plottext can parse it */
6675 
6676 	unsigned int i, j, k = 0, n, last = 0, n_slash;
6677 	double rgb[4];
6678 	char buffer[GMT_BUFSIZ] = {""}, color[GMT_LEN256] = {""}, *p = NULL;
6679 
6680 	if (!strchr (text, '@')) return;	/* Nothing to do since no escape sequence in string */
6681 
6682 	while ((p = strstr (text, "@;"))) {	/* Found a @; sequence */
6683 		i = (unsigned int)(p - text) + 2;	/* Position of first character after @; */
6684 		for (j = last; j < i; j++, k++) buffer[k] = text[j];	/* Copy everything from last stop up to the color specification */
6685 		text[i-1] = 'X';	/* Wipe the ; so that @; won't be found a 2nd time */
6686 		if (text[i] != ';') {	/* Color info now follows */
6687 			n = i;
6688 			n_slash = 0;
6689 			while (text[n] && text[n] != ';') {	/* Find end of the color info and also count slashes */
6690 				if (text[n] == '/') n_slash++;
6691 				n++;
6692 			}
6693 			if (n_slash != 2) {	/* r/g/b not given, must replace whatever it was with a r/g/b triplet */
6694 				text[n] = '\0';	/* Temporarily terminate string so getrgb can work */
6695 				if (gmt_getrgb (GMT, &text[i], rgb))
6696 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Failed to convert %s to r/g/b\n", &text[i]);
6697 				text[n] = ';';	/* Undo damage */
6698 				if (rgb[3] > 0.0)	/* Asked for transparency as well */
6699 					snprintf (color, GMT_LEN256, "%f/%f/%f=%ld", gmt_M_t255(rgb,0), gmt_M_t255(rgb,1), gmt_M_t255(rgb,2), lrint (100.0 * rgb[3]));	/* Format triplet w/ transparency */
6700 				else
6701 					snprintf (color, GMT_LEN256, "%f/%f/%f", gmt_M_t255(rgb,0), gmt_M_t255(rgb,1), gmt_M_t255(rgb,2));	/* Format triplet only */
6702 				for (j = 0; color[j]; j++, k++) buffer[k] = color[j];	/* Copy over triplet and update buffer pointer k */
6703 			}
6704 			else	/* Already in r/g/b format, just copy */
6705 				for (j = i; j < n; j++, k++) buffer[k] = text[j];
6706 			i = n;	/* Position of terminating ; */
6707 		}
6708 		buffer[k++] = ';';	/* Finish the specification */
6709 		last = i + 1;	/* Start of next part to copy */
6710 	}
6711 	i = last;	/* Finish copying everything left in the text string */
6712 	while (text[i]) buffer[k++] = text[i++];
6713 	buffer[k++] = '\0';	/* Properly terminate buffer */
6714 
6715 	if (k > size) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Replacement string too long - truncated\n");
6716 	strncpy (text, buffer, k);	/* Copy back the revised string */
6717 }
6718 
6719 /*! . */
gmt_getfont(struct GMT_CTRL * GMT,char * buffer,struct GMT_FONT * F)6720 int gmt_getfont (struct GMT_CTRL *GMT, char *buffer, struct GMT_FONT *F) {
6721 	unsigned int i, n;
6722 	int k;
6723 	double pointsize;
6724 	char size[GMT_LEN256] = {""}, name[GMT_LEN256] = {""}, fill[GMT_LEN256] = {""}, line[GMT_BUFSIZ] = {""}, *s = NULL;
6725 
6726 	if (!buffer || !buffer[0]) {
6727 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No argument given to gmt_getfont\n");
6728 		return GMT_PARSE_ERROR;
6729 	}
6730 	assert (F);	/* F needs to not point to NULL */
6731 	strncpy (line, buffer, GMT_BUFSIZ-1);	/* Work on a copy of the arguments */
6732 	gmt_chop (line);	/* Remove trailing CR, LF and properly NULL-terminate the string */
6733 
6734 	/* Processes font settings given as [size][,name][,fill][=pen] */
6735 
6736 	F->form = 1;	/* Default is to fill the text with a solid color */
6737 	F->set = 0;     /* Start from no settings */
6738 	if ((s = strchr (line, '='))) {	/* Specified an outline pen */
6739 		s[0] = 0, i = 1;	/* Chop of this modifier */
6740 		if (s[1] == '~') F->form |= 8, i = 2;	/* Want to have an outline that does not obscure the text */
6741 		if (gmt_getpen (GMT, &s[i], &F->pen))
6742 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of font outline pen not recognized - ignored.\n");
6743 		else
6744 			F->form |= 2;	/* Turn on outline font flag */
6745 		F->set = 2;	/* Means we specified font pen */
6746 	}
6747 	for (i = 0; line[i]; i++) if (line[i] == ',') line[i] = ' ';	/* Replace , with space */
6748 	n = sscanf (line, "%s %s %s", size, name, fill);
6749 	for (i = 0; line[i]; i++) if (line[i] == ' ') line[i] = ',';	/* Replace space with , */
6750 	if (n == 2 && i > 0) {	/* Could be size,name or size,fill or name,fill */
6751 		if (line[i-1] == ',') {		/* Must be size,name, so we can continue */
6752 		}
6753 		else if (line[0] == ',') {	/* ,name,fill got stored in size,name */
6754 			strncpy (fill, name, GMT_LEN256-1);
6755 			strncpy (name, size, GMT_LEN256-1);
6756 			size[0] = '\0';
6757 		}
6758 		else if (gmt_is_fill (GMT, name)) {	/* fill got stored in name */
6759 			strncpy (fill, name, GMT_LEN256-1);
6760 			name[0] = '\0';
6761 			if (gmtsupport_is_fontname (GMT, size)) {	/* name got stored in size */
6762 				strncpy (name, size, GMT_LEN256-1);
6763 				size[0] = '\0';
6764 			}
6765 		}
6766 		else if (gmtsupport_is_fontname (GMT, size)) {	/* name got stored in size and size in name */
6767 			strncpy (fill, name, GMT_LEN256-1);	/* Copy size */
6768 			strncpy (name, size, GMT_LEN256-1);	/* Place name where it belongs */
6769 			strncpy (size, fill, GMT_LEN256-1);	/* Place size where it belongs */
6770 			fill[0] = '\0';	/* No fill */
6771 		}
6772 	}
6773 	else if (n == 1) {	/* Could be size or name or fill */
6774 		if (line[0] == ',' && line[1] == ',') {	/* ,,fill got stored in size */
6775 			strncpy (fill, size, GMT_LEN256-1);
6776 			size[0] = '\0';
6777 		}
6778 		else if (line[0] == ',') {		/* ,name got stored in size */
6779 			strncpy (name, size, GMT_LEN256-1);
6780 			size[0] = '\0';
6781 		}
6782 		else if (gmt_is_fill (GMT, size)) {	/* fill got stored in size */
6783 			strncpy (fill, size, GMT_LEN256-1);
6784 			size[0] = '\0';
6785 		}
6786 		else if (gmtsupport_is_fontname (GMT, size)) {	/* name got stored in size */
6787 			strncpy (name, size, GMT_LEN256-1);
6788 			size[0] = '\0';
6789 		}
6790 		/* Unstated else branch means we got size stored correctly */
6791 	}
6792 	/* Unstated else branch means we got all 3: size,name,fill */
6793 
6794 	/* Assign font size, type, and fill, if given */
6795 	if (!size[0] || size[0] == '-') { /* Skip */ }
6796 	else if (!strncmp (size, "auto", 4U))
6797 		F->size = GMT->session.d_NaN;
6798 	else if ((pointsize = gmt_convert_units (GMT, size, GMT_PT, GMT_PT)) < GMT_CONV4_LIMIT)
6799 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of font size not recognized. Using default.\n");
6800 	else
6801 		F->size = pointsize;
6802 	if (!name[0] || name[0] == '-') { /* Skip */ }
6803 	else if ((k = gmt_getfonttype (GMT, name)) >= 0)
6804 		F->id = k;
6805 	else
6806 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of font type not recognized. Using default.\n");
6807 	if (!fill[0]) { /* Skip */ }
6808 	else if (fill[0] == '-') {	/* Want no fill */
6809 		F->form &= 2;	/* Turn off fill font flag set initially */
6810 		gmt_M_rgb_copy (F->fill.rgb, GMT->session.no_rgb);
6811 	}
6812 	else {	/* Decode fill and set flags */
6813 		if (gmt_getfill (GMT, fill, &F->fill)) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of font fill not recognized. Using default.\n");
6814 		if (F->fill.use_pattern) F->form &= 2, F->form |= 4;	/* Flag that font fill is a pattern and not solid color */
6815 		F->set |= 1;	/* Means we did specify something related to the fill */
6816 	}
6817 	if ((F->form & 7) == 0) {
6818 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Cannot turn off both font fill and font outline.  Reset to font fill.\n");
6819 		F->form = 1;
6820 	}
6821 	return (false);
6822 }
6823 
6824 /*! . */
gmt_putfont(struct GMT_CTRL * GMT,struct GMT_FONT * F)6825 char *gmt_putfont (struct GMT_CTRL *GMT, struct GMT_FONT *F) {
6826 	/* gmt_putfont creates a GMT textstring equivalent of the specified font */
6827 
6828 	static char text[GMT_BUFSIZ];
6829 	char size[GMT_LEN32] = {""};
6830 
6831 	if (gmt_M_is_dnan (F->size))
6832 		snprintf (size, GMT_LEN32, "auto,");
6833 	else
6834 		snprintf (size, GMT_LEN32, "%gp,", F->size);
6835 
6836 	if (F->form & 2) {
6837 		if (F->form & 8)
6838 			snprintf (text, GMT_BUFSIZ, "%s%s,%s=~%s", size, GMT->session.font[F->id].name, gmtlib_putfill (GMT, &F->fill), gmt_putpen (GMT, &F->pen));
6839 		else
6840 			snprintf (text, GMT_BUFSIZ, "%s%s,%s=%s", size, GMT->session.font[F->id].name, gmtlib_putfill (GMT, &F->fill), gmt_putpen (GMT, &F->pen));
6841 	}
6842 	else
6843 		snprintf (text, GMT_BUFSIZ, "%s%s,%s", size, GMT->session.font[F->id].name, gmtlib_putfill (GMT, &F->fill));
6844 	return (text);
6845 }
6846 
6847 /*! . */
gmt_init_pen(struct GMT_CTRL * GMT,struct GMT_PEN * pen,double width)6848 void gmt_init_pen (struct GMT_CTRL *GMT, struct GMT_PEN *pen, double width) {
6849 	/* Sets default black solid pen of given width in points */
6850 	gmt_M_unused(GMT);
6851 	gmt_M_memset (pen, 1, struct GMT_PEN);
6852 	pen->width = width;
6853 }
6854 
gmtsupport_is_pen(struct GMT_CTRL * GMT,char * line)6855 GMT_LOCAL bool gmtsupport_is_pen (struct GMT_CTRL *GMT, char *line) {
6856 	/* Returns true if text is a pen specification. Note: false means it is only a number, which could be a dumb specification for a pen, e.g. 2 */
6857 	char *c = NULL;
6858 	unsigned int i, nc;
6859 	gmt_M_unused (GMT);
6860 	if ((c = strchr (line, '+')) && strchr ("cosv", c[1])) return (true);	/* Found valid pen modifiers */
6861 	for (i = nc = 0; line[i]; i++) if (line[i] == ',') nc++;	/* count commas */
6862 	if (nc > 0) return (true);	/* At least 1 comma means we got color and/or style so clearly a pen */
6863 	if (strchr (GMT_DIM_UNITS, line[strlen(line)-1])) return (true);	/* Clearly ends with a explicit measure unit, so a pen */
6864 	if (isalpha (line[0])) return true;	/* A name probably, like faint */
6865 	return (false);	/* Might still be, but here we just have a dumb number so who can tell */
6866 }
6867 
6868 /*! . */
gmt_getpen(struct GMT_CTRL * GMT,char * buffer,struct GMT_PEN * P)6869 bool gmt_getpen (struct GMT_CTRL *GMT, char *buffer, struct GMT_PEN *P) {
6870 	int i, n;
6871 	bool set_NaN = false;
6872 	char def_width[GMT_LEN256] = {""}, width[GMT_LEN256] = {""}, color[GMT_LEN256] = {""}, style[GMT_LEN256] = {""}, line[GMT_BUFSIZ] = {""}, *c = NULL;
6873 
6874 	if (!buffer || !buffer[0]) return (false);		/* Nothing given: return silently, leaving P in tact */
6875 	assert (P);	/* P needs to not point to NULL */
6876 
6877 	strncpy (line, buffer, GMT_BUFSIZ-1);	/* Work on a copy of the arguments */
6878 	gmt_chop (line);	/* Remove trailing CR, LF and properly NULL-terminate the string */
6879 	if (!line[0]) return (false);		/* Nothing given: return silently, leaving P in tact */
6880 
6881 	/* First chop off and processes any line modifiers :
6882 	 * +c[l|f] : Determine how a CPT (-C) affects pen and fill colors normally controlled via -W.
6883 	 * +s : Draw line via Bezier spline [Default is linear segments]
6884 	 * +o<offset(s) : Start and end line after an initial offset from actual coordinates [planned for 5.3]
6885 	 * +v[b|e]<size><vecargs>  : Draw vector head at line endings [planned for 5.3].
6886 	 */
6887 
6888 	if ((c = strchr (line, '+')) && strchr ("cosv", c[1])) {	/* Found valid modifiers */
6889 		char mods[GMT_LEN256] = {""}, v_args[2][GMT_LEN256] = {"",""}, p[GMT_LEN64] = {""}, T[2][GMT_LEN64] = {"",""}, *t = NULL, *t2 = NULL;
6890 		unsigned int pos = 0;
6891 		int n;
6892 		bool processed_vector = false, use[2] = {false, false}, may_differ = false;
6893 		size_t len;
6894 		if ((t2 = strstr (line, "+v"))) {	/* Keep the +v <args> since strtok will split them otherwise */
6895 			if ((t = strstr (line, "+vb"))) {	/* Keep the +v <args> since strtok will split them otherwise */
6896 				strncpy (v_args[BEG], &t[3], GMT_LEN256-1);
6897 				if ((t = strstr (v_args[BEG], "+ve"))) t[0] = '\0';	/* Chop off the other one */
6898 				use[BEG] = true;
6899 			}
6900 			if ((t = strstr (line, "+ve"))) {	/* Keep the +v <args> since strtok will split them otherwise */
6901 				strncpy (v_args[END], &t[3], GMT_LEN256-1);
6902 				if ((t = strstr (v_args[END], "+vb"))) t[0] = '\0';	/* Chop off the other one */
6903 				use[END] = true;
6904 			}
6905 			if (use[BEG] == false && use[END] == false) {	/* Just gave +v to apply to both ends */
6906 				strncpy (v_args[BEG], &t2[2], GMT_LEN256-1);
6907 				strncpy (v_args[END], &t2[2], GMT_LEN256-1);
6908 				may_differ = use[BEG] = use[END] = true;
6909 			}
6910 		}
6911 		strncpy (mods, &c[1], GMT_LEN256-1);	/* Get our copy of the modifiers */
6912 		c[0] = '\0';		/* Chop off modifiers */
6913 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Pen modifier found: %s\n", mods);
6914 		while ((gmt_strtok (mods, "+", &pos, p))) {
6915 			switch (p[0]) {
6916 				case 'c':	/* Effect of CPT on lines and fills */
6917 					switch (p[1]) {
6918 						case 'l':   P->cptmode = 1; break;
6919 						case 'f':   P->cptmode = 2; break;
6920 						case '\0':  P->cptmode = 3; break;
6921 						default:
6922 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure parsing pen modification +%s\n", p);
6923 							return false;
6924 						break;
6925 					}
6926 					break;
6927 				case 's':
6928 					if (processed_vector) break;	/* This is the +s within +v vector specifications */
6929 					P->mode = PSL_BEZIER;
6930 					break;
6931 				case 'o':
6932 					if (processed_vector) break;	/* This is the +o within +v vector specifications */
6933 					n = sscanf (&p[1], "%[^/]/%s", T[BEG], T[END]);
6934 					if (n == 1) strcpy (T[END], T[BEG]);
6935 					for (n = 0; n < 2; n++) {
6936 						len = strlen (T[n]) - 1;
6937 						if (strchr (GMT_DIM_UNITS, T[n][len])) {	/* Plot distances in cm,inch,points */
6938 							P->end[n].type = 0;
6939 							P->end[n].unit = 'C';
6940 							P->end[n].offset = gmt_M_to_inch (GMT, T[n]);
6941 						}
6942 						else if (strchr (GMT_LEN_UNITS, T[n][len]))	/* Length units */
6943 							P->end[n].type = gmt_get_distance (GMT, T[n], &P->end[n].offset, &P->end[n].unit);
6944 						else {	/* No units, select Cartesian */
6945 							P->end[n].type = 0;
6946 							P->end[n].unit = 'X';
6947 							P->end[n].offset = atof (T[n]);
6948 						}
6949 					}
6950 					break;
6951 				case 'v':	/* Vector specs */
6952 					processed_vector = true;
6953 					for (n = 0; n < 2; n++) {
6954 						if (!use[n]) continue;
6955 						gmt_M_free (GMT, P->end[n].V);	/* Must free previous structure first */
6956 						P->end[n].V = gmt_M_memory (GMT, NULL, 1, struct GMT_SYMBOL);
6957 						sscanf (v_args[n], "%[^+]%s", T[BEG], T[END]);
6958 						P->end[n].V->size_x = gmt_M_to_inch (GMT, T[BEG]);
6959 						if (gmt_parse_vector (GMT, 'v', T[END], P->end[n].V)) {	/* Error decoding new vector syntax */
6960 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure parsing vector specifications %s\n", T[END]);
6961 							return false;
6962 						}
6963 						if (P->end[n].V->v.status & PSL_VEC_BEGIN) P->end[n].V->v.status -= PSL_VEC_BEGIN;	/* Always at end in this context */
6964 						P->end[n].V->v.status |= PSL_VEC_END;	/* Always at end in this context */
6965 						gmt_init_vector_param (GMT, P->end[n].V, false, false, NULL, false, NULL);	/* Update vector head parameters */
6966 						if (may_differ)	/* Must allow different symbols at the two ends */
6967 							P->end[n].V->v.v_kind[END] = P->end[n].V->v.v_kind[n];
6968 						else
6969 							P->end[n].V->v.v_kind[END] = MAX (P->end[n].V->v.v_kind[BEG], P->end[n].V->v.v_kind[END]);
6970 						switch (P->end[n].V->v.v_kind[END]) {
6971 							case PSL_VEC_ARROW:
6972 								P->end[n].length = 0.5 * P->end[n].V->size_x * (2.0 - P->end[n].V->v.v_shape);
6973 								break;
6974 							case PSL_VEC_CIRCLE:
6975 								P->end[n].length = sqrt (P->end[n].V->size_x * 0.5 * P->end[n].V->v.h_width / M_PI);	/* Same circle area as vector head */
6976 								break;
6977 							case PSL_VEC_SQUARE:
6978 								P->end[n].length = sqrt (P->end[n].V->size_x * 0.5 * P->end[n].V->v.h_width)/2;	/* Same square area as vector head */
6979 								break;
6980 							case PSL_VEC_TERMINAL:
6981 								P->end[n].length = 0.0;
6982 								break;
6983 
6984 						}
6985 						use[n] = false;	/* Done processing this one */
6986 					}
6987 					break;
6988 				case 'a': case 'b': case 'e': case 'g': case 'h': case 'j': case 'l': case 'm': case 'n': case 'p': case 'q':
6989 					 case 'r': case 't': case 'z':	/* These are possible modifiers within +v vector specifications */
6990 					if (processed_vector) break;
6991 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Pen modifier (%s) not recognized.\n", p);
6992 					break;
6993 				default:
6994 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Pen modifier (%s) not recognized.\n", p);
6995 					break;
6996 			}
6997 		}
6998 	}
6999 
7000 	/* Processes pen specifications given as [width[,<color>[,<style>[t<unit>]]][@<transparency>] */
7001 
7002 	if (gmt_M_is_dnan (P->width)) {	/* Worry in case no width is given */
7003 		strcpy (def_width, "0");	/* To avoid any parsing errors */
7004 		set_NaN = true;	/* Flag this specific case */
7005 	}
7006 	else	/* Default to current pen width if pen width is not given */
7007 		sprintf (def_width, "%.16gp", P->width);
7008 	for (i = 0; line[i]; i++) if (line[i] == ',') line[i] = ' ';	/* Replace , with space */
7009 	n = sscanf (line, "%s %s %s", width, color, style);
7010 	for (i = 0; line[i]; i++) if (line[i] == ' ') line[i] = ',';	/* Replace space with , */
7011 	if (n == 2) {	/* Could be width,color or width,style or color,style */
7012 		if (line[0] == ',') {	/* ,color,style got stored in width,color */
7013 			strncpy (style, color, GMT_LEN256-1);
7014 			strncpy (color, width, GMT_LEN256-1);
7015 			strncpy (width, def_width, GMT_LEN256-1);
7016 		}
7017 		else if (gmtsupport_is_penstyle (color)) {	/* style got stored in color */
7018 			strncpy (style, color, GMT_LEN256-1);
7019 			color[0] = '\0';
7020 			if (gmtlib_is_color (GMT, width)) {	/* color got stored in width */
7021 				strncpy (color, width, GMT_LEN256-1);
7022 				strncpy (width, def_width, GMT_LEN256-1);
7023 			}
7024 		}
7025 		else	/* Means we got width, color */
7026 			set_NaN = false;
7027 	}
7028 	else if (n == 1) {	/* Could be width or color or style */
7029 		if (line[0] == ',' && line[1] == ',') {	/* ,,style got stored in width */
7030 			strncpy (style, width, GMT_LEN256-1);
7031 			strncpy (width, def_width, GMT_LEN256-1);
7032 		}
7033 		else if (line[0] == ',') {		/* ,color got stored in width */
7034 			strncpy (color, width, GMT_LEN256-1);
7035 			strncpy (width, def_width, GMT_LEN256-1);
7036 		}
7037 		else if (gmtsupport_is_penstyle (width)) {	/* style got stored in width */
7038 			strncpy (style, width, GMT_LEN256-1);
7039 			strncpy (width, def_width, GMT_LEN256-1);
7040 		}
7041 		else if (gmtlib_is_color (GMT, width)) {	/* color got stored in width */
7042 			strncpy (color, width, GMT_LEN256-1);
7043 			strncpy (width, def_width, GMT_LEN256-1);
7044 		}
7045 		else	/* Means we got width */
7046 			set_NaN = false;
7047 	}
7048 	/* Assign width, color, style if given */
7049 	if (gmtsupport_getpenwidth (GMT, width, P)) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of pen width (%s) not recognized. Using default.\n", width);
7050 	if (gmt_getrgb (GMT, color, P->rgb)) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of pen color (%s) not recognized. Using default.\n", color);
7051 	if (set_NaN) P->width = GMT->session.d_NaN;	/* This will prevent gmtlib_getpenstyle to set final style since that needs actual width [see gmt_set_undefined_defaults] */
7052 	if (gmtlib_getpenstyle (GMT, style, P)) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Representation of pen style (%s) not recognized. Using default.\n", style);
7053 
7054 	return (false);
7055 }
7056 
7057 /*! . */
gmt_putpen(struct GMT_CTRL * GMT,struct GMT_PEN * P)7058 char *gmt_putpen (struct GMT_CTRL *GMT, struct GMT_PEN *P) {
7059 	/* gmt_putpen creates a GMT textstring equivalent of the specified pen */
7060 
7061 	static char text[GMT_BUFSIZ];
7062 	int i, k;
7063 
7064 	k = gmtsupport_pen2name (P->width);
7065 	if (P->style[0]) {
7066 		if (k == -2)	/* Width is undefined */
7067 			snprintf (text, GMT_BUFSIZ, "%s,%s:%.5gp", gmt_putcolor (GMT, P->rgb), P->style, P->offset);
7068 		else if (k == -1)	/* Width has no name */
7069 			snprintf (text, GMT_BUFSIZ, "%.5gp,%s,%s:%.5gp", P->width, gmt_putcolor (GMT, P->rgb), P->style, P->offset);
7070 		else	/* Named pen width */
7071 			snprintf (text, GMT_BUFSIZ, "%s,%s,%s:%.5gp", GMT_penname[k].name, gmt_putcolor (GMT, P->rgb), P->style, P->offset);
7072 		for (i = 0; text[i]; i++) if (text[i] == ' ') text[i] = '_';
7073 	}
7074 	else {
7075 		if (k == -2)	/* Width is undefined */
7076 			snprintf (text, GMT_BUFSIZ, "%s", gmt_putcolor (GMT, P->rgb));
7077 		else if (k == -1)	/* Width has no name */
7078 			snprintf (text, GMT_BUFSIZ, "%.5gp,%s", P->width, gmt_putcolor (GMT, P->rgb));
7079 		else	/* Named pen width */
7080 			snprintf (text, GMT_BUFSIZ, "%s,%s", GMT_penname[k].name, gmt_putcolor (GMT, P->rgb));
7081 	}
7082 	return (text);
7083 }
7084 
gmt_freepen(struct GMT_CTRL * GMT,struct GMT_PEN * P)7085 void gmt_freepen (struct GMT_CTRL *GMT, struct GMT_PEN *P) {
7086 	for (int k = 0; k < 2; k++) {
7087 		gmt_M_free (GMT, P->end[k].V);
7088 	}
7089 }
7090 
gmt_scale_pen(struct GMT_CTRL * GMT,struct GMT_PEN * P,double scale)7091 void gmt_scale_pen (struct GMT_CTRL *GMT, struct GMT_PEN *P, double scale) {
7092 	/* Scale all pen attributes by given scale. Note: P as assumed to be reset to nominal values before scaling */
7093 	gmt_M_unused (GMT);
7094 	P->width  *= scale;
7095 	P->offset *= scale;
7096 	if (P->style[0]) {	/* Must scale the dashes and gaps and update string */
7097 		unsigned int pos = 0;
7098 		char tmp[GMT_PEN_LEN] = {""}, p[GMT_LEN64] = {""};
7099 		double w;
7100 		while ((gmt_strtok (P->style, "+", &pos, p))) {
7101 			w = atof (p) * scale;	/* New gap or dash length */
7102 			snprintf (p, GMT_LEN64, "%.3g", w);
7103 			if (tmp[0]) strcat (tmp, " ");	/* If not first then we need space between items */
7104 			strcat (tmp, p);
7105 		}
7106 		/* Update the style attribute */
7107 		strncpy (P->style, tmp, GMT_PEN_LEN);
7108 	}
7109 }
7110 
7111 #define GMT_INC_IS_FEET		1
7112 #define GMT_INC_IS_SURVEY_FEET	2
7113 #define GMT_INC_IS_M		4
7114 #define GMT_INC_IS_KM		8
7115 #define GMT_INC_IS_MILES	16
7116 #define GMT_INC_IS_NMILES	32
7117 #define GMT_INC_IS_NNODES	64
7118 #define GMT_INC_IS_EXACT	128
7119 #define GMT_INC_UNITS		63
7120 
7121 /*! . */
gmt_getinc(struct GMT_CTRL * GMT,char * line,double inc[])7122 bool gmt_getinc (struct GMT_CTRL *GMT, char *line, double inc[]) {
7123 	/* Special case of getincn use where n is two. */
7124 
7125 	int n;
7126 
7127 	/* Syntax: -I<xinc>[m|s|e|f|k|M|n|u|+e|n][/<yinc>][m|s|e|f|k|M|n|u|+e|n]
7128 	 * Units: d = arc degrees
7129 	 * 	  m = arc minutes
7130 	 *	  s = arc seconds [was c]
7131 	 *	  e = meter [Convert to degrees]
7132 	 *	  f = feet [Convert to degrees]
7133 	 *	  M = Miles [Convert to degrees]
7134 	 *	  k = km [Convert to degrees]
7135 	 *	  n = nautical miles [Convert to degrees]
7136 	 *	  u = survey feet [Convert to degrees]
7137 	 * Flags: +e = Adjust -R to fit exact -I [Default modifies -I to fit -R]
7138 	 *	  +n = incs are actually n_columns/n_rows - convert to get xinc/yinc
7139 	 */
7140 
7141 	if (!line) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "No argument given to gmt_getinc\n"); return (true); }
7142 
7143 	if ((n = gmt_getincn (GMT, line, inc, 2)) < 0) return true;
7144 	if (n == 1) {	/* Must copy y info from x */
7145 		inc[GMT_Y] = inc[GMT_X];
7146 		GMT->current.io.inc_code[GMT_Y] = GMT->current.io.inc_code[GMT_X];	/* Use exact inc codes for both x and y */
7147 	}
7148 
7149 	if (GMT->current.io.inc_code[GMT_X] & GMT_INC_IS_NNODES && GMT->current.io.inc_code[GMT_X] & GMT_INC_UNITS) {
7150 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "number of x nodes cannot have units\n");
7151 		return (true);
7152 	}
7153 	if (GMT->current.io.inc_code[GMT_Y] & GMT_INC_IS_NNODES && GMT->current.io.inc_code[GMT_Y] & GMT_INC_UNITS) {
7154 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "number of y nodes cannot have units\n");
7155 		return (true);
7156 	}
7157 	return (false);
7158 }
7159 
gmtsupport_set_geo(struct GMT_CTRL * GMT)7160 GMT_LOCAL unsigned int gmtsupport_set_geo (struct GMT_CTRL *GMT) {
7161 	/* Returns 0 for all Cartesian, or sum of GMT_IS_LON if longitudes and GMT_IS_LAT if latitudes */
7162 	unsigned int x = GMT_IS_LON, y = GMT_IS_LAT;
7163 	if (!GMT->common.R.active[RSET]) return x + y;	/* Just as before to avoid breaking things */
7164 	/* Here we have -Rw/e/s/n.  If it clearly is not geographic then we return 0, else we return one or the sum of GMT_IS_LON and GMT_IS_LAT */
7165 	if ((GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) > 360.0) x = 0;	/* Cannot exceed 360 degrees longitude */
7166 	if ((GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) > 180.0) y = 0;	/* Cannot exceed 180 degrees latitude */
7167 	if (GMT->common.R.wesn[XLO] < -720.0 || GMT->common.R.wesn[XLO] > 360.0) x = 0;	/* Clearly outside normal longitude range */
7168 	if (GMT->common.R.wesn[XHI] < -360.0 || GMT->common.R.wesn[XHI] > 720.0) x = 0;	/* Clearly outside normal longitude range */
7169 	if (GMT->common.R.wesn[YLO] < -90.0 || GMT->common.R.wesn[YLO] > 90.0) y = 0;	/* Clearly outside normal latitude range */
7170 	if (GMT->common.R.wesn[YHI] < -90.0 || GMT->common.R.wesn[YHI] > 90.0) y = 0;	/* Clearly outside normal latitude range */
7171 	return x+y;	/* Might still be geographic for the purpose of parsing -I */
7172 }
7173 
7174 /*! . */
gmt_getincn(struct GMT_CTRL * GMT,char * line,double inc[],unsigned int n)7175 int gmt_getincn (struct GMT_CTRL *GMT, char *line, double inc[], unsigned int n) {
7176 	bool separate;
7177 	unsigned int last, i, pos, side = GMT_X, geo = gmtsupport_set_geo (GMT);	/* true unless clearly -R is Cartesian */
7178 	unsigned geo_bit[2] = {GMT_IS_LON, GMT_IS_LAT};
7179 	char p[GMT_BUFSIZ];
7180 	double scale = 1.0;
7181 
7182 	/* Deciphers dx/dy/dz/dw/du/dv/... increment strings with n items */
7183 
7184 	if (!line) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "No argument given to gmt_getincn\n"); GMT->parent->error = GMT_PARSE_ERROR; return -GMT_PARSE_ERROR; }
7185 
7186 	gmt_M_memset (inc, n, double);
7187 
7188 	i = pos = GMT->current.io.inc_code[GMT_X] = GMT->current.io.inc_code[GMT_Y] = 0;
7189 	separate = (strchr (line, '/') != NULL);
7190 	while (i < n && (gmt_strtok (line, "/", &pos, p))) {
7191 		last = (unsigned int)strlen (p) - 1;
7192 		if (last && p[last] == 'e' && p[last-1] == '+') {	/* +e: Let -I override -R */
7193 			p[last] = p[last-1] = 0;
7194 			if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_EXACT;
7195 			last -= 2;
7196 		}
7197 		else if (last && p[last] == '=') {	/* Obsolete =: Let -I override -R */
7198 			p[last] = 0;
7199 			if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_EXACT;
7200 			last--;
7201 		}
7202 		else if (last && p[last] == 'n' && p[last-1] == '+') {	/* +n: Number of nodes given, determine inc from domain (! added since documentation mentioned this once... */
7203 			p[last] = 0;
7204 			if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_NNODES;
7205 			last -= 2;
7206 		}
7207 		else if (p[last] == '+' || p[last] == '!') {	/* Obsolete + (! added since documentation mentioned this once...) */
7208 			p[last] = 0;
7209 			if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_NNODES;
7210 			if (last) last--;	/* Coverity rightly points out that if last == 0 it would become 4294967295 */
7211 		}
7212 		if (geo == 0 || (separate && (side <= GMT_Y && (geo & geo_bit[side]) == 0)) ) {	/* Gave a unit to a Cartesian axes that does not take any unit */
7213 			if (p[last] && strchr (GMT_LEN_UNITS "c", p[last])) {
7214 				if (separate) {	/* Report per axis since separate increments where given */
7215 					static char *A = "xyzvuw";
7216 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unit %c is ignored as the %c-axis is not geographic\n", p[last], A[i]);
7217 				}
7218 				else	/* Single message since common increment for all axes */
7219 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unit %c is ignored as no axis is geographic\n", p[last]);
7220 				p[last] = 0;
7221 			}
7222 		}
7223 		switch (p[last]) {
7224 			case 'd':	/* Gave arc degree */
7225 				p[last] = 0;
7226 				break;
7227 			case 'm':	/* Gave arc minutes */
7228 				p[last] = 0;
7229 				scale = GMT_MIN2DEG;
7230 				break;
7231 			case 'c':
7232 				if (gmt_M_compat_check (GMT, 4)) {
7233 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Second interval unit c is deprecated; use s instead\n");
7234 				}
7235 				else {
7236 					scale = 1.0;
7237 					break;
7238 				}
7239 				/* Intentionally fall through - to case 's' */
7240 			case 's':	/* Gave arc seconds */
7241 				p[last] = 0;
7242 				scale = GMT_SEC2DEG;
7243 				break;
7244 			case 'e':	/* Gave meters along mid latitude */
7245 				p[last] = 0;
7246 				if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_M;
7247 				break;
7248 			case 'f':	/* Gave feet along mid latitude */
7249 				p[last] = 0;
7250 				if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_FEET;
7251 				break;
7252 			case 'k':	/* Gave km along mid latitude */
7253 				p[last] = 0;
7254 				if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_KM;
7255 				break;
7256 			case 'M':	/* Gave miles along mid latitude */
7257 				p[last] = 0;
7258 				if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_MILES;
7259 				break;
7260 			case 'n':	/* Gave nautical miles along mid latitude */
7261 				p[last] = 0;
7262 				if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_NMILES;
7263 				break;
7264 			case 'u':	/* Gave survey feet along mid latitude */
7265 				p[last] = 0;
7266 				if (i < 2) GMT->current.io.inc_code[i] |= GMT_INC_IS_SURVEY_FEET;
7267 				break;
7268 			default:	/* No special flags or units */
7269 				scale = 1.0;
7270 				geo = false;
7271 				break;
7272 		}
7273 		if ((sscanf (p, "%lf", &inc[i])) != 1) {
7274 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to decode %s as a floating point number\n", p);
7275 			GMT->parent->error = GMT_PARSE_ERROR;
7276 			return -GMT_PARSE_ERROR;
7277 		}
7278 		inc[i] *= scale;
7279 		i++;	/* Goto next increment */
7280 		side++;
7281 	}
7282 	if (geo) {
7283 		if (geo == (GMT_IS_LON+GMT_IS_LAT))	/* Regular lon/lat region presumably */
7284 			gmt_set_geographic (GMT, GMT_IN);
7285 		else if (geo & GMT_IS_LON)
7286 			gmt_set_column_type (GMT, GMT_IN, GMT_X, GMT_IS_LON);
7287 		else if (geo & GMT_IS_LAT)
7288 			gmt_set_column_type (GMT, GMT_IN, GMT_Y, GMT_IS_LAT);
7289 	}
7290 
7291 	return (i);	/* Returns the number of increments found */
7292 }
7293 
gmtlib_conv_distance(struct GMT_CTRL * GMT,double value,char in_unit,char out_unit)7294 double gmtlib_conv_distance (struct GMT_CTRL *GMT, double value, char in_unit, char out_unit)
7295 {
7296 	/* Given the length in value and the unit of measure, convert from in_unit to out_unit.
7297 	 * When conversions between arc lengths and linear distances are used we assume a spherical Earth. */
7298 		unsigned int k;
7299 	char units[2];
7300 	double scale[2] = {1.0, 1.0};
7301 	units[GMT_IN] = in_unit;	units[GMT_OUT] = out_unit;
7302 	for (k = GMT_IN; k <= GMT_OUT; k++) {	/* Set in/out scales */
7303 		switch (units[k]) {
7304 			case 'd': scale[k] = GMT->current.proj.DIST_M_PR_DEG; break;			/* arc degree */
7305 			case 'm': scale[k] = GMT->current.proj.DIST_M_PR_DEG * GMT_MIN2DEG; break;	/* arc minutes */
7306 			case 's': scale[k] = GMT->current.proj.DIST_M_PR_DEG * GMT_SEC2DEG; break;	/* arc seconds */
7307 			case 'e': scale[k] = 1.0; break;						/* meters */
7308 			case 'f': scale[k] = METERS_IN_A_FOOT; break;					/* feet */
7309 			case 'k': scale[k] = METERS_IN_A_KM; break;					/* km */
7310 			case 'M': scale[k] = METERS_IN_A_MILE; break;					/* miles */
7311 			case 'n': scale[k] = METERS_IN_A_NAUTICAL_MILE; break;				/* nautical miles */
7312 			case 'u': scale[k] = METERS_IN_A_SURVEY_FOOT; break;				/* survey feet */
7313 			default:  break;								/* No units */
7314 		}
7315 	}
7316 	return (value * scale[GMT_IN] / scale[GMT_OUT]);	/* Do the conversion */
7317 }
7318 
7319 /*! . */
gmt_get_distance(struct GMT_CTRL * GMT,char * line,double * dist,char * unit)7320 int gmt_get_distance (struct GMT_CTRL *GMT, char *line, double *dist, char *unit) {
7321 	/* Accepts a distance length with optional unit character.  The
7322 	 * recognized units are:
7323 	 * e (meter), f (foot), M (mile), n (nautical mile), k (km), u(survey foot)
7324 	 * and d (arc degree), m (arc minute), s (arc second).
7325 	 * If no unit is found it means Cartesian data, unless -fg is set,
7326 	 * in which we default to meters.
7327 	 * Passes back the radius, the unit, and returns distance_flag:
7328 	 * flag = 0: Units are user Cartesian. Use Cartesian distances
7329 	 * flag = 1: Unit is d|e|f|k|m|M|n|s|u. Use Flat-Earth distances.
7330 	 * flag = 2: Unit is d|e|f|k|m|M|n|s|u. Use great-circle distances.
7331 	 * flag = 3: Unit is d|e|f|k|m|M|n|s|u. Use geodesic distances.
7332 	 * One of 2 modifiers may be prepended to the distance to control how
7333 	 * spherical distances are computed:
7334 	 *   - means less accurate; use Flat Earth approximation (fast).
7335 	 *   + means more accurate; use geodesic distances (slow).
7336 	 * Otherwise we use great circle distances (intermediate) [Default].
7337 	 * The calling program must call gmt_init_distaz with the
7338 	 * distance_flag and unit to set up the gmt_distance functions.
7339 	 * Distances computed will be in the unit selected.
7340 	 */
7341 	int last, d_flag = 1, start = 1, way;
7342 	char copy[GMT_LEN64] = {""};
7343 
7344 	/* Syntax:  -S<dist>[d|e|f|k|m|M|n|s|u]  */
7345 
7346 	if (!line) { GMT_Report (GMT->parent, GMT_MSG_ERROR, "No argument given to gmt_get_distance\n"); return (-1); }
7347 
7348 	strncpy (copy, line, GMT_LEN64-1);
7349 	*dist = GMT->session.d_NaN;
7350 
7351 	switch (copy[0]) {	/* Look for obsolete modifiers -/+ to set how spherical distances are computed */
7352 		case '-':	/* Want flat Earth calculations */
7353 			way = 0;
7354 			break;
7355 		case '+':	/* Want geodesic distances */
7356 			way = 2;
7357 			break;
7358 		default:	/* Default is great circle distances */
7359 			way = 1;
7360 			start = 0;
7361 			break;
7362 	}
7363 	if (way != 1) {
7364 		if (gmt_M_compat_check (GMT, 6))
7365 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Leading +|- to set distance mode is deprecated; use common option -j instead\n");
7366 		else if (way == 0) {
7367 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Negative distance given\n");
7368 			return (-1);
7369 		}
7370 	}
7371 
7372 	/* Extract the distance unit, if any */
7373 
7374 	last = (int)strlen (line) - 1;
7375 	if (strchr (GMT_LEN_UNITS GMT_OPT("c"), (int)copy[last])) {	/* Got a valid distance unit */
7376 		*unit = copy[last];
7377 		if (gmt_M_compat_check (GMT, 4) && *unit == 'c') {
7378 			GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Unit c is deprecated; use s instead\n");
7379 			*unit = 's';
7380 		}
7381 		copy[last] = '\0';	/* Chop off the unit */
7382 	}
7383 	else if (!strchr ("0123456789.", (int)copy[last])) {	/* Got an invalid distance unit */
7384 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Invalid distance unit (%c). Choose among %s\n", (int)copy[last], GMT_LEN_UNITS_DISPLAY);
7385 		return (-1);
7386 	}
7387 	else if (start == 1 || gmt_M_is_geographic (GMT, GMT_IN))	/* Indicated a spherical calculation mode (-|+) or -fg but appended no unit; default to meter */
7388 		*unit = GMT_MAP_DIST_UNIT;
7389 	else {	/* Cartesian, presumably */
7390 		*unit = 'X';
7391 		d_flag = way = 0;
7392 	}
7393 
7394 	/* Get the specified length */
7395 	if ((sscanf (&copy[start], "%lf", dist)) != 1) {
7396 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to decode %s as a floating point number.\n", &copy[start]);
7397 		return (-2);
7398 	}
7399 
7400 	if ((*dist) < 0.0) return (-3);	/* Quietly flag a negative distance */
7401 
7402 	return (d_flag + way);	/* Range 0-3 */
7403 }
7404 
7405 /*! . */
gmt_RI_prepare(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)7406 void gmt_RI_prepare (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
7407 	/* This routine adjusts the grid header. It computes the correct n_columns, n_rows, x_inc and y_inc,
7408 	   based on user input of the -I option and the current settings of x_min, x_max, y_min, y_max and registration.
7409 	   On output the grid boundaries are always gridline or pixel oriented, depending on registration.
7410 	   The routine is not run when n_columns and n_rows are already set.
7411 	*/
7412 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
7413 
7414 	h->xy_off = 0.5 * h->registration;	/* Use to calculate mean location of block */
7415 
7416 	gmt_increment_adjust (GMT, h->wesn, h->inc, h->registration);
7417 
7418 	/* Determine n_columns */
7419 	h->n_columns = gmt_M_grd_get_nx (GMT, h);
7420 	/* Determine n_rows */
7421 	h->n_rows = gmt_M_grd_get_ny (GMT, h);
7422 
7423 	/* Set the inverse increments */
7424 	HH->r_inc[GMT_X] = 1.0 / h->inc[GMT_X];
7425 	HH->r_inc[GMT_Y] = 1.0 / h->inc[GMT_Y];
7426 }
7427 
7428 /*! . */
gmt_increment_adjust(struct GMT_CTRL * GMT,double * wesn,double * inc,enum GMT_enum_reg registration)7429 void gmt_increment_adjust (struct GMT_CTRL *GMT, double *wesn, double *inc, enum GMT_enum_reg registration) {
7430 	/* This routine adjusts raw grid increments given with projected units and ensures they are compatible
7431 	 * with teh given domain boundaries.  Depending on -I settings we may need to adjust inc as well as
7432 	 * xmax and ymax.
7433 	*/
7434 	unsigned int one_or_zero, n_rows, n_columns;
7435 	double s;
7436 
7437 	if (GMT->current.io.inc_code[GMT_X] == 0 && GMT->current.io.inc_code[GMT_Y] == 0) return;	/* Nothing to do */
7438 
7439 	one_or_zero = !registration;
7440 
7441 	/* XINC AND XMIN/XMAX CHECK FIRST */
7442 
7443 	/* Adjust x_inc */
7444 
7445 	if (GMT->current.io.inc_code[GMT_X] & GMT_INC_IS_NNODES) {	/* Got n_columns */
7446 		int64_t n = lrint (inc[GMT_X]);
7447 		if (n <= 0 || !doubleAlmostEqual (inc[GMT_X], (double)n)) {
7448 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Your number of x-nodes %g is not a valid integer\n", inc[GMT_X]);
7449 		}
7450 		inc[GMT_X] = gmt_M_get_inc (GMT, wesn[XLO], wesn[XHI], n, registration);
7451 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Given n_columns implies x_inc = %g\n", inc[GMT_X]);
7452 	}
7453 	else if (GMT->current.io.inc_code[GMT_X] & GMT_INC_UNITS) {	/* Got funny units */
7454 		if (gmt_M_is_geographic (GMT, GMT_IN)) {
7455 			switch (GMT->current.io.inc_code[GMT_X] & GMT_INC_UNITS) {
7456 				case GMT_INC_IS_FEET:	/* foot */
7457 					s = METERS_IN_A_FOOT;
7458 					break;
7459 				case GMT_INC_IS_KM:	/* km */
7460 					s = METERS_IN_A_KM;
7461 					break;
7462 				case GMT_INC_IS_MILES:	/* Statute mile */
7463 					s = METERS_IN_A_MILE;
7464 					break;
7465 				case GMT_INC_IS_NMILES:	/* Nautical mile */
7466 					s = METERS_IN_A_NAUTICAL_MILE;
7467 					break;
7468 				case GMT_INC_IS_SURVEY_FEET:	/* US survey foot */
7469 					s = METERS_IN_A_SURVEY_FOOT;
7470 					break;
7471 				case GMT_INC_IS_M:	/* Meter */
7472 				default:
7473 					s = 1.0;
7474 					break;
7475 			}
7476 			inc[GMT_X] *= s / (GMT->current.proj.DIST_M_PR_DEG * cosd (0.5 * (wesn[YLO] + wesn[YHI])));	/* Latitude scaling of E-W distances */
7477 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Distance to degree conversion implies x_inc = %g\n", inc[GMT_X]);
7478 		}
7479 		else {
7480 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Cartesian x-increments are unit-less! - unit ignored\n");
7481 			GMT->current.io.inc_code[GMT_X] -= (GMT->current.io.inc_code[GMT_X] & GMT_INC_UNITS);
7482 		}
7483 	}
7484 	if (!(GMT->current.io.inc_code[GMT_X] & (GMT_INC_IS_NNODES | GMT_INC_IS_EXACT))) {	/* Adjust x_inc to exactly fit west/east */
7485 		s = wesn[XHI] - wesn[XLO];
7486 		n_columns = urint (s / inc[GMT_X]);
7487 		s /= n_columns;
7488 		if (fabs (s - inc[GMT_X]) > 0.0) {
7489 			inc[GMT_X] = s;
7490 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Given domain implies x_inc = %g\n", inc[GMT_X]);
7491 		}
7492 	}
7493 
7494 	if (GMT->current.io.inc_code[GMT_X] & GMT_INC_IS_EXACT) {	/* Want to keep x_inc exactly as given; adjust x_max accordingly */
7495 		/* Determine n_columns */
7496 		n_columns = gmt_M_get_n (GMT, wesn[XLO], wesn[XHI], inc[GMT_X], registration);
7497 		s = (wesn[XHI] - wesn[XLO]) - inc[GMT_X] * (n_columns - one_or_zero);
7498 		if (fabs (s) > 0.0) {
7499 			wesn[XHI] -= s;
7500 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "x_max adjusted to %g\n", wesn[XHI]);
7501 		}
7502 	}
7503 
7504 	/* YINC AND YMIN/YMAX CHECK SECOND */
7505 
7506 	/* Adjust y_inc */
7507 
7508 	if (GMT->current.io.inc_code[GMT_Y] & GMT_INC_IS_NNODES) {	/* Got n_rows */
7509 		int64_t n = lrint (inc[GMT_Y]);
7510 		if (n <= 0 || !doubleAlmostEqual (inc[GMT_Y], (double)n)) {
7511 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Your number of y-nodes %g is not a valid integer\n", inc[GMT_Y]);
7512 		}
7513 		inc[GMT_Y] = gmt_M_get_inc (GMT, wesn[YLO], wesn[YHI], n, registration);
7514 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Given n_rows implies y_inc = %g\n", inc[GMT_Y]);
7515 	}
7516 	else if (GMT->current.io.inc_code[GMT_Y] & GMT_INC_UNITS) {	/* Got funny units */
7517 		if (gmt_M_is_geographic (GMT, GMT_IN)) {
7518 			switch (GMT->current.io.inc_code[GMT_Y] & GMT_INC_UNITS) {
7519 				case GMT_INC_IS_FEET:	/* feet */
7520 					s = METERS_IN_A_FOOT;
7521 					break;
7522 				case GMT_INC_IS_KM:	/* km */
7523 					s = METERS_IN_A_KM;
7524 					break;
7525 				case GMT_INC_IS_MILES:	/* miles */
7526 					s = METERS_IN_A_MILE;
7527 					break;
7528 				case GMT_INC_IS_NMILES:	/* nmiles */
7529 					s = METERS_IN_A_NAUTICAL_MILE;
7530 					break;
7531 				case GMT_INC_IS_SURVEY_FEET:	/* US survey feet */
7532 					s = METERS_IN_A_SURVEY_FOOT;
7533 					break;
7534 				case GMT_INC_IS_M:	/* Meter */
7535 				default:
7536 					s = 1.0;
7537 					break;
7538 			}
7539 			inc[GMT_Y] = (inc[GMT_Y] == 0.0) ? inc[GMT_X] : inc[GMT_Y] * s / GMT->current.proj.DIST_M_PR_DEG;
7540 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Distance to degree conversion implies y_inc = %g\n", inc[GMT_Y]);
7541 		}
7542 		else {
7543 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Cartesian y-increments are unit-less! - unit ignored\n");
7544 			GMT->current.io.inc_code[GMT_Y] -= (GMT->current.io.inc_code[GMT_Y] & GMT_INC_UNITS);
7545 		}
7546 	}
7547 	if (!(GMT->current.io.inc_code[GMT_Y] & (GMT_INC_IS_NNODES | GMT_INC_IS_EXACT))) {	/* Adjust y_inc to exactly fit south/north */
7548 		s = wesn[YHI] - wesn[YLO];
7549 		n_rows = urint (s / inc[GMT_Y]);
7550 		s /= n_rows;
7551 		if (fabs (s - inc[GMT_Y]) > 0.0) {
7552 			inc[GMT_Y] = s;
7553 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Given domain implies y_inc = %g\n", inc[GMT_Y]);
7554 		}
7555 	}
7556 
7557 	if (GMT->current.io.inc_code[GMT_Y] & GMT_INC_IS_EXACT) {	/* Want to keep y_inc exactly as given; adjust y_max accordingly */
7558 		/* Determine n_rows */
7559 		n_rows = gmt_M_get_n (GMT, wesn[YLO], wesn[YHI], inc[GMT_Y], registration);
7560 		s = (wesn[YHI] - wesn[YLO]) - inc[GMT_Y] * (n_rows - one_or_zero);
7561 		if (fabs (s) > 0.0) {
7562 			wesn[YHI] -= s;
7563 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "y_max adjusted to %g\n", wesn[YHI]);
7564 		}
7565 	}
7566 
7567 	GMT->current.io.inc_code[GMT_X] = GMT->current.io.inc_code[GMT_Y] = 0;	/* Processed, so resetting these */
7568 }
7569 
7570 /*! . */
gmtlib_create_palette(struct GMT_CTRL * GMT,uint64_t n_colors)7571 struct GMT_PALETTE * gmtlib_create_palette (struct GMT_CTRL *GMT, uint64_t n_colors) {
7572 	/* Makes an empty palette table with a blank hidden struct */
7573 	struct GMT_PALETTE *P = gmt_M_memory (GMT, NULL, 1, struct GMT_PALETTE);
7574 	struct GMT_PALETTE_HIDDEN *PH = gmt_M_memory (GMT, NULL, 1, struct GMT_PALETTE_HIDDEN);
7575 	P->hidden = PH;
7576 	if (n_colors > 0) P->data = gmt_M_memory (GMT, NULL, n_colors, struct GMT_LUT);
7577 	P->n_colors = (unsigned int)n_colors;
7578 	PH->alloc_mode = GMT_ALLOC_INTERNALLY;		/* Memory can be freed by GMT. */
7579 	PH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level. */
7580 	PH->id = GMT->parent->unique_var_ID++;		/* Give unique identifier */
7581 
7582 	return (P);
7583 }
7584 
7585 /*! . */
gmtlib_free_cpt_ptr(struct GMT_CTRL * GMT,struct GMT_PALETTE * P)7586 void gmtlib_free_cpt_ptr (struct GMT_CTRL *GMT, struct GMT_PALETTE *P) {
7587 	unsigned int i;
7588 	struct GMT_PALETTE_HIDDEN *PH = NULL;
7589 	if (!P) return;
7590 	/* Frees all memory used by this palette but does not free the palette itself */
7591 	PH = gmt_get_C_hidden (P);
7592 	for (i = 0; i < P->n_colors; i++) {
7593 		gmtsupport_free_range (GMT, PH, &P->data[i]);
7594 	}
7595 	for (i = 0; i < 3; i++)
7596 		if (P->bfn[i].fill)
7597 			gmt_M_free (GMT, P->bfn[i].fill);
7598 	gmt_M_free (GMT, P->data);
7599 	/* Use free() to free the headers since they were allocated with strdup */
7600 	if (P->n_headers) {
7601 		for (i = 0; i < P->n_headers; i++) gmt_M_str_free (P->header[i]);
7602 		gmt_M_free (GMT, P->header);
7603 	}
7604 	P->n_headers = P->n_colors = 0;
7605 	gmt_M_free (GMT, P->hidden);
7606 }
7607 
7608 /*! . */
gmtlib_copy_palette(struct GMT_CTRL * GMT,struct GMT_PALETTE * P_to,struct GMT_PALETTE * P_from)7609 void gmtlib_copy_palette (struct GMT_CTRL *GMT, struct GMT_PALETTE *P_to, struct GMT_PALETTE *P_from) {
7610 	unsigned int i;
7611 	/* Copies the information from P_from to P_to */
7612 	P_to->n_headers = P_from->n_headers;		/* Number of CPT header records (0 if no header) */
7613 	P_to->n_colors = P_from->n_colors;			/* Number of colors in CPT lookup table */
7614 	P_to->mode = P_from->mode;					/* Flags controlling use of BFN colors */
7615 	P_to->model = P_from->model;				/* RGB, HSV, CMYK */
7616 	P_to->is_wrapping = P_from->is_wrapping;	/* If 1 then we must wrap around to find color - can never be F or B */
7617 	P_to->is_gray = P_from->is_gray;			/* 1 if only grayshades are needed */
7618 	P_to->is_bw = P_from->is_bw;				/* 1 if only black and white are needed */
7619 	P_to->is_continuous = P_from->is_continuous;	/* 1 if continuous color tables have been given */
7620 	P_to->has_pattern = P_from->has_pattern;	/* 1 if CPT contains any patterns */
7621 	P_to->has_hinge = P_from->has_hinge;		/* 1 if CPT is hinged at hinge (below) */
7622 	P_to->has_range = P_from->has_range;		/* 1 if CPT has a natural range (minmax below) */
7623 	P_to->categorical = P_from->categorical;	/* 1 (number) or 2 (key) if CPT applies to categorical data */
7624 	P_to->hinge = P_from->hinge;				/* z-value for hinged CPTs */
7625 	P_to->wrap_length = P_from->wrap_length;	/* z-length of active CPT */
7626 	gmt_M_memcpy (P_to->minmax, P_from->minmax, 2, double);	/* Min/max z-value for a default range, if given */
7627 
7628 	gmt_M_memcpy (P_to->hidden, P_from->hidden, 1, struct GMT_PALETTE_HIDDEN);
7629 	gmt_M_memcpy (P_to->data, P_from->data, P_to->n_colors, struct GMT_LUT);
7630 	gmt_M_memcpy (P_to->bfn, P_from->bfn, 3, struct GMT_BFN);
7631 	for (i = 0; i < 3; i++) {
7632 		P_to->bfn[i].fill = NULL;	/* Reset junk pointer from the memcopy */
7633 		if (P_from->bfn[i].fill) {
7634 			P_to->bfn[i].fill = gmt_M_memory (GMT, NULL, 1, struct GMT_FILL);
7635 			gmt_M_memcpy (P_to->bfn[i].fill, P_from->bfn[i].fill, 1, struct GMT_FILL);
7636 		}
7637 	}
7638 	for (i = 0; i < P_from->n_colors; i++) {
7639 		P_to->data[i].fill = NULL;	/* Reset junk pointer from the memcopy */
7640 		if (P_from->data[i].fill) {
7641 			P_to->data[i].fill = gmt_M_memory (GMT, NULL, 1, struct GMT_FILL);
7642 			gmt_M_memcpy (P_to->data[i].fill, P_from->data[i].fill, 1, struct GMT_FILL);
7643 		}
7644 		P_to->data[i].key = NULL;
7645 		P_to->data[i].label = NULL;
7646 		if (P_from->data[i].label) P_to->data[i].label = strdup (P_from->data[i].label);
7647 		if (P_from->data[i].key) P_to->data[i].key = strdup (P_from->data[i].key);
7648 	}
7649 	GMT->current.setting.color_model = P_to->model;
7650 	gmtsupport_copy_palette_hdrs (GMT, P_to, P_from);
7651 }
7652 
7653 /*! . */
gmtlib_duplicate_palette(struct GMT_CTRL * GMT,struct GMT_PALETTE * P_from,unsigned int mode)7654 struct GMT_PALETTE * gmtlib_duplicate_palette (struct GMT_CTRL *GMT, struct GMT_PALETTE *P_from, unsigned int mode) {
7655 	/* Mode not used yet */
7656 	struct GMT_PALETTE *P = gmtlib_create_palette (GMT, P_from->n_colors);
7657 	gmt_M_unused(mode);
7658 	gmtlib_copy_palette (GMT, P, P_from);
7659 	return (P);
7660 }
7661 
7662 /*! . */
gmtlib_free_palette(struct GMT_CTRL * GMT,struct GMT_PALETTE ** P)7663 void gmtlib_free_palette (struct GMT_CTRL *GMT, struct GMT_PALETTE **P) {
7664 	gmtlib_free_cpt_ptr (GMT, *P);
7665 	gmt_M_free (GMT, *P);
7666 	*P = NULL;
7667 }
7668 
7669 /*! Adds listing of available GMT cpt choices to a program's usage message */
gmt_list_cpt(struct GMT_CTRL * GMT,char option)7670 int gmt_list_cpt (struct GMT_CTRL *GMT, char option) {
7671 	char line[GMT_LEN256] = {""};
7672 	char divider[110] = {"-----------------------------------------------------------------------------------------------------------"};
7673 	struct GMTAPI_CTRL *API = GMT->parent;
7674 	/* Note: 108 is the max length of entries in gmt_cpt_masters.h.  Update 110 and 108 if that changes */
7675 	int L = MAX (MIN (API->terminal_width-5, 108), 0);	/* Number of dashes to print in divider line */
7676 	GMT_Usage (API, 1, "\n-%c Specify a colortable [Default is %s]:", option, GMT->current.setting.cpt);
7677 	GMT_Usage (API, 2, "[Legend: R = Default z-range, H = Hard Hinge, S = Soft Hinge, C = Colormodel]");
7678 	divider[L] = '\0';	/* Truncate the line */
7679 	gmt_message (GMT, "     %s\n", divider);
7680 	for (unsigned int k = 0; k < GMT_N_CPT_MASTERS; k++) {
7681 		strncpy (line, GMT_CPT_master[k], GMT_LEN256);
7682 		char *c = strchr (line, ':');	/* Find the start of the info */
7683 		c[0] = '\0';
7684 		gmt_message (GMT, "     %s: ", line);
7685 		GMT_Usage (API, -19, "%s", &c[2]);
7686 	}
7687 	gmt_message (GMT, "     %s\n", divider);
7688 	GMT_Usage (API, 2, "[For more, visit soliton.vm.bytemark.co.uk/pub/cpt-city and www.fabiocrameri.ch/visualisation.php]. "
7689 		"Alternatively, specify -Ccolor1,color2[,color3,...] to build a linear "
7690 		"continuous CPT from those colors automatically.");
7691 
7692 	return (GMT_NOERROR);
7693 }
7694 
gmtsupport_cpt_master_index(struct GMT_CTRL * GMT,char * name)7695 GMT_LOCAL bool gmtsupport_cpt_master_index (struct GMT_CTRL *GMT, char *name) {
7696 	size_t len;
7697 	gmt_M_unused(GMT);
7698 	if (name == NULL) return true;	/* true, because no name means we default to GMT->current.setting.cpt */
7699 	len = strlen (name);	/* Length of the master table name so we can limit comparison to just those characters */
7700 	/* Note: THere are near-duplicate names like broc and brocO, but since they are ordered alphabetically our
7701 	 * search for broc will first compare with broc before broc0 so not an issue. */
7702 	for (unsigned int k = 0; k < GMT_N_CPT_MASTERS; k++) if (!strncmp (name, GMT_CPT_master[k], len))
7703 		return true;
7704 	return false;
7705 }
7706 
7707 /*! . */
gmtsupport_make_continuous_colorlist(struct GMT_CTRL * GMT,struct GMT_PALETTE * P)7708 GMT_LOCAL void gmtsupport_make_continuous_colorlist (struct GMT_CTRL *GMT, struct GMT_PALETTE *P) {
7709 	/* Convert a (by default) discrete CPT made from a color list to a continuous CPT instead */
7710 	unsigned int k, i;
7711 	gmt_M_unused(GMT);
7712 	if (P->is_continuous) return;	/* Nothing to do */
7713 	P->n_colors--;	/* One less slice */
7714 	for (k = 0; k < P->n_colors; k++) {	/* Copy next low color to previous high color */
7715 		gmt_M_rgb_copy (P->data[k].rgb_high, P->data[k+1].rgb_low);
7716 		gmt_M_rgb_copy (P->data[k].hsv_high, P->data[k+1].hsv_low);
7717 		/* Update color differences for interpolation function later (dz remains the same == 1) */
7718 		for (i = 0; i < 4; i++) P->data[k].rgb_diff[i] = P->data[k].rgb_high[i] - P->data[k].rgb_low[i];
7719 		for (i = 0; i < 4; i++) P->data[k].hsv_diff[i] = P->data[k].hsv_high[i] - P->data[k].hsv_low[i];
7720 	}
7721 	P->is_continuous = true;	/* Flag this as a continuous CPT */
7722 }
7723 
7724 /*! . */
gmt_validate_cpt_parameters(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,char * file,bool * interpolate,bool * force_continuous)7725 unsigned int gmt_validate_cpt_parameters (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, char *file, bool *interpolate, bool *force_continuous) {
7726 	if (P->mode & GMT_CPT_COLORLIST && !P->categorical && !(*interpolate) && P->n_colors > 1) {	/* Color list with -T/min/max should be seen as continuous */
7727 		*force_continuous = true, P->mode |= GMT_CPT_CONTINUOUS;
7728 		gmtsupport_make_continuous_colorlist (GMT, P);
7729 	}
7730 	if (*interpolate) {
7731 		if (!P->is_continuous && !(P->mode & GMT_CPT_COLORLIST)) {
7732 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "%s is a discrete CPT. You can stretch it (-T<min>/<max>) but not interpolate it (-T<min>/<max>/<inc>).\n", file);
7733 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Selecting the given range and ignoring the increment setting.\n");
7734 			*interpolate = false;
7735 		}
7736 	}
7737 	else {	/* Did not request resampling */
7738 		if (P->categorical) {
7739 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "%s is a special categorical, discrete CPT. You can select a subset only via (-Tmin/max/inc).\n", file);
7740 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "This will yield a subset categorical CPT with [(max-min)/inc] - 1 entries.\n", file);
7741 			return GMT_RUNTIME_ERROR;
7742 		}
7743 	}
7744 	return GMT_NOERROR;
7745 }
7746 
gmt_cat_cpt_strings(struct GMT_CTRL * GMT,char * in_label,unsigned int n,unsigned int * n_set)7747 char ** gmt_cat_cpt_strings (struct GMT_CTRL *GMT, char *in_label, unsigned int n, unsigned int *n_set) {
7748 	/* Generate categorical labels for n categories from the label magic argument.
7749 	 * Note: If n = 12 and label = M then we create month names.
7750 	 * Note: If n = 7 and label = D then we create day names
7751 	 */
7752 	unsigned int k = 0, kind = 0;
7753 	bool upper = false;
7754 	char all_items[12*GMT_LEN16] = {""};
7755 	char *label = NULL, **Clabel = gmt_M_memory (GMT, NULL, n, char *);
7756 
7757 	if (n == 12 && !strcmp (in_label, "M")) {	/* Create a month-list from current defaults */
7758 		gmtlib_set_case_and_kind (GMT, GMT->current.setting.format_time[GMT_PRIMARY], &upper, &kind);
7759 		strcpy (all_items, GMT->current.language.month_name[kind][0]);
7760 		for (k = 1; k < 12; k++) {	/* Append comma-separated list of months in current language, format */
7761 			strcat (all_items, ",");
7762 			strcat (all_items, GMT->current.language.month_name[kind][k]);
7763 		}
7764 		if (upper) gmt_str_toupper (all_items);
7765 		label = all_items;
7766 	}
7767 	else if (n == 7 && !strcmp (in_label, "D")) {	/* Create a weekday-list from current defaults */
7768 		unsigned int day;
7769 		gmtlib_set_case_and_kind (GMT, GMT->current.setting.format_time[GMT_PRIMARY], &upper, &kind);
7770 		day = (GMT->current.setting.time_week_start + GMT_WEEK2DAY_I) % GMT_WEEK2DAY_I;	/* Wrap around */
7771 		strcpy (all_items, GMT->current.language.day_name[kind][day]);
7772 		for (k = 1; k < 7; k++) {	/* Append comma-separated list of weekdays in current language, format */
7773 			day = (k + GMT->current.setting.time_week_start + GMT_WEEK2DAY_I) % GMT_WEEK2DAY_I;	/* Wrap around */
7774 			strcat (all_items, ",");
7775 			strcat (all_items, GMT->current.language.day_name[kind][day]);
7776 		}
7777 		if (upper) gmt_str_toupper (all_items);
7778 		label = all_items;
7779 	}
7780 	else 	/* Pass what we were given as is */
7781 		label = in_label;
7782 	if (strchr (label, ',')) {	/* Got list of category names */
7783 		char *word = NULL, *trail = NULL, *orig = strdup (label);
7784 		trail = orig;	k = 0;
7785 		while ((word = strsep (&trail, ",")) != NULL && k < n) {
7786 			if (*word != '\0')	/* Skip empty strings */
7787 				Clabel[k] = strdup (word);
7788 			k++;
7789 		}
7790 		gmt_M_str_free (orig);
7791 		if (k != n) {
7792 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "The comma-separated string %s had %d entries but %d were expected\n", label, k, n);
7793 		}
7794 	}
7795 	else {	/* Auto-build the labels */
7796 		unsigned int mode;
7797 		int start;
7798 		char string[GMT_LEN64] = {""};
7799 		if (isdigit (label[0])) {	/* Integer categories */
7800 			mode = 1;
7801 			start = atoi (label);
7802 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Building %d sequential strings from integers starting at %d\n", n, start);
7803 		}
7804 		else {	/* Letter categories */
7805 			mode = 3;
7806 			start = label[0];
7807 			if (strlen (label) > 1)
7808 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Expected a single letter to initialize auto-labels but found %s.\n", label);
7809 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Building %d sequential strings from letters starting at %c\n", n, start);
7810 		}
7811 		if (label[strlen(label)-1] == '-') mode++;	/* Wants a range */
7812 		for (k = 0; k < n; k++) {
7813 			switch (mode) {
7814 				case 1:	/* Single integer label */
7815 					sprintf (string, "%d", start+k);
7816 					break;
7817 				case 2:	/* Integer range label */
7818 					sprintf (string, "%d-%d", start+k, start+k+1);
7819 					break;
7820 				case 3:	/* Single letter label */
7821 					sprintf (string, "%c", start+k);
7822 					break;
7823 				case 4:	/* Character range label */
7824 					sprintf (string, "%c-%c", start+k, start+k+1);
7825 					break;
7826 			}
7827 			Clabel[k] = strdup (string);
7828 		}
7829 	}
7830 	*n_set = k;	/* How many we actually set */
7831 	return Clabel;
7832 }
7833 
7834 /*! . */
gmtlib_read_cpt(struct GMT_CTRL * GMT,void * source,unsigned int source_type,unsigned int cpt_flags)7835 struct GMT_PALETTE * gmtlib_read_cpt (struct GMT_CTRL *GMT, void *source, unsigned int source_type, unsigned int cpt_flags) {
7836 	/* Opens and reads a color palette file in RGB, HSV, or CMYK of arbitrary length.
7837 	 * Return the result as a palette struct.
7838 	 * source_type can be GMT_IS_[FILE|STREAM|FDESC]
7839 	 * cpt_flags is a combination of:
7840 	 * GMT_CPT_NO_BNF = Suppress reading BFN (i.e. use parameter settings)
7841 	 * GMT_CPT_EXTEND_BNF = Make B and F equal to low and high color
7842 	 */
7843 
7844 	unsigned int n = 0, i, nread, annot, id, n_cat_records = 0, color_model, hinge_mode = 0;
7845 	size_t k;
7846 	bool gap, overlap, error = false, close_file = false, check_headers = true;
7847 	size_t n_alloc = GMT_SMALL_CHUNK, n_hdr_alloc = 0;
7848 	double dz, z_hinge;
7849 	char T0[GMT_LEN64] = {""}, T1[GMT_LEN64] = {""}, T2[GMT_LEN64] = {""}, T3[GMT_LEN64] = {""}, T4[GMT_LEN64] = {""};
7850 	char T5[GMT_LEN64] = {""}, T6[GMT_LEN64] = {""}, T7[GMT_LEN64] = {""}, T8[GMT_LEN64] = {""}, T9[GMT_LEN64] = {""};
7851 	char line[GMT_BUFSIZ] = {""}, clo[GMT_LEN64] = {""}, chi[GMT_LEN64] = {""}, c, cpt_file[PATH_MAX] = {""};
7852 	char *name = NULL, *h = NULL;
7853 	FILE *fp = NULL;
7854 	struct GMT_PALETTE *X = NULL;
7855 	struct GMT_PALETTE_HIDDEN *XH = NULL;
7856 	struct CPT_Z_SCALE *Z = NULL;	/* For unit manipulations */
7857 
7858 	/* Determine input source */
7859 
7860 	if (source_type == GMT_IS_FILE) {	/* source is a file name */
7861 		struct stat S;
7862 		strncpy (cpt_file, source, PATH_MAX-1);
7863 		Z = gmtsupport_cpt_parse (GMT, cpt_file, GMT_IN, &hinge_mode, &z_hinge);
7864 		if (strncmp (cpt_file, "/dev/fd/", 8U) && stat (cpt_file, &S) == 0 && S.st_size == 0) {	/* Exclude process substitution files in /dev/fd */
7865 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Color palette table %s is empty\n", cpt_file);
7866 			gmt_M_free (GMT, Z);
7867 			return (NULL);
7868 		}
7869 		if ((fp = fopen (cpt_file, "r")) == NULL) {
7870 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open color palette table %s\n", cpt_file);
7871 			gmt_M_free (GMT, Z);
7872 			return (NULL);
7873 		}
7874 		close_file = true;	/* We only close files we have opened here */
7875 
7876 	}
7877 	else if (source_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
7878 		fp = (FILE *)source;
7879 		if (fp == NULL) fp = GMT->session.std[GMT_IN];	/* Default input */
7880 		if (fp == GMT->session.std[GMT_IN])
7881 			strcpy (cpt_file, "<stdin>");
7882 		else
7883 			strcpy (cpt_file, "<input stream>");
7884 	}
7885 	else if (source_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
7886 		int *fd = source;
7887 		if (fd && (fp = fdopen (*fd, "r")) == NULL) {
7888 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert file descriptor %d to stream in gmtlib_read_cpt\n", *fd);
7889 			return (NULL);
7890 		}
7891 		else
7892 			close_file = true;	/* fdopen allocates memory */
7893 		if (fd == NULL) fp = GMT->session.std[GMT_IN];	/* Default input */
7894 		if (fp == GMT->session.std[GMT_IN])
7895 			strcpy (cpt_file, "<stdin>");
7896 		else
7897 			strcpy (cpt_file, "<input file descriptor>");
7898 	}
7899 	else {
7900 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtlib_read_cpt\n", source_type);
7901 		return (NULL);
7902 	}
7903 
7904 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reading CPT from %s\n", cpt_file);
7905 
7906 	X = gmtlib_create_palette (GMT, 0);
7907 	X->data = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_LUT);
7908 	X->mode = cpt_flags;	/* Maybe limit what to do with BFN selections */
7909 	color_model = GMT->current.setting.color_model;		/* Save the original setting since it may be modified by settings in the CPT */
7910 	/* Also: GMT->current.setting.color_model is used in some rgb_to_xxx functions so it must be set if changed by cpt */
7911 	X->is_gray = X->is_bw = true;	/* May be changed when reading the actual colors */
7912 	XH = gmt_get_C_hidden (X);
7913 	/* Set default BFN colors; these may be overwritten by things in the CPT */
7914 	for (id = 0; id < 3; id++) {
7915 		gmt_M_rgb_copy (X->bfn[id].rgb, GMT->current.setting.color_patch[id]);
7916 		gmt_rgb_to_hsv (X->bfn[id].rgb, X->bfn[id].hsv);
7917 		if (X->bfn[id].rgb[0] == -1.0) X->bfn[id].skip = true;
7918 		if (X->is_gray && !gmt_M_is_gray (X->bfn[id].rgb)) X->is_gray = X->is_bw = false;
7919 		if (X->is_bw && !gmt_M_is_bw(X->bfn[id].rgb)) X->is_bw = false;
7920 	}
7921 
7922 	while (!error && fgets (line, GMT_BUFSIZ, fp)) {
7923 		gmt_strstrip (line, true);
7924 		c = line[0];
7925 
7926 		if (strstr (line, "COLOR_MODEL")) {	/* CPT overrides default color model */
7927 			if (strstr (line, "+RGB") || strstr (line, "rgb"))
7928 				X->model = GMT_RGB | GMT_COLORINT;
7929 			else if (strstr (line, "RGB"))
7930 				X->model = GMT_RGB;
7931 			else if (strstr (line, "+HSV") || strstr (line, "hsv"))
7932 				X->model = GMT_HSV | GMT_COLORINT;
7933 			else if (strstr (line, "HSV"))
7934 				X->model = GMT_HSV;
7935 			else if (strstr (line, "+CMYK") || strstr (line, "cmyk"))
7936 				X->model = GMT_CMYK | GMT_COLORINT;
7937 			else if (strstr (line, "CMYK"))
7938 				X->model = GMT_CMYK;
7939 			else {
7940 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized COLOR_MODEL in color palette table %s\n", cpt_file);
7941 				if (close_file) fclose (fp);
7942 				if (Z) gmt_M_free (GMT, Z);
7943 				gmtlib_free_palette (GMT, &X);
7944 				return (NULL);
7945 			}
7946 			continue;	/* Don't want this instruction to be also kept as a comment */
7947 		}
7948 		else if (strstr (line, "HINGE")) {	/* CPT has either a soft or hard hinge */
7949 			if (strstr (line, "HINGE =")) {	/* Bad mix of old CPTs with new GMT parsing - treat as hard hinge */
7950 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Mixing old CPT master tables with HINGE = <value> in %s.  Interpreted as HARD_HINGE.\n", cpt_file);
7951 				X->mode |= GMT_CPT_HARD_HINGE;
7952 				X->has_hinge = 1;
7953 			}
7954 			else if (strstr (line, "HARD_HINGE")) {	/* Hard hinge is always active */
7955 				X->mode |= GMT_CPT_HARD_HINGE;
7956 				X->has_hinge = 1;
7957 			}
7958 			else if (strstr (line, "SOFT_HINGE"))	/* Soft hinge the user can activate to a hard hinge via +h[<value>] */
7959 				X->mode |= GMT_CPT_SOFT_HINGE;
7960 			continue;	/* Don't want this instruction to be also kept as a comment */
7961 		}
7962 		else if ((h = strstr (line, "RANGE ="))) {	/* CPT has a default range */
7963 			k = 7;	while (h[k] == ' ' || h[k] == '\t') k++;	/* Skip any leading spaces or tabs */
7964 			if (sscanf (&h[k], "%[^/]/%s", T1, T2) != 2) {
7965 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not parse RANGE [%s] in %s\n", &h[7], cpt_file);
7966 				if (close_file) fclose (fp);
7967 				if (Z) gmt_M_free (GMT, Z);
7968 				gmtlib_free_palette (GMT, &X);
7969 				return (NULL);
7970 			}
7971 			gmt_scanf_arg (GMT, T1, GMT_IS_UNKNOWN, false, &X->minmax[0]);
7972 			gmt_scanf_arg (GMT, T2, GMT_IS_UNKNOWN, false, &X->minmax[1]);
7973 			X->has_range = 1;
7974 			continue;	/* Don't want this instruction to be also kept as a comment */
7975 		}
7976 		else if ((h = strstr (line, "CYCLIC")))	/* CPT should wrap around */
7977 			X->is_wrapping = 1;
7978 		else if ((h = strstr (line, "ENABLE_B_OPTION")))	/* CPT was stretched to exact min/max with no dz rounding */
7979 			XH->auto_scale = 1;
7980 		else if ((h = strstr (line, "COLOR_LIST")))	/* CPT was created from a list of colors */
7981 			X->mode |= GMT_CPT_COLORLIST;
7982 
7983 		GMT->current.setting.color_model = X->model;
7984 
7985 		if (c == '#') {	/* Possibly a header/comment record */
7986 			if (GMT->common.h.mode == GMT_COMMENT_IS_RESET) continue;	/* Simplest way to replace headers on output is to ignore them on input */
7987 			if (!check_headers) continue;	/* Done with the initial header records */
7988 			if (n_hdr_alloc == 0) X->header = gmt_M_memory (GMT, X->header, (n_hdr_alloc = GMT_TINY_CHUNK), char *);
7989 			X->header[X->n_headers] = strdup (line);
7990 			X->n_headers++;
7991 			if (X->n_headers >= n_hdr_alloc) X->header = gmt_M_memory (GMT, X->header, (n_hdr_alloc += GMT_TINY_CHUNK), char *);
7992 			continue;
7993 		}
7994 		if (c == '\0' || c == '\n') continue;	/* Comment or blank */
7995 		check_headers = false;	/* First non-comment record signals the end of headers */
7996 
7997 		gmt_chop (line);	/* Chop '\r\n' */
7998 
7999 		T1[0] = T2[0] = T3[0] = T4[0] = T5[0] = T6[0] = T7[0] = T8[0] = T9[0] = 0;
8000 		switch (c) {	/* Must check that B,F,N is a single word */
8001 			case 'B':
8002 				id = (line[1] == ' ' || line[1] == '\t') ? GMT_BGD : 3;
8003 				break;
8004 			case 'F':
8005 				id = (line[1] == ' ' || line[1] == '\t') ? GMT_FGD : 3;
8006 				break;
8007 			case 'N':
8008 				id = (line[1] == ' ' || line[1] == '\t') ? GMT_NAN : 3;
8009 				break;
8010 			default:
8011 				id = 3;
8012 				break;
8013 		}
8014 
8015 		if (id <= GMT_NAN) {	/* Foreground, background, or nan color */
8016 			if (X->mode & GMT_CPT_NO_BNF) continue; /* Suppress parsing B, F, N lines when bit 0 of X->mode is set */
8017 			X->bfn[id].skip = false;
8018 			if ((nread = sscanf (&line[2], "%s %s %s %s", T1, T2, T3, T4)) < 1) error = true;
8019 			if (T1[0] == '-')	/* Skip this slice */
8020 				X->bfn[id].skip = true;
8021 			else if (gmtsupport_is_pattern (GMT, T1)) {	/* Gave a pattern */
8022 				X->bfn[id].fill = gmt_M_memory (GMT, NULL, 1, struct GMT_FILL);
8023 				if (gmt_getfill (GMT, T1, X->bfn[id].fill)) {
8024 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "CPT Pattern fill (%s) not understood!\n", T1);
8025 					if (close_file) fclose (fp);
8026 					if (Z) gmt_M_free (GMT, Z);
8027 					gmtlib_free_palette (GMT, &X);
8028 					return (NULL);
8029 				}
8030 				X->has_pattern = true;
8031 				if ((name = gmtsupport_get_userimagename (GMT, T1, cpt_file))) {	/* Must replace fill->pattern with this full path */
8032 					strncpy (X->bfn[id].fill->pattern, name, PATH_MAX-1);
8033 					gmt_M_str_free (name);
8034 				}
8035 			}
8036 			else {	/* Shades, RGB, HSV, or CMYK */
8037 				if (nread == 1)	/* Gray shade */
8038 					snprintf (clo, GMT_LEN64, "%s", T1);
8039 				else if (X->model & GMT_CMYK)
8040 					snprintf (clo, GMT_LEN64, "%s/%s/%s/%s", T1, T2, T3, T4);
8041 				else if (X->model & GMT_HSV)
8042 					snprintf (clo, GMT_LEN64, "%s-%s-%s", T1, T2, T3);
8043 				else
8044 					snprintf (clo, GMT_LEN64, "%s/%s/%s", T1, T2, T3);
8045 				if (X->model & GMT_HSV) {
8046 					if (gmtsupport_gethsv (GMT, clo, X->bfn[id].hsv)) error = true;
8047 					gmt_hsv_to_rgb (X->bfn[id].rgb, X->bfn[id].hsv);
8048 				}
8049 				else {
8050 					if (gmt_getrgb (GMT, clo, X->bfn[id].rgb)) error = true;
8051 					gmt_rgb_to_hsv (X->bfn[id].rgb, X->bfn[id].hsv);
8052 				}
8053 				if (X->is_gray && !gmt_M_is_gray (X->bfn[id].rgb)) X->is_gray = X->is_bw = false;
8054 				if (X->is_bw && !gmt_M_is_bw(X->bfn[id].rgb)) X->is_bw = false;
8055 			}
8056 			continue;
8057 		}
8058 
8059 		if (hinge_mode && ((X->mode & GMT_CPT_SOFT_HINGE) || (X->mode & GMT_CPT_HARD_HINGE))) { /* Activate a soft hinge for the CPT and/or adjust the hinge */
8060 			X->has_hinge = 1;
8061 			X->hinge = z_hinge;	/* This is now the user-selected hinge value */
8062 		}
8063 
8064 		/* Here we have regular z-slices.  Allowable formats are
8065 		 *
8066 		 * key <fill> [;<label>]	for categorical data
8067 		 * z0 - z1 - [LUB] [;<label>]
8068 		 * z0 pattern z1 - [LUB] [;<label>]
8069 		 * z0 r0 z1 r1 [LUB] [;<label>]
8070 		 * z0 r0 g0 b0 z1 r1 g1 b1 [LUB] [;<label>]
8071 		 * z0 h0 s0 v0 z1 h1 s1 v1 [LUB] [;<label>]
8072 		 * z0 c0 m0 y0 k0 z1 c1 m1 y1 k1 [LUB] [;<label>]
8073 		 *
8074 		 * z can be in any format (float, dd:mm:ss, dateTclock)
8075 		 */
8076 
8077 		/* First determine if a label is given */
8078 
8079 		if ((k = strcspn(line, ";")) && line[k] != '\0') {
8080 			char string[GMT_LEN64] = {""};
8081 			/* OK, find the label and chop it off */
8082 			strcpy (string, &line[k+1]);
8083 			gmt_chop (string);	/* Strip off trailing return */
8084 			X->data[n].label = strdup (string);
8085 			k--;	/* Position before ; */
8086 			while (k && (line[k] == '\t' || line[k] == ' ')) k--;
8087 			line[k+1] = '\0';	/* Chop label and trailing white space off from line */
8088 			XH->alloc_mode_text[GMT_CPT_INDEX_LBL] = GMT_ALLOC_INTERNALLY;
8089 		}
8090 
8091 		/* Determine if psscale need to label these steps by looking for the optional L|U|B character at the end */
8092 
8093 		c = line[strlen(line)-1];
8094 		if (c == 'L')
8095 			X->data[n].annot = 1;
8096 		else if (c == 'U')
8097 			X->data[n].annot = 2;
8098 		else if (c == 'B')
8099 			X->data[n].annot = 3;
8100 		if (X->data[n].annot) line[strlen(line)-1] = '\0';	/* Chop off this information so it does not affect our column count below */
8101 
8102 		nread = sscanf (line, "%s %s %s %s %s %s %s %s %s %s", T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);	/* Hope to read 4, 8, or 10 fields */
8103 
8104 		if (nread <= 0) continue;	/* Probably a line with spaces - skip */
8105 		if (X->model & GMT_CMYK && nread != 10) error = true;	/* CMYK should results in 10 fields */
8106 		if (!(X->model & GMT_CMYK) && !(nread == 2 || nread == 4 || nread == 8)) error = true;	/* HSV or RGB should result in 8 fields, gray, patterns, or skips in 4 */
8107 		if (nread == 2 && gmt_not_numeric (GMT, T0)) {	/* Truly categorical CPT with named key */
8108 			X->data[n].z_low = n;	/* Just use counter as z value dummy */
8109 			X->data[n].key = (T0[0] == '\\') ? strdup (&T0[1]) : strdup (T0);	/* Skip escape: For user to have a name like B it must be \B to avoid conflict with BNF settings*/
8110 			X->categorical |= GMT_CPT_CATEGORICAL_KEY;	/* Flag this type of CPT */
8111 			XH->alloc_mode_text[GMT_CPT_INDEX_KEY] = GMT_ALLOC_INTERNALLY;
8112 		}
8113 		else {	/* Floating point lookup values */
8114 			gmt_scanf_arg (GMT, T0, GMT_IS_UNKNOWN, false, &X->data[n].z_low);
8115 			if (strchr (T0, 'T')) X->mode |= GMT_CPT_TIME;
8116 		}
8117 		X->data[n].skip = false;
8118 		if (T1[0] == '-') {				/* Skip this slice */
8119 			if (nread != 4) {
8120 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "z-slice to skip not in [z0 - z1 -] format!\n");
8121 				if (close_file) fclose (fp);
8122 				if (Z) gmt_M_free (GMT, Z);
8123 				gmtlib_free_palette (GMT, &X);
8124 				return (NULL);
8125 			}
8126 			gmt_scanf_arg (GMT, T2, GMT_IS_UNKNOWN, false, &X->data[n].z_high);
8127 			X->data[n].skip = true;		/* Don't paint this slice if possible*/
8128 			gmt_M_rgb_copy (X->data[n].rgb_low,  GMT->current.setting.ps_page_rgb);	/* If we must paint, use page color */
8129 			gmt_M_rgb_copy (X->data[n].rgb_high, GMT->current.setting.ps_page_rgb);
8130 		}
8131 		else if (gmtsupport_is_pattern (GMT, T1)) {	/* Gave pattern fill */
8132 			X->data[n].fill = gmt_M_memory (GMT, NULL, 1, struct GMT_FILL);
8133 			if (gmt_getfill (GMT, T1, X->data[n].fill)) {
8134 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "CPT Pattern fill (%s) not understood!\n", T1);
8135 				if (close_file) fclose (fp);
8136 				if (Z) gmt_M_free (GMT, Z);
8137 				gmtlib_free_palette (GMT, &X);
8138 				return (NULL);
8139 			}
8140 			else if (nread == 2) {	/* Categorical cpt records with key fill [;label] */
8141 				X->data[n].z_high = X->data[n].z_low;
8142 				n_cat_records++;
8143 				X->categorical |= GMT_CPT_CATEGORICAL_VAL;
8144 			}
8145 			else if (nread == 4) {
8146 				gmt_scanf_arg (GMT, T2, GMT_IS_UNKNOWN, false, &X->data[n].z_high);
8147 			}
8148 			else {
8149 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "z-slice with pattern fill not in [z0 pattern z1 -] format!\n");
8150 				if (close_file) fclose (fp);
8151 				if (Z) gmt_M_free (GMT, Z);
8152 				gmtlib_free_palette (GMT, &X);
8153 				return (NULL);
8154 			}
8155 			X->has_pattern = true;
8156 			if ((name = gmtsupport_get_userimagename (GMT, T1, cpt_file))) {	/* Must replace fill->pattern with this full path */
8157 				strncpy (X->data[n].fill->pattern, name, PATH_MAX-1);
8158 				gmt_M_str_free (name);
8159 			}
8160 		}
8161 		else {						/* Shades, RGB, HSV, or CMYK */
8162 			if (nread == 2) {	/* Categorical cpt records with key fill [;label] */
8163 				X->data[n].z_high = X->data[n].z_low;
8164 				snprintf (clo, GMT_LEN64, "%s", T1);
8165 				sprintf (chi, "-");
8166 				n_cat_records++;
8167 				X->categorical |= GMT_CPT_CATEGORICAL_VAL;
8168 			}
8169 			else if (nread == 4) {	/* gray shades or color names */
8170 				gmt_scanf_arg (GMT, T2, GMT_IS_UNKNOWN, false, &X->data[n].z_high);
8171 				snprintf (clo, GMT_LEN64, "%s", T1);
8172 				snprintf (chi, GMT_LEN64, "%s", T3);
8173 			}
8174 			else if (X->model & GMT_CMYK) {
8175 				gmt_scanf_arg (GMT, T5, GMT_IS_UNKNOWN, false, &X->data[n].z_high);
8176 				snprintf (clo, GMT_LEN64, "%s/%s/%s/%s", T1, T2, T3, T4);
8177 				snprintf (chi, GMT_LEN64, "%s/%s/%s/%s", T6, T7, T8, T9);
8178 			}
8179 			else if (X->model & GMT_HSV) {
8180 				gmt_scanf_arg (GMT, T4, GMT_IS_UNKNOWN, false, &X->data[n].z_high);
8181 				snprintf (clo, GMT_LEN64, "%s-%s-%s", T1, T2, T3);
8182 				snprintf (chi, GMT_LEN64, "%s-%s-%s", T5, T6, T7);
8183 			}
8184 			else {			/* RGB */
8185 				gmt_scanf_arg (GMT, T4, GMT_IS_UNKNOWN, false, &X->data[n].z_high);
8186 				snprintf (clo, GMT_LEN64, "%s/%s/%s", T1, T2, T3);
8187 				snprintf (chi, GMT_LEN64, "%s/%s/%s", T5, T6, T7);
8188 			}
8189 			if (X->model & GMT_HSV) {
8190 				if (gmtsupport_gethsv (GMT, clo, X->data[n].hsv_low)) error = true;
8191 				if (!strcmp (chi, "-"))	/* Duplicate first color */
8192 					gmt_M_memcpy (X->data[n].hsv_high, X->data[n].hsv_low, 4, double);
8193 				else if (gmtsupport_gethsv (GMT, chi, X->data[n].hsv_high)) error = true;
8194 				gmt_hsv_to_rgb (X->data[n].rgb_low,  X->data[n].hsv_low);
8195 				gmt_hsv_to_rgb (X->data[n].rgb_high, X->data[n].hsv_high);
8196 			}
8197 			else {
8198 				if (gmt_getrgb (GMT, clo, X->data[n].rgb_low)) error = true;
8199 				if (!strcmp (chi, "-"))	/* Duplicate first color */
8200 					gmt_M_memcpy (X->data[n].rgb_high, X->data[n].rgb_low, 4, double);
8201 				else if (gmt_getrgb (GMT, chi, X->data[n].rgb_high)) error = true;
8202 				gmt_rgb_to_hsv (X->data[n].rgb_low,  X->data[n].hsv_low);
8203 				gmt_rgb_to_hsv (X->data[n].rgb_high, X->data[n].hsv_high);
8204 			}
8205 			if (!X->categorical) {
8206 				dz = X->data[n].z_high - X->data[n].z_low;
8207 				if (dz == 0.0) {
8208 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Z-slice with dz = 0\n");
8209 					if (Z) gmt_M_free (GMT, Z);
8210 					gmtlib_free_palette (GMT, &X);
8211 					if (close_file) fclose (fp);
8212 					return (NULL);
8213 				}
8214 				X->data[n].i_dz = 1.0 / dz;
8215 			}
8216 			/* Is color map continuous, gray or b/w? */
8217 			if (X->is_gray && !(gmt_M_is_gray (X->data[n].rgb_low) && gmt_M_is_gray (X->data[n].rgb_high))) X->is_gray = X->is_bw = false;
8218 			if (X->is_bw && !(gmt_M_is_bw(X->data[n].rgb_low) && gmt_M_is_bw(X->data[n].rgb_high))) X->is_bw = false;
8219 
8220 			/* Differences used in gmt_get_rgb_from_z */
8221 			for (i = 0; i < 4; i++) X->data[n].rgb_diff[i] = X->data[n].rgb_high[i] - X->data[n].rgb_low[i];
8222 			for (i = 0; i < 4; i++) X->data[n].hsv_diff[i] = X->data[n].hsv_high[i] - X->data[n].hsv_low[i];
8223 
8224 			if (X->model & GMT_HSV) {
8225 				if (!X->is_continuous && !gmt_M_same_rgb(X->data[n].hsv_low,X->data[n].hsv_high)) X->is_continuous = true;
8226 			}
8227 			else {
8228 				if (!X->is_continuous && !gmt_M_same_rgb(X->data[n].rgb_low,X->data[n].rgb_high)) X->is_continuous = true;
8229 			/* When HSV is converted from RGB: avoid interpolation over hue differences larger than 180 degrees;
8230 			   take the shorter distance instead. This does not apply for HSV color tables, since there we assume
8231 			   that the H values are intentional and one might WANT to interpolate over more than 180 degrees. */
8232 				if (X->data[n].hsv_diff[0] < -180.0) X->data[n].hsv_diff[0] += 360.0;
8233 				if (X->data[n].hsv_diff[0] >  180.0) X->data[n].hsv_diff[0] -= 360.0;
8234 			}
8235 		}
8236 
8237 		n++;
8238 		if (n == n_alloc) {
8239 			size_t old_n_alloc = n_alloc;
8240 			n_alloc <<= 1;
8241 			X->data = gmt_M_memory (GMT, X->data, n_alloc, struct GMT_LUT);
8242 			gmt_M_memset (&(X->data[old_n_alloc]), n_alloc - old_n_alloc, struct GMT_LUT);	/* Initialize new structs to zero */
8243 		}
8244 	}
8245 
8246 	if (close_file) fclose (fp);
8247 
8248 	if (X->mode & GMT_CPT_EXTEND_BNF) {	/* Use low and high colors as back and foreground */
8249 		gmt_M_rgb_copy (X->bfn[GMT_BGD].rgb, X->data[0].rgb_low);
8250 		gmt_M_rgb_copy (X->bfn[GMT_FGD].rgb, X->data[n-1].rgb_high);
8251 		gmt_M_rgb_copy (X->bfn[GMT_BGD].hsv, X->data[0].hsv_low);
8252 		gmt_M_rgb_copy (X->bfn[GMT_FGD].hsv, X->data[n-1].hsv_high);
8253 	}
8254 
8255 	if (X->categorical && n_cat_records != n) {
8256 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot decode %s as categorical CPT\n", cpt_file);
8257 		if (Z) gmt_M_free (GMT, Z);
8258 		gmtlib_free_palette (GMT, &X);
8259 		return (NULL);
8260 	}
8261 	if (error) {
8262 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to decode %s\n", cpt_file);
8263 		if (Z) gmt_M_free (GMT, Z);
8264 		gmtlib_free_palette (GMT, &X);
8265 		return (NULL);
8266 	}
8267 	if (n == 0) {
8268 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "CPT %s has no z-slices!\n", cpt_file);
8269 		if (Z) gmt_M_free (GMT, Z);
8270 		gmtlib_free_palette (GMT, &X);
8271 		return (NULL);
8272 	}
8273 
8274 	if (n < n_alloc) X->data = gmt_M_memory (GMT, X->data, n, struct GMT_LUT);
8275 	X->n_colors = n;
8276 
8277 	if (X->categorical) {	/* Set up fake ranges so CPT is continuous */
8278 		dz = 1.0;	/* This will presumably get reset in the loop */
8279 		for (i = 0; i < X->n_colors; i++) {
8280 			X->data[i].z_high = (i == (X->n_colors-1)) ? X->data[i].z_low + dz : X->data[i+1].z_low;
8281 			dz = X->data[i].z_high - X->data[i].z_low;
8282 			if (dz == 0.0) {
8283 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Z-slice with dz = 0\n");
8284 				return (NULL);
8285 			}
8286 			X->data[i].i_dz = 1.0 / dz;
8287 		}
8288 		/* Add special flag since this CPT is basically a list of colors */
8289 		X->mode |= GMT_CPT_COLORLIST;
8290 	}
8291 	X->wrap_length = X->data[X->n_colors-1].z_high - X->data[0].z_low;
8292 
8293 	for (i = annot = 0, gap = overlap = false; i < X->n_colors - 1; i++) {
8294 		dz = X->data[i].z_high - X->data[i+1].z_low;
8295 		if (dz < -GMT_CONV12_LIMIT) {
8296 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Color palette table %s has a gap of size %g between slices %d and %d!\n", cpt_file, -dz, i, i+1);
8297 			gap = true;
8298 		}
8299 		else if (dz > GMT_CONV12_LIMIT) {
8300 			overlap = true;
8301 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Color palette table %s has an overlap of size %g between slices %d and %d\n", cpt_file, dz, i, i+1);
8302 		}
8303 		else if (fabs (dz) > 0.0) {	/* Split the tiny difference across the two values */
8304 			dz *= 0.5;
8305 			X->data[i].z_high -= dz;
8306 			X->data[i+1].z_low += dz;
8307 		}
8308 		annot += X->data[i].annot;
8309 	}
8310 	annot += X->data[i].annot;
8311 	if (gap || overlap) {
8312 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Must abort due to above errors in %s\n", cpt_file);
8313 		for (i = 0; i < X->n_colors; i++) {
8314 			gmt_M_free (GMT, X->data[i].fill);
8315 			gmt_M_str_free (X->data[i].label);
8316 		}
8317 		gmtlib_free_palette (GMT, &X);
8318 		gmt_M_free (GMT, Z);
8319 		return (NULL);
8320 	}
8321 
8322 	if (!annot) {	/* Must set default annotation flags */
8323 		for (i = 0; i < X->n_colors; i++) X->data[i].annot = 1;
8324 		X->data[i-1].annot = 3;
8325 	}
8326 
8327 	/* Reset the color model to what it was in the GMT defaults when a + is used there. */
8328 	if (color_model & GMT_COLORINT) GMT->current.setting.color_model = color_model;
8329 
8330 	if (X->n_headers < n_hdr_alloc) X->header = gmt_M_memory (GMT, X->header, X->n_headers, char *);
8331 
8332 	if (Z) {
8333 		gmtsupport_cpt_z_scale (GMT, X, Z, GMT_IN);
8334 		gmt_M_free (GMT, Z);
8335 	}
8336 
8337 	return (X);
8338 }
8339 
gmt_cpt_default(struct GMTAPI_CTRL * API,char * cpt,char * file)8340 char * gmt_cpt_default (struct GMTAPI_CTRL *API, char *cpt, char *file) {
8341 	/* Return which type of default CPT this data set should use.
8342 	 * If cpt is specified then that is what we will use. If not, then
8343 	 * we determine if file is a remote data set, and if it is and has a
8344 	 * default CPT then we use that, else we return NULL which means use
8345 	 * the GMT default CPT given by GMT->current.setting.cpt */
8346 	int k_data;
8347 	static char *srtm_cpt = "srtm";
8348 	char *curr_cpt = NULL;
8349 
8350 	if (cpt) return strdup (cpt);	/* CPT was already specified */
8351 	if (file == NULL) return NULL;	/* No file given, so there */
8352 	if (API->GMT->current.setting.run_mode == GMT_MODERN && (curr_cpt = gmt_get_current_item (API->GMT, "cpt", false))) return curr_cpt;	/* Use current CPT */
8353 
8354 	if ((k_data = gmt_remote_dataset_id (API, file)) == GMT_NOTSET) {
8355 		size_t LOX;
8356 		if ((k_data = gmt_get_tile_id (API, file)) == GMT_NOTSET)
8357 			return NULL;	/* Go with the default, whatever that is */
8358 		LOX = strlen (file) - 8;	/* Position of the L|O|X flag */
8359 		if (file[LOX] == 'L') return strdup (srtm_cpt);
8360 	}
8361 	if (API->remote_info[k_data].CPT[0] == '-') return (NULL);
8362 
8363 	return (strdup (API->remote_info[k_data].CPT));
8364 }
8365 
gmt_is_cpt_master(struct GMT_CTRL * GMT,char * cpt)8366 bool gmt_is_cpt_master (struct GMT_CTRL *GMT, char *cpt) {
8367 	/* Return true if cpt is the name of a GMT CPT master table and not a local file */
8368 	char *c = NULL, *f = NULL;
8369 	if (cpt == NULL) return true;	/* No cpt given means use GMT->current.setting.cpt master */
8370 	if (gmt_M_file_is_memory (cpt)) return false;	/* A CPT was given via memory location so cannot be a master reference */
8371 	if ((f = gmt_strrstr (cpt, GMT_CPT_EXTENSION)))	/* Only examine modifiers from there onwards */
8372 		c = gmtlib_last_valid_file_modifier (GMT->parent, f, GMT_CPTFILE_MODIFIERS);
8373 	else	/* Must examine the entire file name for modifiers */
8374 		c = gmtlib_last_valid_file_modifier (GMT->parent, cpt, GMT_CPTFILE_MODIFIERS);
8375 	if (c && (f = gmt_first_modifier (GMT, c, GMT_CPTFILE_MODIFIERS)))
8376 		f[0] = '\0';	/* Must chop off modifiers for further checks to work */
8377 	if (gmtsupport_cpt_master_index (GMT, cpt)) {
8378 		if (c && f) f[0] = '+';	/* Restore modifier before we return */
8379 		return true;
8380 	}
8381 	if (cpt[0] && !gmt_access (GMT, cpt, R_OK)) return false;	/* A CPT was given and exists */
8382 	return false;	/* Well, what can we do at this point */
8383 }
8384 
gmtlib_set_current_item_file(struct GMT_CTRL * GMT,const char * item,char * file)8385 int gmtlib_set_current_item_file (struct GMT_CTRL *GMT, const char *item, char *file) {
8386 	char panel[GMT_LEN16] = {""};
8387 	int fig, subplot, inset;
8388 
8389 	if (GMT->current.setting.run_mode == GMT_CLASSIC) {	/* Must be more careful */
8390 		if (!strncmp (item, "cpt", 3U)) return (GMT_FILE_NOT_FOUND);	/* Current CPT not available in classic mode */
8391 		snprintf (file, PATH_MAX, "%s/%s-gmt.%s", GMT->parent->tmp_dir, GMT->parent->session_name, item);	/* Global file in the tmp dir only */
8392 		return (GMT_NOERROR);
8393 	}
8394 
8395 	gmtlib_get_graphics_item (GMT->parent, &fig, &subplot, panel, &inset);	/* Determine the graphics item */
8396 
8397 	/* Set the correct output file name given the graphics item */
8398 	if (inset)	/* Only one inset may be active at any given time */
8399 		snprintf (file, PATH_MAX, "%s/gmt.inset.%s", GMT->parent->gwf_dir, item);
8400 	else if (subplot & GMT_SUBPLOT_ACTIVE) {	/* Either subplot master or a panel-specific item */
8401 		if (subplot & GMT_PANEL_NOTSET)	/* Master for all subplot panels */
8402 			snprintf (file, PATH_MAX, "%s/gmt.%d.subplot.%s", GMT->parent->gwf_dir, fig, item);
8403 		else	/* item for just this subplot */
8404 			snprintf (file, PATH_MAX, "%s/gmt.%d.panel.%s.%s", GMT->parent->gwf_dir, fig, panel, item);
8405 	}
8406 	else if (fig)	/* Limited to one figure only */
8407 		snprintf (file, PATH_MAX, "%s/gmt.%d.%s", GMT->parent->gwf_dir, fig, item);
8408 	else
8409 		snprintf (file, PATH_MAX, "%s/gmt.%s", GMT->parent->gwf_dir, item);
8410 	return (GMT_NOERROR);
8411 }
8412 
gmt_save_current_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,unsigned int cpt_flags)8413 void gmt_save_current_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, unsigned int cpt_flags) {
8414 	char file[PATH_MAX] = {""};
8415 
8416 	if (gmtlib_set_current_item_file (GMT, "cpt", file) == GMT_FILE_NOT_FOUND) return;
8417 
8418 	if (GMT_Write_Data (GMT->parent, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, cpt_flags, NULL, file, P) != GMT_NOERROR)
8419 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to save current CPT file to %s !\n", file);
8420 	else
8421 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Save current CPT file to %s !\n", file);
8422 }
8423 
gmt_get_current_item(struct GMT_CTRL * GMT,const char * item,bool strict)8424 char *gmt_get_current_item (struct GMT_CTRL *GMT, const char *item, bool strict) {
8425 	/* If modern and a current item file exists, allocate a string with its name and return, else NULL.
8426 	 * Currently, item can be cpt.
8427 	 * if strict is true the file must be available for the current item, otherwise we may look up the hierarchical chain */
8428 	char path[PATH_MAX] = {""}, panel[GMT_LEN16] = {""}, *file = NULL;
8429 	int fig, subplot, inset;
8430 
8431 	if (GMT->current.setting.run_mode == GMT_CLASSIC) {	/* A few more checks */
8432 		if (!strncmp (item, "cpt", 3U)) return NULL;	/* Current CPT concept not available in classic mode */
8433 		/* For gridlines we must use a global file in the tmp dir */
8434 		snprintf (path, PATH_MAX, "%s/%s-gmt.%s", GMT->parent->tmp_dir, GMT->parent->session_name, item);
8435 		if (!access (path, R_OK)) file = strdup (path);	/* Yes, found it */
8436 		if (strict && file == NULL) goto FOUND_NOTHING;
8437 	}
8438 
8439 	/* Modern mode */
8440 
8441 	gmtlib_get_graphics_item (GMT->parent, &fig, &subplot, panel, &inset);	/* Determine the cpt level */
8442 	/* Find the appropriate graphics item for where we are but may have to go up the hierarchy */
8443 
8444 	if (inset) {	/* See if an inset item exists */
8445 		snprintf (path, PATH_MAX, "%s/gmt.inset.%s", GMT->parent->gwf_dir, item);
8446 		if (!file && !access (path, R_OK)) file = strdup (path);	/* Yes, found it */
8447 		if (strict && file == NULL) goto FOUND_NOTHING;
8448 	}
8449 	if (!file && (subplot & GMT_SUBPLOT_ACTIVE)) {	/* Nothing yet, see if subplot has one */
8450 		if ((subplot & GMT_PANEL_NOTSET) == 0) {	/* Panel-specific item available? */
8451 			snprintf (path, PATH_MAX, "%s/gmt.%d.panel.%s.%s", GMT->parent->gwf_dir, fig, panel, item);
8452 			if (!access (path, R_OK)) file = strdup (path);	/* Yes, found it */
8453 			if (strict && file == NULL) goto FOUND_NOTHING;
8454 		}
8455 		if (!file) {	/* No, try subplot master item instead? */
8456 			snprintf (path, PATH_MAX, "%s/gmt.%d.subplot.%s", GMT->parent->gwf_dir, fig, item);
8457 			if (!access (path, R_OK)) file = strdup (path);	/* Yes, found it */
8458 			if (strict && file == NULL) goto FOUND_NOTHING;
8459 		}
8460 	}
8461 	if (!file && fig) {	/* Not found an item file yet, so try one for this specific figure */
8462 		snprintf (path, PATH_MAX, "%s/gmt.%d.%s", GMT->parent->gwf_dir, fig, item);
8463 		if (!access (path, R_OK)) file = strdup (path);	/* Yes, found it */
8464 		if (strict && file == NULL) goto FOUND_NOTHING;
8465 	}
8466 	if (!file) {	/* Not found an item file yet, finally try the session master */
8467 		snprintf (path, PATH_MAX, "%s/gmt.%s", GMT->parent->gwf_dir, item);
8468 		if (!access (path, R_OK)) file = strdup (path);	/* Yes, found it */
8469 		if (strict && file == NULL) goto FOUND_NOTHING;
8470 	}
8471 	if (file) {
8472 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Selected current %s file %s\n", file, item);
8473 		return (file);
8474 	}
8475 
8476 FOUND_NOTHING:
8477 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "No current %s file found\n", item);
8478 	return (NULL);
8479 }
8480 
8481 /*! . */
gmt_get_palette(struct GMT_CTRL * GMT,char * file,enum GMT_enum_cpt mode,double zmin,double zmax,double dz)8482 struct GMT_PALETTE *gmt_get_palette (struct GMT_CTRL *GMT, char *file, enum GMT_enum_cpt mode, double zmin, double zmax, double dz) {
8483 	/* Will read in a CPT.  However, if file does not exist in the current directory we may provide
8484 	   a CPT for quick/dirty work provided mode == GMT_CPT_OPTIONAL and hence zmin/zmax are set to the desired data range */
8485 
8486 	struct GMT_PALETTE *P = NULL;
8487 	unsigned int continuous = (file && strchr(file,',')), first = 0;
8488 	bool is_cpt_master = false;
8489 
8490 	if (mode == GMT_CPT_REQUIRED) {	/* The calling function requires the CPT to be present; GMT_Read_Data will work or fail accordingly */
8491 		P = GMT_Read_Data (GMT->parent, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL|continuous, NULL, file, NULL);
8492 		return (P);
8493 	}
8494 
8495 	/* Here, the cpt is optional (mode == GMT_CPT_OPTIONAL).  There are four possibilities:
8496 	   1. A cpt in current directory is given - simply read it and return.
8497 	   2. file is NULL and runmode is classic and hence we default to master CPT name GMT->init.cpt[GMT_DEFAULT_CPT] (or a type-specific CPT).
8498 	   3. file is NULL and runmode is modern and a current cpt exists - use it.
8499 	   4. A specific master CPT name is given. If this does not exist then things will fail in GMT_makecpt.
8500 
8501 	   For 2 & 3 we take the master table and linearly stretch the z values to fit the range, or honor default range for dynamic CPTs.
8502 	*/
8503 
8504 	if (gmt_file_is_cache (GMT->parent, file))	/* Must be a cache file */
8505 		first = gmt_download_file_if_not_found (GMT, file, 0);
8506 	if (first == 0)	/* Check if given a master or a real file */
8507 		is_cpt_master = gmt_is_cpt_master (GMT, file);
8508 	if (is_cpt_master) {	/* Take master cpt and stretch to fit data range using continuous colors */
8509 		char *master = NULL, *current_cpt = NULL;
8510 		double noise;
8511 		struct GMT_PALETTE_HIDDEN *PH = NULL;
8512 
8513 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "CPT argument %s understood to be a master table\n", file);
8514 		if (gmt_M_is_dnan (zmin) || gmt_M_is_dnan (zmax)) {	/* Safety valve 1 */
8515 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Passing zmax or zmin == NaN prevents automatic CPT generation!\n");
8516 			return (NULL);
8517 		}
8518 		if (zmax <= zmin) {	/* Safety valve 2 */
8519 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Passing zmax <= zmin prevents automatic CPT generation!\n");
8520 			return (NULL);
8521 		}
8522 
8523 		if (file == NULL && (current_cpt = gmt_get_current_item (GMT, "cpt", false))) {	/* There is a current CPT in modern mode */
8524 			P = GMT_Read_Data (GMT->parent, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, current_cpt, NULL);
8525 			gmt_M_str_free (current_cpt);
8526 			return (P);
8527 		}
8528 
8529 		master = (file && file[0]) ? file : GMT->current.setting.cpt;	/* Set master CPT prefix */
8530 		P = GMT_Read_Data (GMT->parent, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL|GMT_CPT_CONTINUOUS, NULL, master, NULL);
8531 		if (!P) return (P);		/* Error reading file. Return right away to avoid a segv in next line */
8532 		/* Stretch to fit the data range */
8533 		/* Prevent slight round-off from causing the min/max float data values to fall outside the cpt range */
8534 		if (gmt_M_is_zero (dz)) {
8535 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Auto-stretching CPT file %s to fit data range %g to %g\n", master, zmin, zmax);
8536 			noise = (zmax - zmin) * GMT_CONV8_LIMIT;
8537 			zmin -= noise;	zmax += noise;
8538 		}
8539 		else {	/* Round to multiple of dz */
8540 			zmin = (floor (zmin / dz) * dz);
8541 			zmax = (ceil (zmax / dz) * dz);
8542 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Auto-stretching CPT file %s to fit rounded data range %g to %g\n", master, zmin, zmax);
8543 		}
8544 		PH = gmt_get_C_hidden (P);
8545 		PH->auto_scale = 1;	/* Flag for colorbar to supply -Baf if not given */
8546 		gmt_stretch_cpt (GMT, P, zmin, zmax);
8547 		gmt_save_current_cpt (GMT, P, 0);	/* Save for use by session, if modern */
8548 	}
8549 	else if (file) {	/* Gave a CPT file */
8550 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "CPT argument %s understood to be a regular CPT table\n", file);
8551 		P = GMT_Read_Data (GMT->parent, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, &file[first], NULL);
8552 	}
8553 	else
8554 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No CPT file or master given?\n");
8555 
8556 	return (P);
8557 }
8558 
8559 /*! . */
gmt_cpt_transparency(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double transparency,unsigned int mode)8560 void gmt_cpt_transparency (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double transparency, unsigned int mode) {
8561 	/* Set transparency for all slices, and possibly BNF */
8562 
8563 	unsigned int i;
8564 	gmt_M_unused(GMT);
8565 
8566 	for (i = 0; i < P->n_colors; i++) P->data[i].hsv_low[3] = P->data[i].hsv_high[3] = P->data[i].rgb_low[3] = P->data[i].rgb_high[3] = transparency;
8567 
8568 	if (mode == 0) return;	/* Do not want to change transparency of BFN*/
8569 
8570 	/* Background, foreground, and nan colors */
8571 
8572 	for (i = 0; i < 3; i++) P->bfn[i].hsv[3] = P->bfn[i].rgb[3] = transparency;
8573 }
8574 
8575 /*! . */
gmt_parse_inv_cpt(struct GMT_CTRL * GMT,char * arg)8576 unsigned int gmt_parse_inv_cpt (struct GMT_CTRL *GMT, char *arg) {
8577 	unsigned int mode = 0;
8578 	/* Sets the mode for the -I option in makecpt and grd2cpt */
8579 	if (!arg || arg[0] == 0)	/* Default is -Ic */
8580 		mode |= GMT_CPT_C_REVERSE;
8581 	else {	/* Got argument(s).  Only combinations c, z, cz, or zc allowed */
8582 		size_t k;
8583 		for (k = 0; k < strlen (arg); k++) {
8584 			switch (arg[k]) {
8585 				case 'c':	mode |= GMT_CPT_C_REVERSE;	break;	/* Invert c-range */
8586 				case 'z':	mode |= GMT_CPT_Z_REVERSE;	break;	/* Invert z-range */
8587 				default:
8588 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_parse_inv_cpt: Unrecognized mode %c passed!\n", arg[k]);
8589 					return UINT_MAX;
8590 					break;
8591 			}
8592 		}
8593 	}
8594 	return (mode);
8595 }
8596 
gmtsupport_validate_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double * z_low,double * z_high)8597 GMT_LOCAL int gmtsupport_validate_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double *z_low, double *z_high) {
8598 	int ks;
8599 	if (!P->has_hinge) return GMT_NOTSET;	/* Not our concern here */
8600 	/* Claims to have a hinge */
8601 	ks = gmtsupport_find_cpt_hinge (GMT, P);	/* Get hinge slice (or -1 if no hinge found) */
8602 	if (ks == GMT_NOTSET) {	/* Must be a rogue CPT - ignore the hinge setting */
8603 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmt_stretch_cpt: CPT says it has a hinge but none is actually found? - ignored.\n");
8604 		P->has_hinge = 0;
8605 		return GMT_NOTSET;
8606 	}
8607 	if (*z_low < P->hinge && *z_high > P->hinge) return ks;	/* Output range includes hinge, all is well */
8608 
8609 	/* Here we have a CPT with a hinge that is not included in the desired range.  Per policy:
8610 	 * We throw away the unreferenced CPT half and return the other relevant half.
8611 	 */
8612 	if (*z_low >= P->hinge) {	/* Must exclude the below-hinge CPT colors entirely */
8613 		gmt_M_memcpy (P->data, &P->data[ks], P->n_colors-ks, struct GMT_LUT);
8614 		P->n_colors -= ks;
8615 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtsupport_validate_cpt: CPT hard hinge is outside actual data range - range adjusted to start at hinge %g and below-hinge CPT ignored.\n", *z_low);
8616 	}
8617 	else if (*z_high <= P->hinge) {	/* Must exclude the above-hinge CPT colors entirely */
8618 		P->n_colors = ks;
8619 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtsupport_validate_cpt: CPT hard hinge is outside actual data range - range adjusted to end at hinge %g and above-hinge CPT ignored.\n", *z_high);
8620 	}
8621 	/* Behave as a single CPT range with no hinge from now on */
8622 	P->has_hinge = 0;
8623 	return GMT_NOTSET;
8624 }
8625 
8626 /*! . */
gmt_stretch_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double z_low,double z_high)8627 void gmt_stretch_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double z_low, double z_high) {
8628 	/* Replace CPT z-values with new ones linearly scaled from z_low to z_high.  If these are
8629 	 * zero then we substitute the CPT's default range instead (if available).
8630 	 * Note: If P has a hinge then its value is expected to be in the final user data values.
8631 	 * Default hinge is 0 but *h modifier can set this for CPTs with soft hinges.
8632 	 * Note: Input P has normalized z' values at this time (0-1) so z' hinge is at 0.5.
8633 	 * If a hinge is outside the requested data range then we make adjustments:
8634 	 *   hard hinge: include hinge in the final output range and ignore the other half of the CPT
8635 	 *   soft hinge: Ignore the hinge setting.
8636 	 */
8637 	int is, ks;
8638 	double z_min, z_start, scale;
8639 	if (z_low == z_high) {	/* Range information not given, rely on CPT RANGE setting */
8640 		if (P->has_range == 0) {
8641 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_stretch_cpt: Passed z_low == z_high but CPT has no explicit range.  No changes made\n");
8642 			return;
8643 		}
8644 		z_low = P->minmax[0];	z_high = P->minmax[1];
8645 	}
8646 	ks = gmtsupport_validate_cpt (GMT, P, &z_low, &z_high);	/* Deal with any issure related to hinges */
8647 
8648 	z_min = P->data[0].z_low;
8649 	z_start = z_low;
8650 	if (P->has_hinge)	/* Separate scale on either side of hinge, start with scale for section below the hinge */
8651 		scale = (P->hinge - z_low) / (0.0 - P->data[0].z_low);
8652 	else	/* No hinge, single scale using the entire CPT */
8653 		scale = (z_high - z_low) / (P->data[P->n_colors-1].z_high - P->data[0].z_low);
8654 
8655 	for (is = 0; is < (int)P->n_colors; is++) {
8656 		if (is == ks) {	/* Must change scale and z_min for cpt above the hinge */
8657 			z_min = 0.0; z_start = P->hinge;
8658 			scale = (z_high - P->hinge) / (P->data[P->n_colors-1].z_high - 0.0);
8659 		}
8660 		P->data[is].z_low  = z_start + (P->data[is].z_low  - z_min) * scale;
8661 		P->data[is].z_high = z_start + (P->data[is].z_high - z_min) * scale;
8662 		P->data[is].i_dz /= scale;
8663 	}
8664 }
8665 
8666 /*! . */
gmtsupport_lutsort(const void * p1,const void * p2)8667 GMT_LOCAL int gmtsupport_lutsort (const void *p1, const void *p2) {
8668 	/* Sorts color LUT into ASCENDING order!  */
8669 	if (((struct GMT_LUT *)p1)->z_low < ((struct GMT_LUT *)p2)->z_low) return (-1);
8670 	if (((struct GMT_LUT *)p1)->z_low > ((struct GMT_LUT *)p2)->z_low) return(+1);
8671 	return (0);	/* Should never happen */
8672 }
8673 
gmtsupport_LUT_swap(struct GMT_LUT * slice)8674 GMT_LOCAL void gmtsupport_LUT_swap (struct GMT_LUT *slice) {
8675 	/* Exchanges the low and high color values for one slice */
8676 	unsigned int k;
8677 	for (k = 0; k < 4; k++) {
8678 		gmt_M_double_swap (slice->rgb_low[k], slice->rgb_high[k]);
8679 		gmt_M_double_swap (slice->hsv_low[k], slice->hsv_high[k]);
8680 		slice->rgb_diff[k] = -slice->rgb_diff[k];
8681 		slice->hsv_diff[k] = -slice->hsv_diff[k];
8682 	}
8683 }
8684 
8685 /*! . */
gmt_scale_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double scale)8686 void gmt_scale_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double scale) {
8687 	/* Scale all z-related variables in the CPT by scale */
8688 	unsigned int i;
8689 	bool flip = (scale < 0.0);	/* More work to do if a negative scale is given */
8690 	gmt_M_unused(GMT);
8691 	for (i = 0; i < P->n_colors; i++) {
8692 		P->data[i].z_low  *= scale;
8693 		P->data[i].z_high *= scale;
8694 		P->data[i].i_dz   /= fabs (scale);
8695 		if (flip) {
8696 			gmt_M_double_swap (P->data[i].z_low, P->data[i].z_high);
8697 			gmtsupport_LUT_swap (&(P->data[i]));
8698 		}
8699 	}
8700 	if (P->has_hinge) P->hinge *= scale;
8701 	if (P->has_range) {	/* Update the range min/max */
8702 		P->minmax[0] *= scale;
8703 		P->minmax[1] *= scale;
8704 		if (flip) gmt_M_double_swap (P->minmax[0], P->minmax[1]);
8705 	}
8706 	if (flip)	/* Must also reverse the order of slices */
8707 		qsort (P->data, P->n_colors, sizeof (struct GMT_LUT), gmtsupport_lutsort);
8708 }
8709 
8710 #define gmt_M_fill_swap(x, y) {struct GMT_FILL *F_tmp; F_tmp = x, x = y, y = F_tmp;}
8711 /*! . */
gmt_invert_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P)8712 void gmt_invert_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P) {
8713 	unsigned int i, j, k;
8714 	gmt_M_unused(GMT);
8715 	/* Reverse the order of the colors, leaving the z arrangement intact */
8716 	for (i = 0, j = P->n_colors-1; i < P->n_colors; i++, j--) {
8717 		for (k = 0; k < 4; k++) {
8718 			gmt_M_double_swap (P->data[i].rgb_low[k], P->data[j].rgb_high[k]);
8719 			gmt_M_double_swap (P->data[i].hsv_low[k], P->data[j].hsv_high[k]);
8720 		}
8721 		if (i < j) gmt_M_fill_swap (P->data[i].fill, P->data[j].fill);
8722 
8723 	}
8724 	for (i = 0; i < P->n_colors; i++) {	/* Update the difference arrays */
8725 		for (k = 0; k < 4; k++) {
8726 			P->data[i].rgb_diff[k] = P->data[i].rgb_high[k] - P->data[i].rgb_low[k];
8727 			P->data[i].hsv_diff[k] = P->data[i].hsv_high[k] - P->data[i].hsv_low[k];
8728 		}
8729 	}
8730 	/* Swap the B&F settings */
8731 	for (k = 0; k < 4; k++) {
8732 		gmt_M_double_swap (P->bfn[GMT_BGD].rgb[k], P->bfn[GMT_FGD].rgb[k]);
8733 		gmt_M_double_swap (P->bfn[GMT_BGD].hsv[k], P->bfn[GMT_FGD].hsv[k]);
8734 	}
8735 	gmt_M_fill_swap (P->bfn[GMT_BGD].fill, P->bfn[GMT_FGD].fill);
8736 }
8737 
gmt_undo_log10(struct GMT_CTRL * GMT,struct GMT_PALETTE * P)8738 void gmt_undo_log10 (struct GMT_CTRL *GMT, struct GMT_PALETTE *P) {
8739 	unsigned int i;
8740 	gmt_M_unused(GMT);
8741 	/* Turn z into 10^z */
8742 	for (i = 0; i < P->n_colors; i++) {
8743 		P->data[i].z_low  = pow (10.0, P->data[i].z_low);
8744 		P->data[i].z_high = pow (10.0, P->data[i].z_high);
8745 	}
8746 }
8747 
8748 /*! . */
gmt_sample_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * Pin,double z[],int nz_in,bool continuous,bool reverse,bool log_mode,bool no_inter)8749 struct GMT_PALETTE *gmt_sample_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *Pin, double z[], int nz_in, bool continuous, bool reverse, bool log_mode, bool no_inter) {
8750 	/* Resamples the current CPT based on new z-array.
8751 	 * Old cpt is first normalized to z' = 0-1 range and scaled to fit new user z range.
8752 	 * New cpt may be continuous and/or reversed.
8753 	 * We write the new CPT to stdout. */
8754 
8755 	unsigned int i = 0, j, k, upper, lower, nz;
8756 	uint64_t dim_nz[GMT_DIM_SIZE] = {0, 0, 0, 0};
8757 	bool even = false;	/* even is true when nz is passed as negative */
8758 	bool set_z_only = false;
8759 	double rgb_low[4], rgb_high[4], rgb_fore[4], rgb_back[4];
8760 	double *x = NULL, *z_out = NULL, a, b, f, x_inc, x_hinge = 0.5;
8761 	double hsv_low[4], hsv_high[4], hsv_fore[4], hsv_back[4];
8762 
8763 	struct GMT_LUT *lut = NULL;
8764 	struct GMT_PALETTE *P = NULL;
8765 
8766 	if (nz_in < 0) {	/* Called from grd2cpt which wants equal area colors */
8767 		nz = abs (nz_in);
8768 		even = true;
8769 	}
8770 	else
8771 		nz = nz_in;
8772 
8773 	if (log_mode) {	/* Our z values are actually log10(z), need array with z for output */
8774 		z_out = gmt_M_memory (GMT, NULL, nz, double);
8775 		for (i = 0; i < nz; i++) z_out[i] = pow (10.0, z[i]);
8776 	}
8777 	else	/* Just point to the incoming z values */
8778 		z_out = z;
8779 
8780 	(void) gmtsupport_validate_cpt (GMT, Pin, &z_out[0], &z_out[nz-1]);	/* Deal with any issure related to hinges */
8781 
8782 	i += gmt_M_check_condition (GMT, !Pin->is_continuous && continuous, "Making a continuous cpt from a discrete cpt may give unexpected results!\n");
8783 
8784 
8785 	dim_nz[0] = nz - 1;
8786 	if ((P = GMT_Create_Data (GMT->parent, GMT_IS_PALETTE, GMT_IS_NONE, 0, dim_nz, NULL, NULL, 0, 0, NULL)) == NULL) {
8787 		gmt_M_free (GMT, z_out);
8788 		return NULL;
8789 	}
8790 
8791 	lut = gmt_M_memory (GMT, NULL, Pin->n_colors, struct GMT_LUT);
8792 
8793 	i += gmt_M_check_condition (GMT, (no_inter || set_z_only) && P->n_colors > Pin->n_colors, "Number of picked colors exceeds colors in input cpt!\n");
8794 
8795 	/* First normalize old CPT so z-range is normalized z' = 0-1 */
8796 
8797 	b = 1.0 / (Pin->data[Pin->n_colors-1].z_high - Pin->data[0].z_low);
8798 	a = -Pin->data[0].z_low * b;
8799 
8800 	for (i = 0; i < Pin->n_colors; i++) {	/* Copy/normalize CPT and reverse if needed */
8801 		if (reverse) {
8802 			j = Pin->n_colors - i - 1;
8803 			lut[i].z_low = 1.0 - a - b * Pin->data[j].z_high;
8804 			lut[i].z_high = 1.0 - a - b * Pin->data[j].z_low;
8805 			gmt_M_rgb_copy (lut[i].rgb_high, Pin->data[j].rgb_low);
8806 			gmt_M_rgb_copy (lut[i].rgb_low,  Pin->data[j].rgb_high);
8807 			gmt_M_rgb_copy (lut[i].hsv_high, Pin->data[j].hsv_low);
8808 			gmt_M_rgb_copy (lut[i].hsv_low,  Pin->data[j].hsv_high);
8809 		}
8810 		else {
8811 			j = i;
8812 			lut[i].z_low = a + b * Pin->data[j].z_low;
8813 			lut[i].z_high = a + b * Pin->data[j].z_high;
8814 			gmt_M_rgb_copy (lut[i].rgb_high, Pin->data[j].rgb_high);
8815 			gmt_M_rgb_copy (lut[i].rgb_low,  Pin->data[j].rgb_low);
8816 			gmt_M_rgb_copy (lut[i].hsv_high, Pin->data[j].hsv_high);
8817 			gmt_M_rgb_copy (lut[i].hsv_low,  Pin->data[j].hsv_low);
8818 		}
8819 	}
8820 	lut[0].z_low = 0.0;			/* Prevent roundoff errors */
8821 	lut[Pin->n_colors-1].z_high = 1.0;
8822 
8823 	/* Then set up normalized output locations x */
8824 
8825 	x = gmt_M_memory (GMT, NULL, nz, double);
8826 
8827 	if (nz < 2)	/* Want a single color point, assume range 0-1 */
8828 		nz = 2;
8829 	else if (even) {
8830 		x_inc = 1.0 / (nz - 1);
8831 		for (i = 0; i < nz; i++) x[i] = i * x_inc;	/* Normalized z values 0-1 */
8832 	}
8833 	else {	/* As with LUT, translate users z-range to 0-1 range */
8834 		double scale_low, scale_high, z_hinge = 0.0, hinge = 0.0;
8835 		if (Pin->has_hinge) {	/* Need separate scales on either side of hinge at 0.0 */
8836 			hinge = Pin->hinge;
8837 			scale_low  = x_hinge / (hinge - z[0]);	/* Convert z_out below the hinge to x = 0 - x_hinge */
8838 			scale_high = (1.0 - x_hinge) * (1.0 / (z[nz-1] - hinge));	/* Convert z)out above hinge to x_hinge - 1 */
8839 		}
8840 		else {	/* No hinge, or output range excludes hinge, same scale for all of CPT */
8841 			scale_high = 1.0 / (z[nz-1] - z[0]);
8842 			z_hinge = -DBL_MAX;	/* So the if-test in the loop below always fail */
8843 			x_hinge = 0.0;		/* Starting x is zero */
8844 			hinge = z[0];	/* There is no hinge so we need z-min */
8845 		}
8846 
8847 		for (i = 0; i < nz; i++) {
8848 			if (z[i] <= z_hinge)	/* Below or equal to hinge */
8849 				x[i] = (z[i] - z[0]) * scale_low;
8850 			else /* Above hinge or there is no hinge */
8851 				x[i] = x_hinge + (z[i] - hinge) * scale_high;
8852 		}
8853 	}
8854 	x[0] = 0.0;	/* Prevent bad roundoff */
8855 	x[nz-1] = 1.0;
8856 
8857 	/* Determine color at lower and upper limit of each interval */
8858 
8859 	for (i = 0; i < P->n_colors; i++) {
8860 
8861 		lower = i;
8862 		upper = i + 1;
8863 
8864 		if (set_z_only) { /* Just duplicate the RGB colors */
8865 			gmt_M_memcpy (rgb_low, Pin->data[i].rgb_low, 4, double);
8866 			gmt_M_memcpy (rgb_high, Pin->data[i].rgb_high, 4, double);
8867 			gmt_rgb_to_hsv (rgb_low, hsv_low);
8868 			gmt_rgb_to_hsv (rgb_high, hsv_high);
8869 		}
8870 		else if (no_inter) { /* Just pick the first n_colors */
8871 			j = MIN (i, Pin->n_colors);
8872 			if (Pin->model & GMT_HSV) {	/* Pick in HSV space */
8873 				for (k = 0; k < 4; k++) hsv_low[k] = lut[j].hsv_low[k], hsv_high[k] = lut[j].hsv_high[k];
8874 				gmt_hsv_to_rgb (rgb_low, hsv_low);
8875 				gmt_hsv_to_rgb (rgb_high, hsv_high);
8876 			}
8877 			else {	/* Pick in RGB space */
8878 				for (k = 0; k < 4; k++) rgb_low[k] = lut[j].rgb_low[k], rgb_high[k] = lut[j].rgb_low[k];
8879 				gmt_rgb_to_hsv (rgb_low, hsv_low);
8880 				gmt_rgb_to_hsv (rgb_high, hsv_high);
8881 			}
8882 		}
8883 		else if (continuous) { /* Interpolate color at lower and upper value */
8884 
8885 			for (j = 0; j < Pin->n_colors && x[lower] >= lut[j].z_high; j++);
8886 			if (j == Pin->n_colors) j--;
8887 
8888 			f = 1.0 / (lut[j].z_high - lut[j].z_low);
8889 
8890 			if (Pin->model & GMT_HSV) {	/* Interpolation in HSV space */
8891 				for (k = 0; k < 4; k++) hsv_low[k] = lut[j].hsv_low[k] + (lut[j].hsv_high[k] - lut[j].hsv_low[k]) * f * (x[lower] - lut[j].z_low);
8892 				gmt_hsv_to_rgb (rgb_low, hsv_low);
8893 			}
8894 			else {	/* Interpolation in RGB space */
8895 				for (k = 0; k < 4; k++) rgb_low[k] = lut[j].rgb_low[k] + (lut[j].rgb_high[k] - lut[j].rgb_low[k]) * f * (x[lower] - lut[j].z_low);
8896 				gmt_rgb_to_hsv (rgb_low, hsv_low);
8897 			}
8898 
8899 			while (j < Pin->n_colors && x[upper] > lut[j].z_high) j++;
8900 
8901 			f = 1.0 / (lut[j].z_high - lut[j].z_low);
8902 
8903 			if (Pin->model & GMT_HSV) {	/* Interpolation in HSV space */
8904 				for (k = 0; k < 4; k++) hsv_high[k] = lut[j].hsv_low[k] + (lut[j].hsv_high[k] - lut[j].hsv_low[k]) * f * (x[upper] - lut[j].z_low);
8905 				gmt_hsv_to_rgb (rgb_high, hsv_high);
8906 			}
8907 			else {	/* Interpolation in RGB space */
8908 				for (k = 0; k < 4; k++) rgb_high[k] = lut[j].rgb_low[k] + (lut[j].rgb_high[k] - lut[j].rgb_low[k]) * f * (x[upper] - lut[j].z_low);
8909 				gmt_rgb_to_hsv (rgb_high, hsv_high);
8910 			}
8911 #if 0
8912 			/* Avoid aliasing for continuous CPTs */
8913 			if (i > 0 && !gmt_M_same_rgb (P->data[i-1].rgb_high, rgb_low)) {
8914 				/* Discontinuous resampling, must average the colors */
8915 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "CPT resampling caused aliasing - corrected by averaging at boundaries\n");
8916 				if (Pin->model & GMT_HSV) {	/* Interpolation in HSV space */
8917 					for (k = 0; k < 4; k++) P->data[i-1].hsv_high[k] = hsv_low[k] = 0.5 * (P->data[i-1].hsv_high[k] + hsv_low[k]);
8918 					gmt_hsv_to_rgb (P->data[i-1].rgb_high, P->data[i-1].hsv_high);
8919 				}
8920 				else {
8921 					for (k = 0; k < 4; k++) P->data[i-1].rgb_high[k] = rgb_low[k] = 0.5 * (P->data[i-1].rgb_high[k] + rgb_low[k]);
8922 					gmt_rgb_to_hsv (P->data[i-1].rgb_high, P->data[i-1].hsv_high);
8923 				}
8924 			}
8925 #endif
8926 		}
8927 		else {	 /* Interpolate central value and assign color to both lower and upper limit */
8928 
8929 			if (Pin->has_hinge && x[lower] <= x_hinge && x[upper] > x_hinge)	/* Detected hinge, so select the hinge normalized x-value */
8930 				a = x_hinge;
8931 			else	/* Get halfway between the limits */
8932 				a = (x[lower] + x[upper]) / 2;
8933 			for (j = 0; j < Pin->n_colors && a >= lut[j].z_high; j++);
8934 			if (j == Pin->n_colors) j--;
8935 
8936 			f = 1.0 / (lut[j].z_high - lut[j].z_low);
8937 
8938 			if (Pin->model & GMT_HSV) {	/* Interpolation in HSV space */
8939 				for (k = 0; k < 4; k++) hsv_low[k] = hsv_high[k] = lut[j].hsv_low[k] + (lut[j].hsv_high[k] - lut[j].hsv_low[k]) * f * (a - lut[j].z_low);
8940 				gmt_hsv_to_rgb (rgb_low, hsv_low);
8941 				gmt_hsv_to_rgb (rgb_high, hsv_high);
8942 			}
8943 			else {	/* Interpolation in RGB space */
8944 				for (k = 0; k < 4; k++) rgb_low[k] = rgb_high[k] = lut[j].rgb_low[k] + (lut[j].rgb_high[k] - lut[j].rgb_low[k]) * f * (a - lut[j].z_low);
8945 				gmt_rgb_to_hsv (rgb_low, hsv_low);
8946 				gmt_rgb_to_hsv (rgb_high, hsv_high);
8947 			}
8948 		}
8949 
8950 		if (lower == 0) {
8951 			gmt_M_rgb_copy (rgb_back, rgb_low);
8952 			gmt_M_rgb_copy (hsv_back, hsv_low);
8953 		}
8954 		if (upper == P->n_colors) {
8955 			gmt_M_rgb_copy (rgb_fore, rgb_high);
8956 			gmt_M_rgb_copy (hsv_fore, hsv_high);
8957 		}
8958 
8959 		gmt_M_rgb_copy (P->data[i].rgb_low, rgb_low);
8960 		gmt_M_rgb_copy (P->data[i].rgb_high, rgb_high);
8961 		gmt_M_rgb_copy (P->data[i].hsv_low, hsv_low);
8962 		gmt_M_rgb_copy (P->data[i].hsv_high, hsv_high);
8963 		P->data[i].z_low = z_out[lower];
8964 		P->data[i].z_high = z_out[upper];
8965 		P->is_gray = (gmt_M_is_gray (P->data[i].rgb_low) && gmt_M_is_gray (P->data[i].rgb_high));
8966 		P->is_bw = (gmt_M_is_bw(P->data[i].rgb_low) && gmt_M_is_bw (P->data[i].rgb_high));
8967 
8968 		/* Differences used in gmt_get_rgb_from_z */
8969 		for (k = 0; k < 4; k++) P->data[i].rgb_diff[k] = P->data[i].rgb_high[k] - P->data[i].rgb_low[k];
8970 		for (k = 0; k < 4; k++) P->data[i].hsv_diff[k] = P->data[i].hsv_high[k] - P->data[i].hsv_low[k];
8971 
8972 		/* When HSV is converted from RGB: avoid interpolation over hue differences larger than 180 degrees;
8973 		   take the shorter distance instead. This does not apply for HSV color tables, since there we assume
8974 		   that the H values are intentional and one might WANT to interpolate over more than 180 degrees. */
8975 		if (!(P->model & GMT_HSV)) {
8976 			if (P->data[i].hsv_diff[0] < -180.0) P->data[i].hsv_diff[0] += 360.0;
8977 			if (P->data[i].hsv_diff[0] >  180.0) P->data[i].hsv_diff[0] -= 360.0;
8978 		}
8979 		f = P->data[i].z_high - P->data[i].z_low;
8980 		if (f == 0.0) {
8981 			gmt_M_free (GMT, x);
8982 			gmt_M_free (GMT, lut);
8983 			if (log_mode) gmt_M_free (GMT, z_out);
8984 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Z-slice with dz = 0\n");
8985 			return (NULL);
8986 		}
8987 		P->data[i].i_dz = 1.0 / f;
8988 	}
8989 
8990 	gmt_M_free (GMT, x);
8991 	gmt_M_free (GMT, lut);
8992 	if (log_mode) gmt_M_free (GMT, z_out);
8993 	P->model = Pin->model;
8994 	P->categorical = Pin->categorical;
8995 	P->is_wrapping = Pin->is_wrapping;
8996 	P->is_continuous = continuous;
8997 	P->is_bw = Pin->is_bw;
8998 	P->is_gray = Pin->is_gray;
8999 	P->has_range = Pin->has_hinge = 0;	/* No longer normalized or special hinge after resampling */
9000 
9001 	/* Background, foreground, and nan colors */
9002 
9003 	gmt_M_memcpy (P->bfn, Pin->bfn, 3, struct GMT_BFN);	/* Copy over BNF */
9004 
9005 	if (reverse) {	/* Flip foreground and background colors */
9006 		gmt_M_rgb_copy (rgb_low, P->bfn[GMT_BGD].rgb);
9007 		gmt_M_rgb_copy (P->bfn[GMT_BGD].rgb, P->bfn[GMT_FGD].rgb);
9008 		gmt_M_rgb_copy (P->bfn[GMT_FGD].rgb, rgb_low);
9009 		gmt_M_rgb_copy (hsv_low, P->bfn[GMT_BGD].hsv);
9010 		gmt_M_rgb_copy (P->bfn[GMT_BGD].hsv, P->bfn[GMT_FGD].hsv);
9011 		gmt_M_rgb_copy (P->bfn[GMT_FGD].hsv, hsv_low);
9012 	}
9013 
9014 	/* Must set default annotation flags */
9015 	for (i = 0; i < P->n_colors; i++) P->data[i].annot = 1;
9016 	if (i) P->data[i-1].annot = 3;
9017 	else P->data[i].annot = 3;
9018 
9019 	gmtsupport_copy_palette_hdrs (GMT, P, Pin);
9020 	return (P);
9021 }
9022 
9023 /*! . */
gmtlib_write_cpt(struct GMT_CTRL * GMT,void * dest,unsigned int dest_type,unsigned int cpt_flags,struct GMT_PALETTE * P)9024 int gmtlib_write_cpt (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type, unsigned int cpt_flags, struct GMT_PALETTE *P) {
9025 	/* We write the CPT to fp [or stdout].
9026 	 * dest_type can be GMT_IS_[FILE|STREAM|FDESC]
9027 	 * cpt_flags is a combination of:
9028 	 * GMT_CPT_NO_BNF = Do not write BFN
9029 	 * GMT_CPT_EXTEND_BNF = Make B and F equal to low and high color
9030 	 */
9031 
9032 	unsigned int i, append = 0, hinge_mode = 0;
9033 	bool close_file = false, use[3] = {true, true, true};
9034 	double cmyk[5], z_hinge;
9035 	char format[GMT_BUFSIZ] = {""}, cpt_file[PATH_MAX] = {""}, code[3] = {'B', 'F', 'N'};
9036 	char lo[GMT_LEN64] = {""}, hi[GMT_LEN64] = {""}, kind[3] = {'L', 'U', 'B'};
9037 	static char *msg1[2] = {"Writing", "Appending"};
9038 	FILE *fp = NULL;
9039 	struct CPT_Z_SCALE *Z = NULL;	/* For unit manipulations */
9040 	struct GMT_PALETTE_HIDDEN *PH = NULL;
9041 
9042 	/* When writing the CPT to file it is no longer a normalized CPT with a hinge */
9043 	P->has_range = P->has_hinge = 0;
9044 
9045 	if (dest_type == GMT_IS_FILE && !dest) dest_type = GMT_IS_STREAM;	/* No filename given, default to stdout */
9046 
9047 	if (dest_type == GMT_IS_FILE) {	/* dest is a file name */
9048 		static char *msg2[2] = {"create", "append to"};
9049 		strncpy (cpt_file, dest, PATH_MAX-1);
9050 		append = (cpt_file[0] == '>');	/* Want to append to existing file */
9051 		if ((Z = gmtsupport_cpt_parse (GMT, &cpt_file[append], GMT_OUT, &hinge_mode, &z_hinge))) {
9052 			gmtsupport_cpt_z_scale (GMT, P, Z, GMT_OUT);
9053 			gmt_M_free (GMT, Z);
9054 		}
9055 		if ((fp = fopen (&cpt_file[append], (append) ? "a" : "w")) == NULL) {
9056 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot %s file %s\n", msg2[append], &cpt_file[append]);
9057 			return (GMT_ERROR_ON_FOPEN);
9058 		}
9059 		close_file = true;	/* We only close files we have opened here */
9060 	}
9061 	else if (dest_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
9062 		fp = (FILE *)dest;
9063 		if (fp == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
9064 		if (fp == GMT->session.std[GMT_OUT])
9065 			strcpy (cpt_file, "<stdout>");
9066 		else
9067 			strcpy (cpt_file, "<output stream>");
9068 	}
9069 	else if (dest_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
9070 		int *fd = dest;
9071 		if (fd && (fp = fdopen (*fd, "a")) == NULL) {
9072 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert file descriptor %d to stream in gmtlib_write_cpt\n", *fd);
9073 			return (GMT_ERROR_ON_FDOPEN);
9074 		}
9075 		else
9076 			close_file = true;	/* fdopen allocates memory */
9077 		if (fd == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
9078 		if (fp == GMT->session.std[GMT_OUT])
9079 			strcpy (cpt_file, "<stdout>");
9080 		else
9081 			strcpy (cpt_file, "<output file descriptor>");
9082 	}
9083 	else {
9084 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtlib_write_cpt\n", dest_type);
9085 		return (GMT_NOT_A_VALID_METHOD);
9086 	}
9087 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s CPT to %s\n", msg1[append], &cpt_file[append]);
9088 
9089 	PH = gmt_get_C_hidden (P);
9090 
9091 	if (hinge_mode && P->mode & GMT_CPT_SOFT_HINGE) { /* Add a hard hinge to the CPT */
9092 		P->mode -= GMT_CPT_SOFT_HINGE;
9093 		P->mode |= GMT_CPT_HARD_HINGE;
9094 		P->has_hinge = 1;
9095 		P->hinge = z_hinge;	/* This is the user-unit value to be used during stretching in gmt_stretch_cpt */
9096 	}
9097 
9098 	/* Start writing CPT info to fp */
9099 
9100 	for (i = 0; i < P->n_headers; i++) {	/* First write the old headers */
9101 		gmtlib_write_tableheader (GMT, fp, P->header[i]);
9102 	}
9103 	gmtlib_write_newheaders (GMT, fp, 0);	/* Write general header block */
9104 
9105 	if (!(P->model & GMT_COLORINT)) {}	/* Write nothing when color interpolation is not forced */
9106 	else if (P->model & GMT_HSV)
9107 		fprintf (fp, "# COLOR_MODEL = hsv\n");
9108 	else if (P->model & GMT_CMYK)
9109 		fprintf (fp, "# COLOR_MODEL = cmyk\n");
9110 	else
9111 		fprintf (fp, "# COLOR_MODEL = rgb\n");
9112 	if (P->has_hinge)
9113 		fprintf (fp, "# HARD_HINGE\n");
9114 	if (P->is_wrapping)
9115 		fprintf (fp, "# CYCLIC\n");
9116 	if (PH->auto_scale)
9117 		fprintf (fp, "# ENABLE_B_OPTION\n");
9118 
9119 	sprintf (format, "%%s\t%%s%%c");
9120 
9121 	/* Determine color at lower and upper limit of each interval */
9122 
9123 	for (i = 0; i < P->n_colors; i++) {
9124 
9125 		/* Print out one row */
9126 		gmt_ascii_format_col (GMT, lo, P->data[i].z_low,  GMT_OUT, GMT_Z);
9127 		gmt_ascii_format_col (GMT, hi, P->data[i].z_high, GMT_OUT, GMT_Z);
9128 
9129 		if (P->categorical) {
9130 			if (P->categorical & GMT_CPT_CATEGORICAL_KEY) strncpy (lo, P->data[i].key, GMT_LEN64-1);
9131 			if (P->model & GMT_HSV)
9132 				fprintf (fp, format, lo, gmtlib_puthsv (GMT, P->data[i].hsv_low), '\t');
9133 			else if (P->model & GMT_CMYK) {
9134 				gmtsupport_rgb_to_cmyk (P->data[i].rgb_low, cmyk);
9135 				fprintf (fp, format, lo, gmtlib_putcmyk (GMT, cmyk), '\t');
9136 			}
9137 			else if (P->model & GMT_NO_COLORNAMES)
9138 				fprintf (fp, format, lo, gmt_putrgb (GMT, P->data[i].rgb_low), '\t');
9139 			else
9140 				fprintf (fp, format, lo, gmt_putcolor (GMT, P->data[i].rgb_low), '\t');
9141 		}
9142 		else if (P->model & GMT_HSV) {
9143 			fprintf (fp, format, lo, gmtlib_puthsv (GMT, P->data[i].hsv_low), '\t');
9144 			fprintf (fp, format, hi, gmtlib_puthsv (GMT, P->data[i].hsv_high), '\t');
9145 		}
9146 		else if (P->model & GMT_CMYK) {
9147 			gmtsupport_rgb_to_cmyk (P->data[i].rgb_low, cmyk);
9148 			fprintf (fp, format, lo, gmtlib_putcmyk (GMT, cmyk), '\t');
9149 			gmtsupport_rgb_to_cmyk (P->data[i].rgb_high, cmyk);
9150 			fprintf (fp, format, hi, gmtlib_putcmyk (GMT, cmyk), '\t');
9151 		}
9152 		else if (P->model & GMT_NO_COLORNAMES) {
9153 			fprintf (fp, format, lo, gmt_putrgb (GMT, P->data[i].rgb_low), '\t');
9154 			fprintf (fp, format, hi, gmt_putrgb (GMT, P->data[i].rgb_high), '\t');
9155 		}
9156 		else {
9157 			fprintf (fp, format, lo, gmt_putcolor (GMT, P->data[i].rgb_low), '\t');
9158 			fprintf (fp, format, hi, gmt_putcolor (GMT, P->data[i].rgb_high), '\t');
9159 		}
9160 		if (P->data[i].annot) fprintf (fp, "%c", kind[P->data[i].annot-1]);
9161 		if (P->data[i].label) fprintf (fp, "\t;%s", P->data[i].label);
9162 		fputc ('\n', fp);
9163 	}
9164 
9165 	/* Background, foreground, and nan colors */
9166 
9167 	if (cpt_flags & GMT_CPT_NO_BNF) {	/* Do not want to write BFN to the CPT */
9168 		if (close_file) fclose (fp);
9169 		return (GMT_NOERROR);
9170 	}
9171 
9172 	if (P->is_wrapping || P->categorical) use[GMT_BGD] = use[GMT_FGD] = false;	/* These types of CPT has no back/foreground colors */
9173 
9174 	for (i = 0; i < 3; i++) {
9175 		if (!use[i]) continue;	/* This BNF entry does not apply to this CPT */
9176 		if (P->bfn[i].skip)
9177 			fprintf (fp, "%c\t-\n", code[i]);
9178 		else if (P->model & GMT_HSV)
9179 			fprintf (fp, "%c\t%s\n", code[i], gmtlib_puthsv (GMT, P->bfn[i].hsv));
9180 		else if (P->model & GMT_CMYK) {
9181 			gmtsupport_rgb_to_cmyk (P->bfn[i].rgb, cmyk);
9182 			fprintf (fp, "%c\t%s\n", code[i], gmtlib_putcmyk (GMT, cmyk));
9183 		}
9184 		else if (P->model & GMT_NO_COLORNAMES)
9185 			fprintf (fp, "%c\t%s\n", code[i], gmt_putrgb (GMT, P->bfn[i].rgb));
9186 		else
9187 			fprintf (fp, "%c\t%s\n", code[i], gmt_putcolor (GMT, P->bfn[i].rgb));
9188 	}
9189 	if (close_file) fclose (fp);
9190 
9191 	return (GMT_NOERROR);
9192 }
9193 
gmtsupport_reset_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P)9194 GMT_LOCAL void gmtsupport_reset_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P) {
9195 	/* Determine if CPT is continuous, B/W, or gray-scale */
9196 	unsigned int k;
9197 	gmt_M_unused (GMT);
9198 
9199 	P->is_continuous = false;
9200 	P->is_gray = P->is_bw = true;	/* May be changed when reading the actual colors */
9201 	for (k = 0; k < 3; k++) {
9202 		if (P->bfn[k].rgb[0] == -1.0) P->bfn[k].skip = true;
9203 		if (P->is_gray && !gmt_M_is_gray (P->bfn[k].rgb)) P->is_gray = P->is_bw = false;
9204 		if (P->is_bw && !gmt_M_is_bw(P->bfn[k].rgb)) P->is_bw = false;
9205 	}
9206 	for (k = 0; k < P->n_colors; k++) {
9207 		if (!P->is_continuous && !gmt_M_same_rgb(P->data[k].hsv_low,P->data[k].hsv_high)) P->is_continuous = true;
9208 		if (P->is_gray && !(gmt_M_is_gray (P->data[k].rgb_low) && gmt_M_is_gray (P->data[k].rgb_high))) P->is_gray = P->is_bw = false;
9209 		if (P->is_bw && !(gmt_M_is_bw(P->data[k].rgb_low) && gmt_M_is_bw(P->data[k].rgb_high))) P->is_bw = false;
9210 	}
9211 
9212 }
9213 
9214 /*! . */
gmt_truncate_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double z_low,double z_high)9215 struct GMT_PALETTE * gmt_truncate_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double z_low, double z_high) {
9216 	/* Truncate this CPT to start and end at z_low, z_high.  If either is NaN we do nothing at that end. */
9217 	unsigned int k, j, first = 0, last = P->n_colors - 1;
9218 	struct GMT_PALETTE_HIDDEN *PH = NULL;
9219 
9220 	if (gmt_M_is_dnan (z_low) && gmt_M_is_dnan (z_high)) return (P);	/* No change */
9221 
9222 	/* If this CPT has a natural range then its z-values are either 0 to 1 or -1 to +1.  We must first
9223 	 * expand it to its natural z-range before we can truncate since z_low/z_high are in user units */
9224 
9225 	if (P->has_range) {
9226 		gmt_stretch_cpt (GMT, P, 0.0, 0.0);	/* Stretch to its natural range first */
9227 		P->has_range = 0;
9228 	}
9229 
9230 	if ((!gmt_M_is_dnan (z_low) && z_low > P->data[last].z_high) || (!gmt_M_is_dnan (z_high) && z_high < P->data[0].z_low)) {
9231 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_truncate_cpt error: z_low/z_high [%g/%g] is completely outside range of this CPT [%g/%g]!\n",
9232 			z_low, z_high, P->data[0].z_low, P->data[last].z_high);
9233 			return NULL;
9234 	}
9235 	if (!gmt_M_is_dnan (z_low) && z_low < P->data[0].z_low) {
9236 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_truncate_cpt error: z_low = %g less than lowest z (%g)\n",
9237 			z_low, P->data[0].z_low, P->data[0].z_low);
9238 		return NULL;
9239 	}
9240 	if (!gmt_M_is_dnan (z_high) && z_high > P->data[last].z_high) {
9241 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_truncate_cpt: z_high = %g larger than highest z (%g)\n",
9242 			z_high, P->data[last].z_high, P->data[last].z_high);
9243 		return NULL;
9244 	}
9245 	/* OK, here we can sanely do things */
9246 	if (!gmt_M_is_dnan (z_low)) {	/* Find first slice fully or partially within range */
9247 		while (first < P->n_colors && P->data[first].z_high <= z_low) first++;
9248 		if (z_low > P->data[first].z_low)	/* Must truncate this slice */
9249 			gmtsupport_truncate_cpt_slice (&P->data[first], P->model & GMT_HSV, z_low, -1);
9250 	}
9251 	if (!gmt_M_is_dnan (z_high)) {	/* Find last slice fully or partially within range */
9252 		while (last > 0 && P->data[last].z_low >= z_high) last--;
9253 		if (P->data[last].z_high > z_high)	/* Must truncate this slice */
9254 			gmtsupport_truncate_cpt_slice (&P->data[last], P->model & GMT_HSV, z_high, +1);
9255 	}
9256 
9257 	PH = gmt_get_C_hidden (P);
9258 	for (k = 0; k < first; k++)
9259 		gmtsupport_free_range (GMT, PH, &P->data[k]);	/* Free any char strings */
9260 	for (k = last + 1; k < P->n_colors; k++)
9261 		gmtsupport_free_range (GMT, PH, &P->data[k]);	/* Free any char strings */
9262 
9263 	if (first) {	/* Shuffle CPT down */
9264 		for (k = 0, j = first; j <= last; k++, j++) {
9265 			P->data[k] = P->data[j];
9266 		}
9267 	}
9268 	P->n_colors = last - first + 1;
9269 	P->data = gmt_M_memory (GMT, P->data, P->n_colors, struct GMT_LUT);	/* Truncate */
9270 	/* Check if anything has changed regarding continuous, gray scale, or B/W */
9271 	gmtsupport_reset_cpt (GMT, P);
9272 	return (P);
9273 }
9274 
9275 /*! . */
gmtlib_init_cpt(struct GMT_CTRL * GMT,struct GMT_PALETTE * P)9276 void gmtlib_init_cpt (struct GMT_CTRL *GMT, struct GMT_PALETTE *P) {
9277 	/* For CPTs passed to/from external APIs we need to initialize some derived CPT quantities.
9278 	 * The data is received as r/b/g regardless of model so we must set hsv quantities. */
9279 	unsigned int k, n;
9280 
9281 	for (n = 0; n < P->n_colors; n++) {
9282 		gmt_rgb_to_hsv (P->data[n].rgb_low,  P->data[n].hsv_low);
9283 		gmt_rgb_to_hsv (P->data[n].rgb_high, P->data[n].hsv_high);
9284 		P->data[n].i_dz = 1.0 / (P->data[n].z_high - P->data[n].z_low);	/* Recompute inverse stepsize */
9285 		/* Differences used in gmt_get_rgb_from_z */
9286 		for (k = 0; k < 4; k++) P->data[n].rgb_diff[k] = P->data[n].rgb_high[k] - P->data[n].rgb_low[k];
9287 		for (k = 0; k < 4; k++) P->data[n].hsv_diff[k] = P->data[n].hsv_high[k] - P->data[n].hsv_low[k];
9288 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%d: %g to %g. R/G/B %s to %s. idz = %g diff R/G/B = %g/%g/%g\n", n,
9289 			P->data[n].z_low, P->data[n].z_high, gmt_putrgb (GMT, P->data[n].rgb_low), gmt_putrgb (GMT, P->data[n].rgb_high),
9290 			P->data[n].i_dz, P->data[n].rgb_diff[0], P->data[n].rgb_diff[1], P->data[n].rgb_diff[2]);
9291 	}
9292 	P->wrap_length = P->data[P->n_colors-1].z_high - P->data[0].z_low;
9293 	GMT->current.setting.color_model = (P->model | GMT_COLORINT);	/* So color interpolation will happen in the color system */
9294 
9295 	/* We leave BNF as we got them from the external API */
9296 }
9297 
9298 /*! . */
gmt_get_index(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double value)9299 int gmt_get_index (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double value) {
9300 	unsigned int index, lo, hi, mid;
9301 	gmt_M_unused(GMT);
9302 
9303 	if (gmt_M_is_dnan (value)) return (GMT_NAN - 3);	/* Set to NaN color */
9304 	if (P->is_wrapping)	/* Wrap to fit CPT range - we can never return back- or fore-ground colors */
9305 		value = MOD (value - P->data[0].z_low, P->wrap_length) + P->data[0].z_low;	/* Now within range */
9306 	else if (value > P->data[P->n_colors-1].z_high) {
9307 		if (P->categorical) {	/* Set to NaN for categorical */
9308 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Requested color lookup for z = %.12lg is not a categorical value - returning NaN color\n", value);
9309 			return GMT_NAN - 3;
9310 		}
9311 		return (GMT_FGD - 3);	/* Set to foreground color */
9312 	}
9313 	else if (value < P->data[0].z_low) {
9314 		if (P->categorical) {	/* Set to NaN for categorical */
9315 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Requested color lookup for z = %.12lg is not a categorical value - returning NaN color\n", value);
9316 			return GMT_NAN - 3;
9317 		}
9318 		return (GMT_BGD - 3);	/* Set to background color */
9319 	}
9320 
9321 	/* Must search for correct index */
9322 
9323 	/* Speedup by Mika Heiskanen. This works if the colortable
9324 	 * has been is sorted into increasing order. Careful when
9325 	 * modifying the tests in the loop, the test and the mid+1
9326 	 * parts are especially designed to make sure the loop
9327 	 * converges to a single index.
9328 	 */
9329 
9330 	lo = 0;
9331 	hi = P->n_colors - 1;
9332 	while (lo != hi)
9333 	{
9334 		mid = (lo + hi) / 2;
9335 		if (value >= P->data[mid].z_high)
9336 			lo = mid + 1;
9337 		else
9338 			hi = mid;
9339 	}
9340 	index = lo;
9341 
9342 	if (value >= P->data[index].z_low && value < P->data[index].z_high) {
9343 		if (P->categorical && !doubleAlmostEqualZero (P->data[index].z_low, value)) {
9344 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Requested color lookup for z = %.12lg is not a categorical value - returning NaN color\n", value);
9345 			index = GMT_NAN - 3;	/* Since categorical data is not on an interval */
9346 		}
9347 		return (index);
9348 	}
9349 
9350 	/* Slow search in case the table was not sorted
9351 	 * No idea whether it is possible, but it most certainly
9352 	 * does not hurt to have the code here as a backup.
9353 	 */
9354 
9355 	index = 0;
9356 	while (index < P->n_colors && ! (value >= P->data[index].z_low && value < P->data[index].z_high) ) index++;
9357 	if (index == P->n_colors) index--;	/* Because we use <= for last range */
9358 	if (P->categorical && !doubleAlmostEqualZero (P->data[index].z_low, value)) {
9359 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Requested color lookup for z = %.12lg is not a categorical value - returning NaN color\n", value);
9360 		index = GMT_NAN - 3;	/* Since categorical data is not on an interval */
9361 	}
9362 	return (index);
9363 }
9364 
9365 /*! . */
gmt_get_rgb_lookup(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,int index,double value,double * rgb)9366 void gmt_get_rgb_lookup (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, int index, double value, double *rgb) {
9367 	unsigned int i;
9368 	double rel, hsv[4];
9369 	struct GMT_PALETTE_HIDDEN *PH = gmt_get_C_hidden (P);
9370 
9371 	if (index < 0) {	/* NaN, Foreground, Background */
9372 		gmt_M_rgb_copy (rgb, P->bfn[index+3].rgb);
9373 		PH->skip = P->bfn[index+3].skip;
9374 	}
9375 	else if (P->data[index].skip) {		/* Set to page color for now */
9376 		gmt_M_rgb_copy (rgb, GMT->current.setting.ps_page_rgb);
9377 		PH->skip = true;
9378 	}
9379 	else {	/* Do linear interpolation between low and high colors */
9380 		rel = (value - P->data[index].z_low) * P->data[index].i_dz;
9381 		if (GMT->current.setting.color_model == (GMT_HSV | GMT_COLORINT)) {	/* Interpolation in HSV space */
9382 			for (i = 0; i < 4; i++) hsv[i] = P->data[index].hsv_low[i] + rel * P->data[index].hsv_diff[i];
9383 			gmt_hsv_to_rgb (rgb, hsv);
9384 		}
9385 		else {	/* Interpolation in RGB space */
9386 			for (i = 0; i < 4; i++) rgb[i] = P->data[index].rgb_low[i] + rel * P->data[index].rgb_diff[i];
9387 		}
9388 		PH->skip = false;
9389 	}
9390 }
9391 
9392 /*! . */
gmt_get_rgb_from_z(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double value,double * rgb)9393 int gmt_get_rgb_from_z (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double value, double *rgb) {
9394 	int index = gmt_get_index (GMT, P, value);
9395 	gmt_get_rgb_lookup (GMT, P, index, value, rgb);
9396 	return (index);
9397 }
9398 
9399 /*! . */
gmt_get_rgbtxt_from_z(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,char * text)9400 int gmt_get_rgbtxt_from_z (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, char *text) {
9401 	/* Assumes text is long enough to hold the color specification */
9402 	double z, rgb[4];
9403 	if (!strcmp (text, "-")) return 0;	/* Got just - which means we did not want fill; not an error */
9404 	if (strncmp (text, "z=", 2U)) return 0;	/* Not a z=<value> specification, so no error since presumably we got a color or fill */
9405 	if (P == NULL) {
9406 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Requested color lookup via z=<value> but no CPT was given via -A<cpt>\n");
9407 		return GMT_NO_CPT;
9408 	}
9409 	z = atof (&text[2]);	/* Extract the z value */
9410 	gmt_get_rgb_from_z (GMT, P, z, rgb);	/* Convert via CPT to r/g/b */
9411 	sprintf (text, "%s", gmt_putcolor (GMT, rgb));
9412 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Gave z=%g and returned %s\n", z, text);
9413 	return 0;
9414 }
9415 
9416 /*! . */
gmt_get_fill_from_z(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,double value,struct GMT_FILL * fill)9417 int gmt_get_fill_from_z (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, double value, struct GMT_FILL *fill) {
9418 	int index;
9419 	struct GMT_FILL *f = NULL;
9420 
9421 	index = gmt_get_index (GMT, P, value);
9422 
9423 	/* Check if pattern */
9424 
9425 	if (index >= 0 && (f = P->data[index].fill))
9426 		gmt_M_memcpy (fill, f, 1, struct GMT_FILL);
9427 	else if (index < 0 && (f = P->bfn[index+3].fill))
9428 		gmt_M_memcpy (fill, f, 1, struct GMT_FILL);
9429 	else {
9430 		gmt_get_rgb_lookup (GMT, P, index, value, fill->rgb);
9431 		fill->use_pattern = false;
9432 	}
9433 	return (index);
9434 }
9435 
gmt_same_fill(struct GMT_CTRL * GMT,struct GMT_FILL * F1,struct GMT_FILL * F2)9436 bool gmt_same_fill (struct GMT_CTRL *GMT, struct GMT_FILL *F1, struct GMT_FILL *F2) {
9437 	gmt_M_unused(GMT);
9438 	/* Return true if the two fills are identical */
9439 	if (F1->use_pattern != F2->use_pattern) return false;	/* One is a pattern, the other isn't, so cannot be the same */
9440 	if (F1->use_pattern) {	/* Both are patterns */
9441 		if (F1->pattern_no != F2->pattern_no) return false;	/* Different patters used */
9442 		if (F1->pattern_no == -1)	/* Both have custom fill patterns */
9443 			return !strcmp (F1->pattern, F2->pattern);
9444 		return true;	/* They are the same */
9445 	}
9446 	return gmt_M_same_rgb (F1->rgb, F2->rgb);	/* true if the same color, including transparency level */
9447 }
9448 
9449 /*! . */
gmtsupport_get_index_from_key(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,char * key)9450 GMT_LOCAL int gmtsupport_get_index_from_key (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, char *key) {
9451 	/* Will match key to a key in the color table.  Because a key is a string and may
9452 	 * some times (via shapefiles) be enclosed in quotes, we must skip using those quotes
9453 	 * in the string comparison.  The CPT key cannot have quotes and must be a single word.
9454 	 * Use the ;labal mechanism in the CPT to annotate colorbars with longer names.
9455 	 * Also note that there is no fore or back-ground color in this case.  We may assign
9456 	 * the NaN color if the key is blank or not matched in the CPT. */
9457 	unsigned int index;
9458 	size_t len, pos = 0;
9459 	gmt_M_unused(GMT);
9460 
9461 	if (!key || key[0] == '\0') return (GMT_NAN - 3);	/* Set to NaN color */
9462 	len = strlen (key);
9463 	if ((key[0] == '\"' && key[len-1] == '\"') || (key[0] == '\'' && key[len-1] == '\'')) {	/* Must exclude quotes */
9464 		pos = 1; len -= 2;
9465 	}
9466 	/* Must search for correct index */
9467 
9468 	for (index = 0; index < P->n_colors; index++) {
9469 		if (!strncmp (P->data[index].key, &key[pos], len))
9470 			return (int)index;
9471 	}
9472 	return (GMT_NAN - 3);
9473 }
9474 
9475 /*! . */
gmt_get_fill_from_key(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,char * key,struct GMT_FILL * fill)9476 int gmt_get_fill_from_key (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, char *key, struct GMT_FILL *fill) {
9477 	int index;
9478 	struct GMT_FILL *f = NULL;
9479 
9480 	index = gmtsupport_get_index_from_key (GMT, P, key);
9481 
9482 	/* Check if pattern */
9483 
9484 	if (index >= 0 && (f = P->data[index].fill))
9485 		gmt_M_memcpy (fill, f, 1, struct GMT_FILL);
9486 	else if (index < 0 && (f = P->bfn[index+3].fill))
9487 		gmt_M_memcpy (fill, f, 1, struct GMT_FILL);
9488 	else {	/* Not pattern */
9489 		if (index < 0) /* NaN color */
9490 			gmt_M_rgb_copy (fill->rgb, P->bfn[index+3].rgb);
9491 		else
9492 			gmt_M_rgb_copy (fill->rgb, P->data[index].rgb_low);
9493 		fill->use_pattern = false;
9494 	}
9495 	return (index);
9496 }
9497 
9498 /*! . */
gmt_illuminate(struct GMT_CTRL * GMT,double intensity,double rgb[])9499 void gmt_illuminate (struct GMT_CTRL *GMT, double intensity, double rgb[]) {
9500 	double di, hsv[4];
9501 
9502 	if (gmt_M_is_dnan (intensity)) return;
9503 	if (intensity == 0.0) return;
9504 	if (fabs (intensity) > 1.0) intensity = copysign (1.0, intensity);
9505 
9506 	gmt_rgb_to_hsv (rgb, hsv);
9507 	if (intensity > 0.0) {	/* Lighten the color */
9508 		di = 1.0 - intensity;
9509 		if (hsv[1] != 0.0) hsv[1] = di * hsv[1] + intensity * GMT->current.setting.color_hsv_max_s;
9510 		hsv[2] = di * hsv[2] + intensity * GMT->current.setting.color_hsv_max_v;
9511 	}
9512 	else {			/* Darken the color */
9513 		di = 1.0 + intensity;
9514 		if (hsv[1] != 0.0) hsv[1] = di * hsv[1] - intensity * GMT->current.setting.color_hsv_min_s;
9515 		hsv[2] = di * hsv[2] - intensity * GMT->current.setting.color_hsv_min_v;
9516 	}
9517 	if (hsv[1] < 0.0) hsv[1] = 0.0;
9518 	if (hsv[2] < 0.0) hsv[2] = 0.0;
9519 	if (hsv[1] > 1.0) hsv[1] = 1.0;
9520 	if (hsv[2] > 1.0) hsv[2] = 1.0;
9521 	gmt_hsv_to_rgb (rgb, hsv);
9522 }
9523 
9524 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9525  * gmtlib_akima computes the coefficients for a quasi-cubic hermite spline.
9526  * Same algorithm as in the IMSL library.
9527  * Programmer:	Paul Wessel
9528  * Date:	16-JAN-1987
9529  * Ver:		v.1-pc
9530  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9531  */
9532 
9533 /*! . */
gmtlib_akima(struct GMT_CTRL * GMT,double * x,double * y,uint64_t nx,double * c)9534 int gmtlib_akima (struct GMT_CTRL *GMT, double *x, double *y, uint64_t nx, double *c) {
9535 	uint64_t i, no;
9536 	double t1, t2, b, rm1, rm2, rm3, rm4;
9537 	gmt_M_unused(GMT);
9538 
9539 	/* Assumes that n >= 4 and x is monotonically increasing */
9540 
9541 	rm3 = (y[1] - y[0])/(x[1] - x[0]);
9542 	t1 = rm3 - (y[1] - y[2])/(x[1] - x[2]);
9543 	rm2 = rm3 + t1;
9544 	rm1 = rm2 + t1;
9545 
9546 	/* get slopes */
9547 
9548 	no = nx - 2;
9549 	for (i = 0; i < nx; i++) {
9550 		if (i >= no)
9551 			rm4 = rm3 - rm2 + rm3;
9552 		else
9553 			rm4 = (y[i+2] - y[i+1])/(x[i+2] - x[i+1]);
9554 		t1 = fabs(rm4 - rm3);
9555 		t2 = fabs(rm2 - rm1);
9556 		b = t1 + t2;
9557 		c[3*i] = (b != 0.0) ? (t1*rm2 + t2*rm3) / b : 0.5*(rm2 + rm3);
9558 		rm1 = rm2;
9559 		rm2 = rm3;
9560 		rm3 = rm4;
9561 	}
9562 	no = nx - 1;
9563 
9564 	/* compute the coefficients for the nx-1 intervals */
9565 
9566 	for (i = 0; i < no; i++) {
9567 		t1 = 1.0 / (x[i+1] - x[i]);
9568 		t2 = (y[i+1] - y[i])*t1;
9569 		b = (c[3*i] + c[3*i+3] - t2 - t2)*t1;
9570 		c[3*i+2] = b*t1;
9571 		c[3*i+1] = -b + (t2 - c[3*i])*t1;
9572 	}
9573 	return (GMT_NOERROR);
9574 }
9575 
9576 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9577  * gmtlib_cspline computes the coefficients for a natural cubic spline.
9578  * To evaluate, call gmtsupport_csplint
9579  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9580  */
9581 
9582 /*! . */
gmtlib_cspline(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,double * c)9583 int gmtlib_cspline (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, double *c) {
9584 	uint64_t i, k;
9585 	double ip, s, dx1, i_dx2, *u = gmt_M_memory (GMT, NULL, n, double);
9586 
9587 	/* Assumes that n >= 4 and x is monotonically increasing */
9588 
9589 	c[0] = c[n-1] = 0.0;	/* The other c[i] are set directly in the loop */
9590 	for (i = 1; i < n-1; i++) {
9591 		i_dx2 = 1.0 / (x[i+1] - x[i-1]);
9592 		dx1 = x[i] - x[i-1];
9593 		s = dx1 * i_dx2;
9594 		ip = 1.0 / (s * c[i-1] + 2.0);
9595 		c[i] = (s - 1.0) * ip;
9596 		u[i] = (y[i+1] - y[i]) / (x[i+1] - x[i]) - (y[i] - y[i-1]) / dx1;
9597 		u[i] = (6.0 * u[i] * i_dx2 - s * u[i-1]) * ip;
9598 	}
9599 	for (k = n-1; k > 0; k--) c[k-1] = c[k-1] * c[k] + u[k-1];
9600 	gmt_M_free (GMT, u);
9601 
9602 	return (GMT_NOERROR);
9603 }
9604 
9605 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9606  * gmt_intpol will interpolate from the dataset <x,y> onto a new set <u,v>
9607  * where <x,y> and <u> is supplied by the user. <v> is returned. The
9608  * parameter mode governs what interpolation scheme that will be used.
9609  * If u[i] is outside the range of x, then v[i] will contain a value
9610  * determined by the content of the GMT_EXTRAPOLATE_VAL. Namely, NaN if
9611  * GMT_EXTRAPOLATE_VAL[0] = 1; pure extrapolation if GMT_EXTRAPOLATE_VAL[0] = 1;
9612  * or a constant value (GMT_EXTRAPOLATE_VAL[1]) if GMT_EXTRAPOLATE_VAL[0] = 2
9613  *
9614  * input:  x = x-values of input data
9615  *	   y = y-values "    "     "
9616  *     w = weights or NULL [for smoothing spline only]
9617  *	   n = number of data pairs
9618  *	   m = number of desired interpolated values
9619  *	   u = x-values of these points
9620  *     p = fit-value for smoothing spline
9621  *	  mode = type of interpolation, with added 10*derivative_level [0,1,2]
9622  *	  mode = GMT_SPLINE_LINEAR [0] : Linear interpolation
9623  *	  mode = GMT_SPLINE_AKIMA [1] : Quasi-cubic hermite spline (gmtlib_akima)
9624  *	  mode = GMT_SPLINE_CUBIC [2] : Natural cubic spline (cubspl)
9625  *	  mode = GMT_SPLINE_CUBIC [3] : Smooth cubic spline
9626  *    mode = GMT_SPLINE_NN [4]    : No interpolation (closest point)
9627  *	  derivative_level = 0 :	The spline values
9628  *	  derivative_level = 1 :	The spline's 1st derivative
9629  *	  derivative_level = 2 :	The spline 2nd derivative
9630  * output: v = y-values at interpolated points
9631  * PS. v must have space allocated before calling gmt_intpol
9632  *
9633  * Programmer:	Paul Wessel
9634  * Date:	16-MAR-2009
9635  * Ver:		v.3.0
9636  * Now y can contain NaNs and we will interpolate within segments of clean data
9637  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
9638  */
9639 
9640 /*! . */
gmt_intpol(struct GMT_CTRL * GMT,double * x,double * y,double * w,uint64_t n,uint64_t m,double * u,double * v,double p,int mode)9641 int gmt_intpol (struct GMT_CTRL *GMT, double *x, double *y, double *w, uint64_t n, uint64_t m, double *u, double *v, double p, int mode) {
9642 	uint64_t i, this_n, this_m, start_i, start_j, stop_i, stop_j;
9643 	int err_flag = 0, smode, deriv;
9644 	bool down = false, check = true, clean = true;
9645 	double dx, *wp = NULL;
9646 
9647 	if (mode < 0) {	/* No need to check for sanity */
9648 		check = false;
9649 		mode = -mode;
9650 	}
9651 	smode = mode % GMT_SPLINE_SLOPE;	/* Get spline method first */
9652 	deriv = mode / GMT_SPLINE_SLOPE;	/* Get spline derivative order [0-2] */
9653 	if (smode > GMT_SPLINE_NN) smode = GMT_SPLINE_LINEAR;	/* Default to linear */
9654 	if (smode != GMT_SPLINE_NN && n < 4) smode = GMT_SPLINE_LINEAR;	/* Default to linear if 3 or fewer points, unless when nearest neighbor */
9655 	if (n < 2) {
9656 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Need at least 2 x-values\n");
9657 		return (GMT_DIM_TOO_SMALL);
9658 	}
9659 	mode = smode + GMT_SPLINE_SLOPE * deriv;	/* Reassemble the possibly new mode */
9660 	if (check) {
9661 		/* Check to see if x-values are monotonically increasing/decreasing */
9662 
9663 		dx = x[1] - x[0];
9664 		if (gmt_M_is_dnan (y[0])) clean = false;
9665 		if (dx > 0.0) {
9666 			for (i = 2; i < n && err_flag == 0; i++) {
9667 				if ((x[i] - x[i-1]) <= 0.0) err_flag = (int)i;
9668 				if (clean && gmt_M_is_dnan (y[i])) clean = false;
9669 			}
9670 		}
9671 		else {
9672 			down = true;
9673 			for (i = 2; i < n && err_flag == 0; i++) {
9674 				if ((x[i] - x[i-1]) >= 0.0) err_flag = (int)i;
9675 				if (clean && gmt_M_is_dnan (y[i])) clean = false;
9676 			}
9677 		}
9678 
9679 		if (err_flag) {
9680 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "x-values are not monotonically increasing/decreasing (at zero-based record %d)!\n", err_flag);
9681 			return (err_flag);
9682 		}
9683 
9684 	}
9685 
9686 	if (down) gmtsupport_intpol_reverse (x, u, n, m);	/* Must flip directions temporarily */
9687 
9688 	if (clean) {	/* No NaNs to worry about */
9689 		err_flag = gmtsupport_intpol_sub (GMT, x, y, w, n, m, u, v, p, mode);
9690 		if (err_flag != GMT_NOERROR) return (err_flag);
9691 		if (down) gmtsupport_intpol_reverse (x, u, n, m);	/* Must flip directions back */
9692 		return (GMT_NOERROR);
9693 	}
9694 
9695 	/* Here input has NaNs so we need to treat it section by section */
9696 
9697 	for (i = 0; i < m; i++) v[i] = GMT->session.d_NaN;	/* Initialize all output to NaN */
9698 	start_i = start_j = 0;
9699 	while (start_i < n && gmt_M_is_dnan (y[start_i])) start_i++;	/* First non-NaN data point */
9700 	while (start_i < n && start_j < m) {
9701 		stop_i = start_i + 1;
9702 		while (stop_i < n && !gmt_M_is_dnan (y[stop_i])) stop_i++;	/* Wind to next NaN point (or past end of array) */
9703 		this_n = stop_i - start_i;	/* Number of clean points to interpolate from */
9704 		stop_i--;			/* Now stop_i is the ID of the last usable point */
9705 		if (this_n == 1) {		/* Not enough to interpolate, just skip this single point */
9706 			start_i++;	/* Skip to next point (which is NaN) */
9707 			while (start_i < n && gmt_M_is_dnan (y[start_i])) start_i++;	/* Wind to next non-NaN data point */
9708 			continue;
9709 		}
9710 		/* OK, have enough points to interpolate; find corresponding output section */
9711 		while (start_j < m && u[start_j] < x[start_i]) start_j++;
9712 		if (start_j == m) continue;	/* Ran out of points */
9713 		stop_j = start_j;
9714 		while (stop_j < m && u[stop_j] <= x[stop_i]) stop_j++;
9715 		this_m = stop_j - start_j;	/* Number of output points to interpolate to */
9716 		wp = (w) ? &w[start_j] : NULL;
9717 		err_flag = gmtsupport_intpol_sub (GMT, &x[start_i], &y[start_i], wp, this_n, this_m, &u[start_j], &v[start_j], p, mode);
9718 		if (err_flag != GMT_NOERROR) return (err_flag);
9719 		start_i = stop_i + 1;	/* Move to point after last usable point in current section */
9720 		while (start_i < n && gmt_M_is_dnan (y[start_i])) start_i++;	/* Next section's first non-NaN data point */
9721 	}
9722 
9723 	if (down) gmtsupport_intpol_reverse (x, u, n, m);	/* Must flip directions back */
9724 
9725 	return (GMT_NOERROR);
9726 }
9727 
9728 /*! . */
gmtlib_inplace_transpose(gmt_grdfloat * A,unsigned int n_rows,unsigned int n_cols)9729 void gmtlib_inplace_transpose (gmt_grdfloat *A, unsigned int n_rows, unsigned int n_cols) {
9730 	/* In-place transpose of a gmt_grdfloat grid array.  Based on example
9731 	 * code from http://www.geeksforgeeks.org/inplace-m-x-n-size-matrix-transpose
9732 	 * Switched to C-style bit flag.
9733 	 */
9734 	uint64_t size = ((uint64_t)n_rows) * ((uint64_t)n_cols) - 1ULL;
9735 	gmt_grdfloat t;	/* holds element to be replaced, eventually becomes next element to move */
9736 	uint64_t next;	/* location of 't' to be moved */
9737 	uint64_t cycleBegin;	/* holds start of cycle */
9738 	uint64_t i;	/* iterator */
9739 	uint64_t n_words = ((size + 1ULL) / 32ULL) + 1ULL;
9740 	unsigned int *mark = NULL;
9741 	unsigned int bits[32];
9742 
9743 	mark = calloc (n_words, sizeof (unsigned int));
9744 	for (i = 1, bits[0] = 1; i < 32; i++) bits[i] = bits[i-1] << 1;
9745 	gmtsupport_set_bit (mark, 0ULL, bits);
9746 	gmtsupport_set_bit (mark, size, bits);
9747 	i = 1;	/* Note that A[0] and A[size-1] won't move */
9748 	while (i < size) {
9749 		cycleBegin = i;
9750 		t = A[i];
9751 		do {
9752 			next = (i * n_rows) % size;
9753 			gmt_M_float_swap (A[next], t);
9754 			gmtsupport_set_bit (mark, i, bits);
9755 			i = next;
9756 		}
9757 		while (i != cycleBegin);
9758 
9759 		/* Get Next Move (what about querying random location?) */
9760 		for (i = 1; i < size && gmtsupport_is_set (mark, i, bits); i++);
9761 	}
9762 	gmt_M_str_free (mark);
9763 }
9764 
9765 /*! . */
gmt_contlabel_init(struct GMT_CTRL * GMT,struct GMT_CONTOUR * G,unsigned int mode)9766 void gmt_contlabel_init (struct GMT_CTRL *GMT, struct GMT_CONTOUR *G, unsigned int mode) {
9767 	/* Assign default values to structure */
9768 	gmt_M_memset (G, 1, struct GMT_CONTOUR);	/* Sets all to 0 */
9769 	if (mode == 1) {
9770 		G->line_type = 1;
9771 		strcpy (G->line_name, "Contour");
9772 	}
9773 	else {
9774 		G->line_type = 0;
9775 		strcpy (G->line_name, "Line");
9776 	}
9777 	snprintf (G->label_file, PATH_MAX, "%s_labels.txt", G->line_name);
9778 	G->must_clip = true;
9779 	G->draw = true;
9780 	G->spacing = true;
9781 	G->half_width = UINT_MAX;	/* Auto */
9782 	G->label_dist_frac = 0.25;	/* Fraction of above head start for closed labels */
9783 	G->box = 2;			/* Rect box shape is Default */
9784 	G->label_dist_spacing = (GMT->current.setting.proj_length_unit == GMT_CM) ? 10.0 / 2.54 : 4.0;	/* Inches */
9785 	G->clearance[GMT_X] = G->clearance[GMT_Y] = 15.0;	/* 15 % */
9786 	G->clearance_flag = 1;	/* Means we gave percentages of label font size */
9787 	G->just = PSL_MC;
9788 	G->font_label = GMT->current.setting.font_annot[GMT_PRIMARY];	/* FONT_ANNOT_PRIMARY */
9789 	G->font_label.size = 9.0;
9790 	G->font_label.set = 0;
9791 	G->dist_unit = GMT->current.setting.proj_length_unit;
9792 	G->pen = GMT->current.setting.map_default_pen;
9793 	G->debug_pen = GMT->current.setting.map_default_pen;
9794 	G->line_pen = GMT->current.setting.map_default_pen;
9795 	gmt_M_rgb_copy (G->rgb, GMT->current.setting.ps_page_rgb);		/* Default box color is page color [nominally white] */
9796 }
9797 
gmtsupport_is_cpt_file(struct GMT_CTRL * GMT,char * file)9798 GMT_LOCAL int gmtsupport_is_cpt_file (struct GMT_CTRL *GMT, char *file) {
9799 	/* Read a file that may be a CPT file or a contour listing file.  We will return
9800 	 * 0 if it is a contour file, 1 if a CPT and -1 if we get read errors.
9801 	 * Because a non-commented record in a contour listing file is of the format
9802 	 *   cval [angle] C|A|c|a [pen]] OR  cval C|A|c|a [angle [pen]] (deprecated format),
9803 	 * we can uniquely determine if it is that sort of file by finding the lone
9804 	 * character A|a|C|c between white space on the left and white space or NULL on right.
9805 	 * If that is not found then it must be a CPT file.
9806 	 */
9807 	int answer = 1;	/* Default response is that this is a CPT file, flagged as 1 */
9808 	char *txt = NULL, *c = NULL;
9809 	unsigned int k = 0;
9810 	struct GMT_DATASET *C = NULL;
9811 
9812 	if ((C = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_TEXT, GMT_IO_ASCII, NULL, file, NULL)) == NULL) {
9813 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to read potential CPT or contour information file %s\n", file);
9814 		return (-1);
9815 	}
9816 	if (C->n_records == 0) {
9817 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No records found in CPT or contour information file %s\n", file);
9818 		return (-1);
9819 	}
9820 	if (C->table[0]->segment[0]->text == NULL || (txt = C->table[0]->segment[0]->text[0]) == NULL) {
9821 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No text records found in CPT or contour information file %s\n", file);
9822 		return (-1);
9823 	}
9824 	if ((c = strchr (txt, ';'))) c[0] = '\0';	/* Chop off optional CPT labels since they may have text that can trick us */
9825 
9826 	while (txt[k] && !(txt[k] == '\t' || txt[k] == ' ')) k++;	/* Scan to first occurrence of white space, thus skipping <cval> */
9827 	while (txt[k] && strchr ("AaCc", txt[k]) == NULL) k++;		/* Scan to first occurrence of one of the key letters */
9828 	if ((k && (txt[k-1] == '\t' || txt[k-1] == ' ')) && (txt[k] && (txt[k+1] == '\t' || txt[k+1] == ' ' || txt[k+1] == '\0')))
9829 		answer = 0;	/* Yes, this is a contour file */
9830 	if (c) c[0] = ';';	/* Restore semicolon to be nice */
9831 
9832 	if (GMT_Destroy_Data (GMT->parent, &C)) {
9833 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unable to free memory for contour information file %s\n", file);
9834 	}
9835 
9836 	if (answer)
9837 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "File %s assumed to be a CPT file\n", file);
9838 	else
9839 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "File %s determined to be a contour information file\n", file);
9840 
9841 	return (answer);	/* If we find a recognized contour type then we return true */
9842 }
9843 
9844 
gmt_contour_C_arg_parsing(struct GMT_CTRL * GMT,char * arg,struct CONTOUR_ARGS * A)9845 unsigned int gmt_contour_C_arg_parsing (struct GMT_CTRL *GMT, char *arg, struct CONTOUR_ARGS *A) {
9846 	unsigned int n_errors = 0;
9847 	struct GMTAPI_CTRL *API = GMT->parent;
9848 
9849 	/* Parse the -C<arg> options for grdcontour and pscontour */
9850 
9851 	if (gmt_M_no_cpt_given (arg)) {
9852 		if (GMT->current.setting.run_mode == GMT_MODERN)
9853 			A->check = true;	/* Must see (after parsing) if there is a current CPT in this modern mode session */
9854 		else {
9855 			GMT_Report (API, GMT_MSG_ERROR, "Option -C: No argument given\n");
9856 			n_errors++;
9857 		}
9858 	}
9859 	else if (gmt_M_file_is_memory (arg)) {	/* Passed a CPT memory reference from a module */
9860 		A->interval = 1.0;	/* This takes us past a check only */
9861 		A->cpt = true;
9862 		gmt_M_str_free (A->file);
9863 		A->file = strdup (arg);
9864 	}
9865 	else if (!gmt_access (GMT, arg, R_OK) || gmt_file_is_cache (API, arg)) {	/* Gave a readable file (CPT or contour file) */
9866 		int answer;
9867 		A->interval = 1.0;	/* This takes us past a check only */
9868 		if ((answer = gmtsupport_is_cpt_file (GMT, arg)) == -1)
9869 			n_errors++;
9870 		else
9871 			A->cpt = (answer == 1);
9872 		gmt_M_str_free (A->file);
9873 		A->file = strdup (arg);
9874 	}
9875 	else if (strchr (arg, ',')) {	/* Gave a comma-separated list of one or more contours */
9876 		A->interval = 1.0;	/* This takes us past a check only */
9877 		gmt_M_str_free (A->file);
9878 		A->file = strdup (arg);
9879 	}
9880 	else if (arg[0] == '+' && (isdigit(arg[1]) || strchr ("-+.", arg[1]))) {
9881 		if (!gmt_M_compat_check (GMT, 5))
9882 			GMT_Report (API, GMT_MSG_COMPAT, "Option -C: Specifying single contour with leading + is deprecated.  Please use -C<cont>, instead\n");
9883 		A->single_cont = atof (&arg[1]);
9884 	}
9885 	else if (arg[0] != '-') {	/* Constant contour interval */
9886 		A->interval = atof (arg);
9887 		if (gmt_M_is_zero (A->interval)) {
9888 			GMT_Report (API, GMT_MSG_ERROR, "Option -C: Contour interval cannot be zero\n");
9889 			n_errors++;
9890 		}
9891 	}
9892 	else {
9893 		GMT_Report (API, GMT_MSG_ERROR, "Option -C: Contour interval cannot be negative (%s)\n", arg);
9894 		n_errors++;
9895 	}
9896 	return (n_errors);
9897 }
9898 
gmt_contour_first_pos(struct GMT_CTRL * GMT,char * arg)9899 unsigned int gmt_contour_first_pos (struct GMT_CTRL *GMT, char *arg) {
9900 	/* Because of backwards compatibility, we need to anticipate shits like -A+1 for a
9901 	 * single annotated contour and hence cannot confuse it with a modifier for contour specs.
9902 	 * Thus, here we scan past any leading single contour specification using deprecated syntax. */
9903 	gmt_M_unused(GMT);
9904 	unsigned int k = 1;
9905 	if (arg[0] != '+') return 0;	/* Start checking from start */
9906 	if (isalpha (arg[1]) || arg[1] == '=') return 0;	/* Standard modifier */
9907 	/* Here we must have +<value> which we wish to skip */
9908 	if (arg[k] == '+') k++;	/* Must step over a signed contour since ++2 and +-2 were OK back then */
9909 	while (arg[k] && arg[k] != '+') k++;
9910 	return k;
9911 }
9912 
gmt_contour_A_arg_parsing(struct GMT_CTRL * GMT,char * arg,struct CONTOUR_ARGS * A)9913 unsigned int gmt_contour_A_arg_parsing (struct GMT_CTRL *GMT, char *arg, struct CONTOUR_ARGS *A) {
9914 	unsigned int n_errors = 0;
9915 	struct GMTAPI_CTRL *API = GMT->parent;
9916 
9917 	/* Parse the -A<arg> options for grdcontour and pscontour after contour-specs are stripped off */
9918 	if (arg[0] == '\0') return GMT_NOERROR;	/* Probably the -A was just setting contour parameters via modifiers */
9919 
9920 	if (arg[0] == 'n' && arg[1] == '\0')	/* -An turns off all labels */
9921 		A->mode = 1;	/* Turn off all labels */
9922 	else if (arg[0] == '+' && (isdigit(arg[1]) || strchr ("-+.", arg[1]))) {
9923 		if (!gmt_M_compat_check (GMT, 5))
9924 			GMT_Report (API, GMT_MSG_COMPAT, "Option -A: Specifying single contour with leading + is deprecated.  Please use -A<cont>, instead\n");
9925 		A->single_cont = atof (&arg[1]);
9926 	}
9927 	else if (strchr (arg, ',')) {	/* Gave a comma-separated list of one or more annotated contours */
9928 		gmt_M_str_free (A->file);
9929 		A->file = strdup (arg);
9930 	}
9931 	else if (arg[0] == '-' && arg[1] == '\0') {	/* -A- is deprecated version of -An */
9932 		if (!gmt_M_compat_check (GMT, 5))
9933 			GMT_Report (API, GMT_MSG_COMPAT, "Option -A: Turning off annotations with -A- is deprecated.  Please use -An instead\n");
9934 		A->mode = 1;	/* Turn off all labels */
9935 	}
9936 	else if (arg[0] != '-') {	/* Constant annotated contour interval */
9937 		A->interval = atof (arg);
9938 		if (gmt_M_is_zero (A->interval)) {
9939 			GMT_Report (API, GMT_MSG_ERROR, "Option -A: Contour interval cannot be zero\n");
9940 			n_errors++;
9941 		}
9942 	}
9943 	else {
9944 		GMT_Report (API, GMT_MSG_ERROR, "Option -A: Annotated contour interval cannot be negative (%s)\n", arg);
9945 		n_errors++;
9946 	}
9947 	return n_errors;
9948 }
9949 
gmtsupport_contour_old_T_parser(struct GMT_CTRL * GMT,char * arg,struct CONTOUR_CLOSED * I)9950 GMT_LOCAL unsigned int gmtsupport_contour_old_T_parser (struct GMT_CTRL *GMT, char *arg, struct CONTOUR_CLOSED *I) {
9951 	/* The backwards compatible parser for old-style -T option: */
9952 	/* -T[+|-][<gap>[c|i|p]/<length>[c|i|p]][:LH] but also accept new -Th|l<...> */
9953 	int n, j;
9954 	unsigned int n_errors = 0;
9955 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""};
9956 	if (strchr (arg, '/')) {	/* Gave gap/length */
9957 		n = sscanf (arg, "%[^/]/%[^:]", txt_a, txt_b);
9958 		if (n == 2) {
9959 			I->dim[GMT_X] = gmt_M_to_inch (GMT, txt_a);
9960 			I->dim[GMT_Y] = gmt_M_to_inch (GMT, txt_b);
9961 		}
9962 	}
9963 	for (j = 0; arg[j] && arg[j] != ':'; j++);
9964 	if (arg[j] == ':') I->label = true, j++;
9965 	if (arg[j]) {	/* Override high/low markers */
9966 		if (strlen (&(arg[j])) == 2) {	/* Standard :LH syntax */
9967 			txt_a[0] = arg[j++];	txt_a[1] = '\0';
9968 			txt_b[0] = arg[j++];	txt_b[1] = '\0';
9969 		}
9970 		else if (strchr (&(arg[j]), ',')) {	/* Found :<labellow>,<labelhigh> */
9971 			sscanf (&(arg[j]), "%[^,],%s", txt_a, txt_b);
9972 		}
9973 		else {
9974 			GMT_Report (GMT->parent, GMT_MSG_ERROR,
9975 			            "Option -T: Give low and high labels either as :LH or :<low>,<high>.\n");
9976 			I->label = false;
9977 			n_errors++;
9978 		}
9979 		if (I->label) {	/* Replace defaults */
9980 			I->txt[0] = strdup (txt_a);
9981 			I->txt[1] = strdup (txt_b);
9982 		}
9983 	}
9984 	return (n_errors);
9985 }
9986 
gmt_contour_T_arg_parsing(struct GMT_CTRL * GMT,char * arg,struct CONTOUR_CLOSED * I)9987 unsigned int gmt_contour_T_arg_parsing (struct GMT_CTRL *GMT, char *arg, struct CONTOUR_CLOSED *I) {
9988 	unsigned int j = 0, n_errors = 0;
9989 	int n;
9990 	char string[GMT_LEN256] = {""};
9991 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""};
9992 	struct GMTAPI_CTRL *API = GMT->parent;
9993 
9994 	/* Parse the -T<arg> options for grdcontour and pscontour */
9995 
9996 	I->high = I->low = true;	/* Default if just -T is given */
9997 	if (arg[0]) {	/* But here we gave more options */
9998 		if (arg[0] == 'h' || (arg[0] == '+' && !strchr ("adl", arg[1])))			/* Only tick local highs */
9999 			I->low = false, j = 1;
10000 		else if (arg[0] == 'l' || arg[0] == '-')	/* Only tick local lows */
10001 			I->high = false, j = 1;
10002 		else
10003 			j = 0;
10004 		if (gmt_found_modifier (GMT, arg, "adl")) {	/* New parser */
10005 			if (gmt_validate_modifiers (GMT, arg, 'T', "adl", GMT_MSG_ERROR)) n_errors++;
10006 			if (gmt_get_modifier (arg, 'a', string))
10007 				I->all = true;
10008 			if (gmt_get_modifier (arg, 'd', string))
10009 				if ((n = gmt_get_pair (GMT, string, GMT_PAIR_DIM_NODUP, I->dim)) < 1) n_errors++;
10010 			if (gmt_get_modifier (arg, 'l', string)) {	/* Want to label innermost contours */
10011 				I->label = true;
10012 				if (string[0] == 0)
10013 					;	/* Use default labels */
10014 				else if (strlen (string) == 2) {	/* Standard +lLH syntax */
10015 					char A[2] = {0, 0};
10016 					A[0] = string[0];	I->txt[0] = strdup (A);
10017 					A[0] = string[1];	I->txt[1] = strdup (A);
10018 				}
10019 				else if (strchr (string, ',') && (n = sscanf (string, "%[^,],%s", txt_a, txt_b)) == 2) {	/* Found :<labellow>,<labelhigh> */
10020 					I->txt[0] = strdup (txt_a);
10021 					I->txt[1] = strdup (txt_b);
10022 				}
10023 				else {
10024 					GMT_Report (API, GMT_MSG_ERROR,
10025 					            "Option -T: Give low and high labels either as +lLH or +l<low>,<high>.\n");
10026 					n_errors++;
10027 				}
10028 			}
10029 		}
10030 		else {
10031 			if (gmt_M_compat_check (API->GMT, 4))  {
10032 				GMT_Report (API, GMT_MSG_COMPAT, "Your format for -T is deprecated (but accepted); use -T[l|h][+d<tick_gap>[%s][/<tick_length>[%s]]][+lLH] instead\n",
10033 					GMT_DIM_UNITS_DISPLAY, GMT_DIM_UNITS_DISPLAY);
10034 				n_errors += gmtsupport_contour_old_T_parser (GMT, &arg[j], I);
10035 			}
10036 			else {
10037 				GMT_Report (API, GMT_MSG_COMPAT, "Option -T: Your format for -T is deprecated; use -T[l|h][+d<tick_gap>[%s][/<tick_length>[%s]]][+lLH] instead\n",
10038 					GMT_DIM_UNITS_DISPLAY, GMT_DIM_UNITS_DISPLAY);
10039 				n_errors++;
10040 			}
10041 		}
10042 		n_errors += gmt_M_check_condition (GMT, I->dim[GMT_X] <= 0.0 || I->dim[GMT_Y] == 0.0,
10043 		                "Option -T: Expected\n\t-T[l|h][+d<tick_gap>[%s][/<tick_length>[%s]]][+lLH], <tick_gap> must be > 0\n",
10044 		                	GMT_DIM_UNITS_DISPLAY, GMT_DIM_UNITS_DISPLAY);
10045 	}
10046 
10047 	return (n_errors);
10048 }
10049 
10050 /*! . */
gmt_contlabel_specs(struct GMT_CTRL * GMT,char * txt,struct GMT_CONTOUR * G)10051 int gmt_contlabel_specs (struct GMT_CTRL *GMT, char *txt, struct GMT_CONTOUR *G) {
10052 	unsigned int k = 0, bad = 0, pos = 0;
10053 	size_t L;
10054 	char p[GMT_BUFSIZ] = {""}, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, c;
10055 	char *specs = NULL;
10056 
10057 	/* Decode [+a<angle>|n|p[u|d]][+c<dx>[/<dy>]][+d[<pen>]][+e][+f<font>][+g<fill>][+i][+j<just>][+l<label>][+n|N<dx>[/<dy>]][+o][+p[<pen>]][+r<min_rc>][+t[<file>]][+u<unit>][+v][+w<width>][+x|X<suffix>][+=<prefix>] strings */
10058 
10059 	/* txt is pointing to the very first modifier */
10060 
10061 	G->nudge_flag = 0;
10062 	G->draw = true;
10063 
10064 	if (txt == NULL || !txt[k]) return (0);
10065 
10066 	/* Decode new-style +separated substrings */
10067 
10068 	specs = &txt[k+1];
10069 	while ((gmt_strtok (specs, "+", &pos, p))) {
10070 		switch (p[0]) {
10071 			case 'a':	/* Angle specification */
10072 				if (p[1] == 'p' || p[1] == 'P')	{	/* Line-parallel label */
10073 					G->angle_type = G->hill_label = 0;
10074 					if (p[2] == 'u' || p[2] == 'U')		/* Line-parallel label readable when looking up hill */
10075 						G->hill_label = +1;
10076 					else if (p[2] == 'd' || p[2] == 'D')	/* Line-parallel label readable when looking down hill */
10077 						G->hill_label = -1;
10078 				}
10079 				else if (p[1] == 'n' || p[1] == 'N')	/* Line-normal label */
10080 					G->angle_type = 1;
10081 				else {					/* Label at a fixed angle */
10082 					G->label_angle = atof (&p[1]);
10083 					G->angle_type = 2;
10084 					gmt_lon_range_adjust (GMT_IS_M180_TO_P180_RANGE, &G->label_angle);	/* Now -180/+180 */
10085 					while (fabs (G->label_angle) > 90.0) G->label_angle -= copysign (180.0, G->label_angle);
10086 				}
10087 				break;
10088 
10089 			case 'c':	/* Clearance specification */
10090 				k = sscanf (&p[1], "%[^/]/%s", txt_a, txt_b);
10091 				G->clearance_flag = ((strchr (txt_a, '%')) ? 1 : 0);
10092 				if (G->clearance_flag) {	/* Chop off percentage sign(s) and read as unitless values */
10093 					if ((L = strlen (txt_a)) && txt_a[L-1] == '%') txt_a[L-1] = '\0';
10094 					G->clearance[GMT_X] = atof (txt_a);
10095 					if (k == 2 && (L = strlen (txt_b)) && txt_b[L-1] == '%') txt_b[L-1] = '\0';
10096 					G->clearance[GMT_Y] = (k == 2) ? atof (txt_b) : G->clearance[GMT_X];
10097 				}
10098 				else {	/* Deal with units */
10099 					G->clearance[GMT_X] = gmt_M_to_inch (GMT, txt_a);
10100 					G->clearance[GMT_Y] = (k == 2 ) ? gmt_M_to_inch (GMT, txt_b) : G->clearance[GMT_X];
10101 				}
10102 				if (k == 0) bad++;
10103 				break;
10104 
10105 			case 'd':	/* Debug option - draw helper points or lines */
10106 				G->debug = true;
10107 				if (p[1] && gmt_getpen (GMT, &p[1], &G->debug_pen)) bad++;
10108 				break;
10109 
10110 			case 'e':	/* Just lay down text info; Delay actual label plotting until a psclip -C call */
10111 				G->delay = true;
10112 				break;
10113 
10114 			case 'f':	/* Font specification */
10115 				if (gmt_getfont (GMT, &p[1], &G->font_label)) bad++;
10116 				break;
10117 
10118 			case 'g':	/* Box Fill specification */
10119 				if (p[1] && gmt_getrgb (GMT, &p[1], G->rgb)) bad++;
10120 				G->fillbox = true;
10121 				G->must_clip = (G->rgb[3] > 0.0);	/* May still be transparent if gave transparency; else opaque */
10122 				break;
10123 
10124 			case 'h':	/* Hide the lines used to place labels [Was this ever documented? If not, remove] */
10125 			case 'i':	/* Make line invisible */
10126 				G->draw = false;
10127 				break;
10128 
10129 			case 'j':	/* Justification specification */
10130 				txt_a[0] = p[1];	txt_a[1] = p[2];	txt_a[2] = '\0';
10131 				G->just = gmt_just_decode (GMT, txt_a, PSL_MC);
10132 				break;
10133 			case 'k':	/* Font color specification (backwards compatibility only since font color is now part of font specification */
10134 				if (gmt_M_compat_check (GMT, 4)) {
10135 					GMT_Report (GMT->parent, GMT_MSG_COMPAT,
10136 					            "+k<fontcolor> in contour label spec is obsolete, now part of +f<font>\n");
10137 					if (gmt_getfill (GMT, &p[1], &(G->font_label.fill))) bad++;
10138 				}
10139 				else
10140 					bad++;
10141 				break;
10142 			case 'l':	/* Exact Label specification */
10143 				strncpy (G->label, &p[1], GMT_BUFSIZ-1);
10144 				G->label_type = GMT_LABEL_IS_CONSTANT;
10145 				break;
10146 
10147 			case 'L':	/* Label code specification */
10148 				switch (p[1]) {
10149 					case 'h':	/* Take the first string in segment headers */
10150 						G->label_type = GMT_LABEL_IS_HEADER;
10151 						break;
10152 					case 'd':	/* Use the current plot distance in chosen units */
10153 						G->label_type = GMT_LABEL_IS_PDIST;
10154 						G->dist_unit = gmtlib_unit_lookup (GMT, p[2], GMT->current.setting.proj_length_unit);
10155 						break;
10156 					case 'D':	/* Use current map distance in chosen units */
10157 						G->label_type = GMT_LABEL_IS_MDIST;
10158 						if (p[2] && strchr ("defkMn", (int)p[2])) {	/* Found a valid unit */
10159 							c = p[2];
10160 							if (gmt_init_distaz (GMT, c, gmt_M_sph_mode (GMT), GMT_LABEL_DIST) == GMT_NOT_A_VALID_TYPE) bad++;
10161 						}
10162 						else 	/* Meaning "not set" */
10163 							c = 0;
10164 						G->dist_unit = (int)c;
10165 						break;
10166 					case 'f':	/* Take the 3rd column in fixed contour location file */
10167 						G->label_type = GMT_LABEL_IS_FFILE;
10168 						break;
10169 					case 'x':	/* Take the first string in segment headers in the crossing file */
10170 						G->label_type = GMT_LABEL_IS_XFILE;
10171 						break;
10172 					case 'n':	/* Use the current segment number */
10173 						G->label_type = GMT_LABEL_IS_SEG;
10174 						break;
10175 					case 'N':	/* Use <current file number>/<segment number> */
10176 						G->label_type = GMT_LABEL_IS_FSEG;
10177 						break;
10178 					default:	/* Probably meant lower case l */
10179 						strncpy (G->label, &p[1], GMT_BUFSIZ-1);
10180 						G->label_type = GMT_LABEL_IS_HEADER;
10181 						break;
10182 				}
10183 				break;
10184 
10185 			case 'n':	/* Nudge specification; dx/dy are increments along local line axes */
10186 				G->nudge_flag = 1;
10187 				/* Intentionally fall through - to 'N' */
10188 			case 'N':	/* Nudge specification; dx/dy are increments along plot axes */
10189 				G->nudge_flag++;
10190 				k = sscanf (&p[1], "%[^/]/%s", txt_a, txt_b);
10191 				G->nudge[GMT_X] = gmt_M_to_inch (GMT, txt_a);
10192 				G->nudge[GMT_Y] = (k == 2 ) ? gmt_M_to_inch (GMT, txt_b) : G->nudge[GMT_X];
10193 				if (k == 0) bad++;
10194 				break;
10195 			case 'o':	/* Use rounded rectangle textbox shape */
10196 				G->box = 4 + (G->box & 1);
10197 				break;
10198 
10199 			case 'p':	/* Draw text box outline [with optional textbox pen specification] */
10200 				if (p[1] && gmt_getpen (GMT, &p[1], &G->pen)) bad++;
10201 				G->box |= 1;
10202 				break;
10203 
10204 			case 'r':	/* Minimum radius of curvature specification */
10205 				G->min_radius = gmt_M_to_inch (GMT, &p[1]);
10206 				break;
10207 
10208 			case 's':	/* Font size specification (for backward compatibility only since size is part of font specification) */
10209 				if (gmt_M_compat_check (GMT, 4)) {
10210 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "+s<fontsize> in contour label spec is obsolete, now part of +f<font>\n");
10211 					G->font_label.size = gmt_convert_units (GMT, &p[1], GMT_PT, GMT_PT);
10212 					if (G->font_label.size <= 0.0) bad++;
10213 				}
10214 				else
10215 					bad++;
10216 				break;
10217 
10218 			case 'T':	/* Deprecated, just give a warning that it is the same as +t and deliberately fall through to case 't' */
10219 				if (gmt_M_compat_check (GMT, 6))
10220 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "+T in contour label spec is deprecated; only +t is supported\n");
10221 				else {
10222 					bad++;
10223 					break;
10224 				}
10225 			case 't':	/* Save contour label locations to given file [x y angle label] */
10226 				G->save_labels = 1;
10227 				if (p[1]) strncpy (G->label_file, &p[1], PATH_MAX-1);
10228 				break;
10229 
10230 			case 'u':	/* Label Unit specification */
10231 				if (p[1]) strncpy (G->unit, &p[1], GMT_LEN64-1);
10232 				break;
10233 
10234 			case 'v':	/* Curved text [Default is straight] */
10235 				G->curved_text = true;
10236 				break;
10237 
10238 			case 'w':	/* Angle filter width [Default is auto] */
10239 				G->half_width = atoi (&p[1]) / 2;
10240 				break;
10241 
10242 			case 'x':	/* Crossection labeling for SqN2 only; add given text to start and end label */
10243 				if (!G->number || G->n_cont != 2) {
10244 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Sq: The +x modifier is only valid with -SqN2\n");
10245 					bad++;
10246 				}
10247 				else {
10248 					G->crossect = true;
10249 					if (p[1])
10250 						sscanf (&p[1], "%[^,],%s", G->crossect_tag[0], G->crossect_tag[1]);
10251 					else {	/* Default is to prime the end annotation only */
10252 						G->crossect_tag[0][0] = '\0';
10253 						strcpy (G->crossect_tag[1], "'");
10254 					}
10255 				}
10256 				break;
10257 
10258 			case '=':	/* Label Prefix specification */
10259 				if (p[1]) strncpy (G->prefix, &p[1], GMT_LEN64-1);
10260 				break;
10261 
10262 			default:
10263 				bad++;
10264 				break;
10265 		}
10266 	}
10267 	if (G->curved_text && G->nudge_flag) {
10268 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot combine +v and +n\n");
10269 		bad++;
10270 	}
10271 	return (bad);
10272 }
10273 
10274 /*! . */
gmt_contlabel_info(struct GMT_CTRL * GMT,char flag,char * txt,struct GMT_CONTOUR * L)10275 int gmt_contlabel_info (struct GMT_CTRL *GMT, char flag, char *txt, struct GMT_CONTOUR *L) {
10276 	/* Interpret the contour-label information string and set structure items */
10277 	int k, j = 0, error = 0;
10278 	char txt_a[GMT_LEN256] = {""}, c, arg, *p = NULL;
10279 
10280 	L->spacing = false;	/* Turn off the default since we gave an option */
10281 	strncpy (L->option, &txt[1], GMT_BUFSIZ-1);	 /* May need to process L->option later after -R,-J have been set */
10282 	if ((p = strstr (txt, "+r"))) {	/* Want to isolate labels by given radius */
10283 		*p = '\0';	/* Temporarily chop off the +r<radius> part */
10284 		L->isolate = true;
10285 		L->label_isolation = gmt_M_to_inch (GMT, &p[2]);
10286 	}
10287 	L->flag = flag;
10288 	/* Special check for quoted lines */
10289 	if (txt[0] == 'S' || txt[0] == 's') {	/* -SqS|n should be treated as -SqN|n once file is segmentized */
10290 		arg = (txt[0] == 'S') ? 'N' : 'n';	/* S -> N and s -> n */
10291 		L->segmentize = true;
10292 	}
10293 	else
10294 		arg = txt[0];
10295 
10296 	switch (arg) {
10297 		case 'L':	/* Quick straight lines for intersections */
10298 			L->do_interpolate = true;
10299 			/* Intentionally fall through - to 'l' */
10300 		case 'l':
10301 			L->crossing = GMT_CONTOUR_XLINE;
10302 			break;
10303 		case 'N':	/* Specify number of labels per segment */
10304 			L->number_placement = 1;	/* Distribution of labels */
10305 			if (txt[1] == '-') L->number_placement = -1, j = 1;	/* Left label if n = 1 */
10306 			if (txt[1] == '+') L->number_placement = +1, j = 1;	/* Right label if n = 1 */
10307 			/* Intentionally fall through - to 'n' */
10308 		case 'n':	/* Specify number of labels per segment */
10309 			L->number = true;
10310 			k = sscanf (&txt[1+j], "%d/%s", &L->n_cont, txt_a);
10311 			if (k == 2) L->min_dist = gmt_M_to_inch (GMT, txt_a);
10312 			if (L->n_cont == 0) {
10313 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Number of labels must exceed zero\n", L->flag);
10314 				error++;
10315 			}
10316 			if (L->min_dist < 0.0) {
10317 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Minimum label separation cannot be negative\n", L->flag);
10318 				error++;
10319 			}
10320 			break;
10321 		case 'f':	/* fixed points file */
10322 			L->fixed = true;
10323 			k = sscanf (&txt[1], "%[^/]/%lf", L->file, &L->slop);
10324 			if (k == 1) L->slop = GMT_CONV8_LIMIT;
10325 			if (gmt_access (GMT, L->file, R_OK)) {	/* Cannot read/find file */
10326 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Cannot find/read fixed point file %s\n", L->flag, L->file);
10327 				error++;
10328 			}
10329 			break;
10330 		case 'X':	/* Crossing complicated curve */
10331 			L->do_interpolate = true;
10332 			/* Intentionally fall through - to 'x' */
10333 		case 'x':	/* Crossing line */
10334 			L->crossing = GMT_CONTOUR_XCURVE;
10335 			strncpy (L->file, &txt[1], PATH_MAX-1);
10336 			if (!gmt_file_is_cache (GMT->parent, L->file) && gmt_access (GMT, L->file, R_OK)) {	/* Cannot read/find file */
10337 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Cannot find/read crossing line file %s\n", L->flag, L->file);
10338 				error++;
10339 			}
10340 			break;
10341 		case 'D':	/* Specify distances in geographic units (km, degrees, etc) */
10342 			L->dist_kind = 1;
10343 			/* Intentionally fall through - to 'd' */
10344 		case 'd':	/* Specify distances in plot units [cip] */
10345 			L->spacing = true;
10346 			k = sscanf (&txt[j], "%[^/]/%lf", txt_a, &L->label_dist_frac);
10347 			if (k == 1) L->label_dist_frac = 0.25;
10348 			if (L->dist_kind == 1) {	/* Distance units other than xy specified */
10349 				k = (int)strlen (txt_a) - 1;
10350 				c = (isdigit ((int)txt_a[k]) || txt_a[k] == '.') ? 0 : txt_a[k];
10351 				L->label_dist_spacing = atof (&txt_a[1]);
10352 				if (gmt_init_distaz (GMT, c, gmt_M_sph_mode (GMT), GMT_CONT_DIST) == GMT_NOT_A_VALID_TYPE) error++;
10353 			}
10354 			else
10355 				L->label_dist_spacing = gmt_M_to_inch (GMT, &txt_a[1]);
10356 			if (L->label_dist_spacing <= 0.0) {
10357 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Spacing between labels must exceed 0.0\n", L->flag);
10358 				error++;
10359 			}
10360 			if (L->label_dist_frac < 0.0 || L->label_dist_frac > 1.0) {
10361 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Initial label distance fraction must be in 0-1 range\n", L->flag);
10362 				error++;
10363 			}
10364 			break;
10365 		default:
10366 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unrecognized modifier %c\n", L->flag, txt[0]);
10367 			error++;
10368 			break;
10369 	}
10370 	if (p && L->isolate) *p = '+';	/* Replace the + from earlier */
10371 
10372 	return (error);
10373 }
10374 
10375 /*! . */
gmtlib_decorate_init(struct GMT_CTRL * GMT,struct GMT_DECORATE * G,unsigned int mode)10376 void gmtlib_decorate_init (struct GMT_CTRL *GMT, struct GMT_DECORATE *G, unsigned int mode) {
10377 	/* Assign default values to structure */
10378 
10379 	gmtsupport_decorate_free (GMT, G);	/* In case we've been here before we must free stuff first */
10380 
10381 	gmt_M_memset (G, 1, struct GMT_DECORATE);	/* Sets all to 0 */
10382 	if (mode == 1) {
10383 		G->line_type = 1;
10384 		strcpy (G->line_name, "Contour");
10385 	}
10386 	else {
10387 		G->line_type = 0;
10388 		strcpy (G->line_name, "Line");
10389 	}
10390 	G->spacing = true;
10391 	G->half_width = UINT_MAX;	/* Auto */
10392 	G->symbol_dist_spacing = 4.0;	/* Inches */
10393 	G->symbol_dist_frac = 0.25;	/* Fraction of above head start for closed lines */
10394 	if (GMT->current.setting.proj_length_unit == GMT_CM) G->symbol_dist_spacing = 10.0 / 2.54;
10395 }
10396 
10397 /*! . */
gmtlib_decorate_specs(struct GMT_CTRL * GMT,char * txt,struct GMT_DECORATE * G)10398 int gmtlib_decorate_specs (struct GMT_CTRL *GMT, char *txt, struct GMT_DECORATE *G) {
10399 	unsigned int k, bad = 0, pos = 0;
10400 	char p[GMT_BUFSIZ] = {""}, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""};
10401 	char *specs = NULL;
10402 
10403 	/* Decode [+a<angle>|n|p[u|d]][+d[<pen>]][+g<fill>][+i][+n|N<dx>[/<dy>]][+p[<pen>]]+s<symbolinfo>[+w<width>] strings */
10404 
10405 	for (k = 0; txt[k] && txt[k] != '+'; k++);	/* Look for +<options> strings */
10406 
10407 	if (!txt[k]) return (0);
10408 
10409 	/* Decode new-style +separated substrings */
10410 
10411 	G->nudge_flag = 0;
10412 	G->fill[0] = G->pen[0] = '\0';	/* Reset each time in case we are parsing args from a segment header */
10413 	specs = &txt[k+1];
10414 	while ((gmt_strtok (specs, "+", &pos, p))) {
10415 		switch (p[0]) {
10416 			case 'a':	/* Angle specification */
10417 				if (p[1] == 'p' || p[1] == 'P')	/* Line-parallel label */
10418 					G->angle_type = 0;
10419 				else if (p[1] == 'n' || p[1] == 'N')	/* Line-normal label */
10420 					G->angle_type = 1;
10421 				else {					/* Label at a fixed angle */
10422 					G->symbol_angle = atof (&p[1]);
10423 					G->angle_type = 2;
10424 					gmt_lon_range_adjust (GMT_IS_M180_TO_P180_RANGE, &G->symbol_angle);	/* Now -180/+180 */
10425 					while (fabs (G->symbol_angle) > 90.0) G->symbol_angle -= copysign (180.0, G->symbol_angle);
10426 				}
10427 				break;
10428 
10429 			case 'd':	/* Debug option - draw helper points or lines */
10430 				G->debug = true;
10431 				if (p[1] && gmt_getpen (GMT, &p[1], &G->debug_pen)) bad++;
10432 				break;
10433 
10434 			case 'g':	/* Symbol Fill specification */
10435 				if (p[1]) strncpy (G->fill, &p[1], GMT_LEN64-1);
10436 				break;
10437 
10438 			case 'i':	/* Invisible main line (do not draw line) */
10439 				G->invisible = true;
10440 				break;
10441 
10442 			case 'n':	/* Nudge specification; dx/dy are increments along local line axes */
10443 				G->nudge_flag = 1;
10444 				/* Intentionally fall through - to 'N' */
10445 			case 'N':	/* Nudge specification; dx/dy are increments along plot axes */
10446 				G->nudge_flag++;
10447 				k = sscanf (&p[1], "%[^/]/%s", txt_a, txt_b);
10448 				G->nudge[GMT_X] = gmt_M_to_inch (GMT, txt_a);
10449 				G->nudge[GMT_Y] = (k == 2 ) ? gmt_M_to_inch (GMT, txt_b) : G->nudge[GMT_X];
10450 				if (k == 0) bad++;
10451 				break;
10452 
10453 			case 'p':	/* Set symbol outline specification */
10454 				if (p[1]) strncpy (G->pen, &p[1], GMT_LEN64-1);
10455 				break;
10456 
10457 			case 's':	/* Symbol to place */
10458 				if (p[1]) {
10459 					if (p[1] == 'k') {	/* Custom symbol - separate file from size */
10460 						char *s = strrchr (p, '/');
10461 						strncpy (G->size, &s[1], GMT_LEN64-1);
10462 						s[0] = '\0';	/* Truncate size */
10463 						strncpy (G->symbol_code, &p[1], GMT_LEN64-1);
10464 						s[0] = '/';	/* Restore size */
10465 					}
10466 					else {	/* Regular symbol */
10467 						strncpy (G->size, &p[2], GMT_LEN64-1);
10468 						G->symbol_code[0] = p[1];
10469 					}
10470 				}
10471 				break;
10472 			case 'w':	/* Angle filter width [Default is auto] */
10473 				G->half_width = atoi (&p[1]) / 2;
10474 				break;
10475 
10476 			case '.':	/* Assume it can be a decimal part without leading 0 */
10477 			case '0':	/* A single annotated contour */
10478 			case '1':
10479 			case '2':
10480 			case '3':
10481 			case '4':
10482 			case '5':
10483 			case '6':
10484 			case '7':
10485 			case '8':
10486 			case '9':
10487 				break;
10488 
10489 			default:
10490 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -S~: Modifier +%s not recognized!\n", p);
10491 				bad++;
10492 				break;
10493 		}
10494 	}
10495 	if (G->symbol_code[0] == '\0') {
10496 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No symbol specified!\n");
10497 		bad++;
10498 	}
10499 	if (G->size[0] == '\0') {
10500 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No symbol size specified!\n");
10501 		bad++;
10502 	}
10503 	if (G->fill[0] == '\0' && G->pen[0] == '\0')	/* Neither fill nor pen - select default outline */
10504 		sprintf (G->pen, "%s", gmt_putpen (GMT, &GMT->current.setting.map_default_pen));
10505 
10506 	return (bad);
10507 }
10508 
10509 /*! . */
gmtlib_decorate_info(struct GMT_CTRL * GMT,char flag,char * txt,struct GMT_DECORATE * L)10510 int gmtlib_decorate_info (struct GMT_CTRL *GMT, char flag, char *txt, struct GMT_DECORATE *L) {
10511 	/* Interpret the contour-label information string and set structure items */
10512 	int k, j = 0, error = 0;
10513 	char txt_a[GMT_LEN256] = {""}, c, arg;
10514 
10515 	L->spacing = false;	/* Turn off the default since we gave an option */
10516 	strncpy (L->option, &txt[1], GMT_BUFSIZ-1);	 /* May need to process L->option later after -R,-J have been set */
10517 	L->flag = flag;
10518 	/* Special check for quoted lines */
10519 	if (txt[0] == 'S' || txt[0] == 's') {	/* -SqS|n should be treated as -SqN|n once file is segmentized */
10520 		arg = (txt[0] == 'S') ? 'N' : 'n';	/* S -> N and s -> n */
10521 		L->segmentize = true;
10522 	}
10523 	else
10524 		arg = txt[0];
10525 
10526 	switch (arg) {
10527 		case 'L':	/* Quick straight lines for intersections */
10528 			L->do_interpolate = true;
10529 			/* Intentionally fall through - to 'l' */
10530 		case 'l':
10531 			L->crossing = GMT_DECORATE_XLINE;
10532 			break;
10533 		case 'N':	/* Specify number of labels per segment */
10534 			L->number_placement = 1;	/* Distribution of labels */
10535 			if (txt[1] == '-') L->number_placement = -1, j = 1;	/* Left symbol if n = 1 */
10536 			if (txt[1] == '+') L->number_placement = +1, j = 1;	/* Right symbol if n = 1 */
10537 			/* Intentionally fall through - to 'n' */
10538 		case 'n':	/* Specify number of labels per segment */
10539 			L->number = true;
10540 			k = sscanf (&txt[1+j], "%d/%s", &L->n_cont, txt_a);
10541 			if (k == 2) L->min_dist = gmt_M_to_inch (GMT, txt_a);
10542 			if (L->n_cont == 0) {
10543 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Number of symbols must exceed zero\n", L->flag);
10544 				error++;
10545 			}
10546 			if (L->min_dist < 0.0) {
10547 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Minimum symbols separation cannot be negative\n", L->flag);
10548 				error++;
10549 			}
10550 			break;
10551 		case 'f':	/* fixed points file */
10552 			L->fixed = true;
10553 			k = sscanf (&txt[1], "%[^/]/%lf", L->file, &L->slop);
10554 			if (k == 1) L->slop = GMT_CONV8_LIMIT;
10555 			if (!gmt_file_is_cache (GMT->parent, L->file) && gmt_access (GMT, L->file, R_OK)) {	/* Cannot read/find file */
10556 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Cannot find/read fixed point file %s\n", L->file);
10557 				error++;
10558 			}
10559 			break;
10560 		case 'X':	/* Crossing complicated curve */
10561 			L->do_interpolate = true;
10562 			/* Intentionally fall through - to 'x'*/
10563 		case 'x':	/* Crossing line */
10564 			L->crossing = GMT_DECORATE_XCURVE;
10565 			strncpy (L->file, &txt[1], PATH_MAX-1);
10566 			if (!gmt_file_is_cache (GMT->parent, L->file) && gmt_access (GMT, L->file, R_OK)) {	/* Cannot read/find file */
10567 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Cannot find/read crossing line file %s\n", L->file);
10568 				error++;
10569 			}
10570 			break;
10571 		case 'D':	/* Specify distances in geographic units (km, degrees, etc) */
10572 			L->dist_kind = 1;
10573 			/* Intentionally fall through - to 'd' */
10574 		case 'd':	/* Specify distances in plot units [cip] */
10575 			L->spacing = true;
10576 			k = sscanf (&txt[j], "%[^/]/%lf", txt_a, &L->symbol_dist_frac);
10577 			if (k == 1) L->symbol_dist_frac = 0.25;
10578 			if (L->dist_kind == 1) {	/* Distance units other than xy specified */
10579 				k = (int)strlen (txt_a) - 1;
10580 				c = (isdigit ((int)txt_a[k]) || txt_a[k] == '.') ? 0 : txt_a[k];
10581 				L->symbol_dist_spacing = atof (&txt_a[1]);
10582 				if (gmt_init_distaz (GMT, c, gmt_M_sph_mode (GMT), GMT_CONT_DIST) == GMT_NOT_A_VALID_TYPE) error++;
10583 			}
10584 			else
10585 				L->symbol_dist_spacing = gmt_M_to_inch (GMT, &txt_a[1]);
10586 			if (L->symbol_dist_spacing <= 0.0) {
10587 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Spacing between symbols must exceed 0.0\n", L->flag);
10588 				error++;
10589 			}
10590 			if (L->symbol_dist_frac < 0.0 || L->symbol_dist_frac > 1.0) {
10591 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Initial symbols distance fraction must be in 0-1 range\n", L->flag);
10592 				error++;
10593 			}
10594 			break;
10595 		default:
10596 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unrecognized modifier %c\n", L->flag, txt[0]);
10597 			error++;
10598 			break;
10599 	}
10600 
10601 	return (error);
10602 }
10603 
gmt_get_dist_units(struct GMT_CTRL * GMT,char * args,char * unit,unsigned int * mode)10604 unsigned int gmt_get_dist_units (struct GMT_CTRL *GMT, char *args, char *unit, unsigned int *mode) {
10605 	/* Examine the -E<args> option and determine the distance unit and mode. */
10606 	unsigned int id, pos = 0, pos2 = 0, error = 0, l_mode[3], this_mode = 0;
10607 	size_t len, k, kk, s;
10608 	char *c = NULL, p[GMT_BUFSIZ] = {""}, modifiers[GMT_BUFSIZ] = {""}, p2[GMT_BUFSIZ] = {""}, this_unit = 0, l_unit[3];
10609 
10610 	/* step is given in either Cartesian units or, for geographic, in the prevailing unit (m, km) */
10611 
10612 	*mode = (gmt_M_is_geographic (GMT, GMT_IN)) ? GMT_GREATCIRCLE : 0;	/* Great circle or Cartesian */
10613 	*unit = 0;	/* Initially not set */
10614 	while (!error && (gmt_strtok (args, ",", &pos, p))) {	/* Split on each line since separated by commas */
10615 		k = s = 0;	len = strlen (p);
10616 		while (s == 0 && k < len) {	/* Find first occurrence of recognized modifier+<char> that may take a unit, if any */
10617 			if ((p[k] == '+') && (p[k+1] && strchr ("ilr", p[k+1]))) s = k;
10618 			k++;
10619 		}
10620 		if (s == 0) continue;	/* No modifier with unit specification; go to next line */
10621 		/* Here we are processing +i, +l, or +r, all of which take a distance with optional unit */
10622 		gmt_M_memset (l_unit, 3, char);		/* Clean register */
10623 		gmt_M_memset (l_mode, 3, unsigned int);	/* Clean register */
10624 		strcpy (modifiers, &p[s]);
10625 		pos2 = 0;
10626 		if (modifiers[2] == '+') {	/* Gave leading + for geodesic calculation (deprecated) */
10627 			if (gmt_M_compat_check (GMT, 6))
10628 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Leading + with increment to set ellipsoidal mode is deprecated; use -je instead\n");
10629 			modifiers[2] = '@';	/* Flag for + in increment which means geodesic mode [to avoid being screwed by gmt_strtok on +] */
10630 		}
10631 		while ((gmt_strtok (modifiers, "+", &pos2, p2))) {
10632 			switch (p2[0]) {
10633 				case 'i':	id = 0;	break;	/* Increment along line */
10634 				case 'l':	id = 1;	break;	/* Length of line */
10635 				case 'r':	id = 2;	break;	/* Radius of circular ring */
10636 				default:	id = 9; break;	/* Probably +d or some other non-distance setting to skip */
10637 			}
10638 			if (id == 9) continue;	/* Just go to next */
10639 			/* id points to the correct array index for i, l, r (0-2) */
10640 			if (strchr (GMT_LEN_UNITS, p2[strlen(p2)-1])) l_unit[id] = p2[strlen(p2)-1];
10641 			if (p2[1] == '-') {
10642 				if (gmt_M_compat_check (GMT, 6)) {
10643 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Leading - to increment to set Flat Earth mode is deprecated; use -jf instead\n");
10644 					l_mode[id] = GMT_FLATEARTH;
10645 				}
10646 				else {
10647 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Negative increment is not allowed\n");
10648 					error++;
10649 				}
10650 			}
10651 			else if (p2[1] == '@') {	/* Leading + in strict GMT6 mode is just a positive sign */
10652 				if (gmt_M_compat_check (GMT, 6))
10653 					l_mode[id] = GMT_GEODESIC;
10654 			}
10655 		}
10656 		/* Some sanity checking to make sure only one unit and mode are given for all lines */
10657 		for (k = 0; k < 3; k++) {
10658 			if (l_unit[k] == 0) continue;	/* Not set, skip to next */
10659 			for (kk = k + 1; kk < 3; kk++) {
10660 				if (l_unit[kk] == 0) continue;	/* Not set, skip to next */
10661 				if (l_unit[k] != l_unit[kk]) error++;
10662 			}
10663 			this_unit = l_unit[k];
10664 		}
10665 		if (this_unit) {	/* Got a unit */
10666 			if (*unit && this_unit != *unit)	/* Got a different unit that before */
10667 				error++;
10668 			else
10669 				*unit = this_unit;	/* Set default unit if not specified as part of the modifiers */
10670 		}
10671 		/* Now check modes */
10672 		for (k = 0; k < 3; k++) {
10673 			if (l_mode[k] == 0) continue;	/* Not set, skip to next */
10674 			for (kk = k + 1; kk < 3; kk++) {
10675 				if (l_mode[kk] == 0) continue;	/* Not set, skip to next */
10676 				if (l_mode[k] != l_mode[kk]) error++;
10677 			}
10678 			this_mode = l_mode[k];
10679 		}
10680 		if (this_mode)	/* Got a mode other than Cartesian */
10681 			*mode = this_mode;
10682 	}
10683 	if (*unit == 0) {
10684 		if (gmt_M_is_geographic (GMT, GMT_IN))	/* km for geographic unless +g given */
10685 			*unit = (strstr (args, "+g")) ? 'd' : 'k';
10686 		else
10687 			*unit = 'X';	/* Default is Cartesian if nothing is specified */
10688 	}
10689 	if (strchr (GMT_LEN_UNITS, *unit) && gmt_M_is_cartesian (GMT, GMT_IN)) {	/* Want geographic distance unit but -fg or -J not set */
10690 		gmt_parse_common_options (GMT, "f", 'f', "g");
10691 		if (*mode == 0) *mode = GMT_GREATCIRCLE;	/* Default to great circle distances if no other mode was implied */
10692 	}
10693 	if (error) GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -E:  All lines must have the same distance units\n");
10694 	/* Process args so any i|l|r+<dist> becomes i|l|r <dist> as the + will cause trouble otherwise.  This + for geodesics have already been processed */
10695 	while ((c = strstr (args, "+i+"))) c[2] = ' ';
10696 	while ((c = strstr (args, "+l+"))) c[2] = ' ';
10697 	while ((c = strstr (args, "+r+"))) c[2] = ' ';
10698 	return (error);
10699 }
10700 
10701 
10702 /*! . */
gmt_make_profiles(struct GMT_CTRL * GMT,char option,char * args,bool resample,bool project,bool get_distances,double step,enum GMT_enum_track mode,double xyz[2][3],unsigned int * dtype)10703 struct GMT_DATASET *gmt_make_profiles (struct GMT_CTRL *GMT, char option, char *args, bool resample, bool project, bool get_distances, double step, enum GMT_enum_track mode, double xyz[2][3], unsigned int *dtype) {
10704 	/* Given a list of comma-separated start/stop coordinates, build a data table
10705  	 * of the profiles. xyz holds the grid min/max coordinates (or NULL if used without a grid;
10706 	 * in that case the special Z+, Z- coordinate shorthands are unavailable).
10707 	 * If resample is true then we sample the track between the end points.
10708 	 * If project is true then we convert to plot units.
10709 	 * If get_distances is true then add a column with distances. We also do this if +d is added to args.
10710 	 */
10711 	unsigned int n_cols, dcol, np = 0, k, s, pos = 0, pos2 = 0, xtype = gmt_M_type (GMT, GMT_IN, GMT_X), ytype = gmt_M_type (GMT, GMT_IN, GMT_Y);
10712 	enum GMT_profmode p_mode;
10713 	bool continuous = false, single, may_adjust = false, gridline_units = false, parallel = false;
10714 	uint64_t dim[GMT_DIM_SIZE] = {1, 1, 0, 0};
10715 	int n, error = 0;
10716 	double L, az = 0.0, length = 0.0, r = 0.0, orig_step = step, last_x = 0, last_y = 0, d_adjust_scl = 1.0;
10717 	size_t len;
10718 	char p[GMT_BUFSIZ] = {""}, txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, txt_d[GMT_LEN256] = {""};
10719 	char modifiers[GMT_BUFSIZ] = {""}, p2[GMT_BUFSIZ] = {""};
10720 	struct GMT_DATASET *D = NULL;
10721 	struct GMT_DATATABLE *T = NULL;
10722 	struct GMT_DATATABLE_HIDDEN *TH = NULL;
10723 	struct GMT_DATASEGMENT *S = NULL;
10724 	struct GMT_DATASEGMENT_HIDDEN *SH = NULL;
10725 
10726 	/* step is given in either Cartesian units or, for geographic, in the prevailing unit (m, km) */
10727 
10728 	if (strstr (args, "+c")) continuous = true;	/* Want to add distances to the output */
10729 	if (strstr (args, "+d")) get_distances = true;	/* Want to join abutting profiles */
10730 	if (strstr (args, "+g")) gridline_units = true;	/* Want degree longitudes or latitudes along a gridline */
10731 	if (strstr (args, "+p")) parallel = true;	/* Want to sample along a parallel */
10732 	if (strstr (args, "+x")) GMT->current.map.loxodrome = true;	/* Want to sample along a rhumbline */
10733 	single = (strchr (args, ',') == NULL);	/* Only a single line */
10734 	if (get_distances) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_make_profiles: Return distances along track\n");
10735 
10736 	if (gmt_M_is_geographic (GMT, GMT_IN)) {
10737 		if (single &&gridline_units)
10738 			may_adjust = true;
10739 		if (parallel) mode = GMT_TRACK_FILL_P;
10740 	}
10741 	n_cols = (get_distances) ? 3 :2;
10742 	dim[GMT_COL] = n_cols;
10743 	dim[GMT_SEG] = GMT_SMALL_CHUNK;
10744 	*dtype = GMT_IS_FLOAT;	/* Normally, distances are floatint point values */
10745 	if ((D = GMT_Create_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_LINE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL)
10746 		return (NULL);
10747 
10748 	T = D->table[0];	/* The only table */
10749 	TH = gmt_get_DT_hidden (T);
10750 	T->n_segments = 0;    /* Start working on first segment */
10751 
10752 	while (gmt_strtok (args, ",", &pos, p)) {	/* Split on each line since separated by commas */
10753 		S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, 2, n_cols, NULL, NULL);	/* n_cols with 2 rows each */
10754 		SH = gmt_get_DS_hidden (S);
10755 		k = p_mode = s = 0;	len = strlen (p);
10756 		while (s == 0 && k < len) {	/* Find first occurrence of recognized modifier+<char>, if any */
10757 			if ((p[k] == '+') && (p[k+1] && strchr ("acdgilnoprx", p[k+1]))) s = k;
10758 			k++;
10759 		}
10760 		if (s) {
10761 			strncpy (modifiers, &p[s], GMT_BUFSIZ-1);
10762 			pos2 = 0;
10763 			while ((gmt_strtok (modifiers, "+", &pos2, p2))) {
10764 				switch (p2[0]) {	/* fabs is used for lengths since -<length> might have been given to indicate Flat Earth Distances */
10765 					case 'a':	az = atof (&p2[1]);	p_mode |= GMT_GOT_AZIM;		break;
10766 					case 'c':	case 'd':	case 'g':	case 'p':	case 'x':	break;	/* Already processed up front */
10767 					case 'n':	np = atoi (&p2[1]);	p_mode |= GMT_GOT_NP;		break;
10768 					case 'o':	az = atof (&p2[1]);	p_mode |= GMT_GOT_ORIENT;	break;
10769 					case 'i':	step = fabs (atof (&p2[1]));
10770 							if (step > 2.0*orig_step)
10771 								GMT_Report (GMT->parent, GMT_MSG_WARNING, "Output sampling interval in d exceeds grid interval and may lead to aliasing.\n");
10772 							p_mode |= GMT_GOT_INC;					break;
10773 					case 'l':	length = fabs (atof (&p2[1]));	p_mode |= GMT_GOT_LENGTH;	break;
10774 					case 'r':	r = fabs (atof (&p2[1]));	p_mode |= GMT_GOT_RADIUS;	break;
10775 					default:	error++;	break;
10776 				}
10777 			}
10778 			/* Some sanity checking to make correct combos of modifiers are given */
10779 			if (((p_mode & GMT_GOT_AZIM) || (p_mode & GMT_GOT_ORIENT)) && (p_mode & GMT_GOT_LENGTH) == 0) {
10780 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Modifiers +a and +o requires +l<length>\n", option);
10781 				error++;
10782 			}
10783 			if ((p_mode & GMT_GOT_RADIUS) && !((p_mode & GMT_GOT_NP) || (p_mode & GMT_GOT_INC))) {
10784 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Modifies +r requires +i<inc> or +n<np>\n", option);
10785 				error++;
10786 			}
10787 			p[s] = '\0';	/* Chop off for now */
10788 			if (error) {
10789 				gmt_M_free (GMT, T->segment);
10790 				gmt_M_free (GMT, T);
10791 				return (NULL);
10792 			}
10793 		}
10794 		n = sscanf (p, "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d);
10795 		if (n == 1) { /* Easy, got <code> for a central point */
10796 			error += gmtsupport_code_to_lonlat (GMT, txt_a, &S->data[GMT_X][0], &S->data[GMT_Y][0]);
10797 		}
10798 		else if (n == 4) {	/* Easy, got lon0/lat0/lon1/lat1 */
10799 			error += gmt_verify_expectations (GMT, xtype, gmt_scanf_arg (GMT, txt_a, xtype, false, &S->data[GMT_X][0]), txt_a);
10800 			error += gmt_verify_expectations (GMT, ytype, gmt_scanf_arg (GMT, txt_b, ytype, false, &S->data[GMT_Y][0]), txt_b);
10801 			error += gmt_verify_expectations (GMT, xtype, gmt_scanf_arg (GMT, txt_c, xtype, false, &S->data[GMT_X][1]), txt_c);
10802 			error += gmt_verify_expectations (GMT, ytype, gmt_scanf_arg (GMT, txt_d, ytype, false, &S->data[GMT_Y][1]), txt_d);
10803 			if (may_adjust && gridline_units) {	/* Want to use arc-units along gridlines and not great circle units */
10804 				if (doubleAlmostEqualZero (S->data[GMT_X][0], S->data[GMT_X][1]))
10805 					d_adjust_scl = 1.0, dcol = GMT_Y, mode = GMT_TRACK_FILL_M;	/* Scaling spherical degrees to latitude degrees on a sphere */
10806 				else if (doubleAlmostEqualZero (S->data[GMT_Y][0], S->data[GMT_Y][1]))
10807 					d_adjust_scl = cosd (S->data[GMT_Y][0]), dcol = GMT_X, mode = GMT_TRACK_FILL_P;	/* Scaling spherical degrees to longitude degrees on a sphere at that latitude */
10808 				else
10809 					may_adjust = false;	/* Not our case */
10810 			}
10811 		}
10812 		else if (n == 2) {	/* More complicated: either <code>/<code> or <clon>/<clat> with +a|o|r */
10813 			if ((p_mode & GMT_GOT_AZIM) || (p_mode & GMT_GOT_ORIENT) || (p_mode & GMT_GOT_RADIUS)) {	/* Got a center point via coordinates */
10814 				error += gmt_verify_expectations (GMT, xtype, gmt_scanf_arg (GMT, txt_a, xtype, false, &S->data[GMT_X][0]), txt_a);
10815 				error += gmt_verify_expectations (GMT, ytype, gmt_scanf_arg (GMT, txt_b, ytype, false, &S->data[GMT_Y][0]), txt_b);
10816 			}
10817 			else { /* Easy, got <code>/<code> */
10818 				error += gmtsupport_code_to_lonlat (GMT, txt_a, &S->data[GMT_X][0], &S->data[GMT_Y][0]);
10819 				error += gmtsupport_code_to_lonlat (GMT, txt_b, &S->data[GMT_X][1], &S->data[GMT_Y][1]);
10820 			}
10821 		}
10822 		else if (n == 3) {	/* More complicated: <code>/<lon>/<lat> or <lon>/<lat>/<code> */
10823 			if (gmtsupport_code_to_lonlat (GMT, txt_a, &S->data[GMT_X][0], &S->data[GMT_Y][0])) {	/* Failed, so try the other way */
10824 				error += gmt_verify_expectations (GMT, xtype, gmt_scanf_arg (GMT, txt_a, xtype, false, &S->data[GMT_X][0]), txt_a);
10825 				error += gmt_verify_expectations (GMT, ytype, gmt_scanf_arg (GMT, txt_b, ytype, false, &S->data[GMT_Y][0]), txt_b);
10826 				error += gmtsupport_code_to_lonlat (GMT, txt_c, &S->data[GMT_X][1], &S->data[GMT_Y][1]);
10827 			}
10828 			else {	/* Worked, pick up second point */
10829 				error += gmt_verify_expectations (GMT, xtype, gmt_scanf_arg (GMT, txt_b, xtype, false, &S->data[GMT_X][1]), txt_b);
10830 				error += gmt_verify_expectations (GMT, ytype, gmt_scanf_arg (GMT, txt_c, ytype, false, &S->data[GMT_Y][1]), txt_c);
10831 			}
10832 		}
10833 		S->n_rows = 2;
10834 		for (n = 0; n < 2; n++) {	/* Reset any zmin/max settings if used and applicable */
10835 			if (S->data[GMT_X][n] == DBL_MAX) {	/* Meant zmax location */
10836 				if (xyz) {
10837 					S->data[GMT_X][n] = xyz[1][GMT_X];
10838 					S->data[GMT_Y][n] = xyz[1][GMT_Y];
10839 				}
10840 				else {
10841 					error++;
10842 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  z+ option not applicable here\n", option);
10843 				}
10844 			}
10845 			else if (S->data[GMT_X][n] == -DBL_MAX) {	/* Meant zmin location */
10846 				if (xyz) {
10847 					S->data[GMT_X][n] = xyz[0][GMT_X];
10848 					S->data[GMT_Y][n] = xyz[0][GMT_Y];
10849 				}
10850 				else {
10851 					error++;
10852 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  z- option not applicable here\n", option);
10853 				}
10854 			}
10855 		}
10856 		if (error) {
10857 			gmt_M_free (GMT, T->segment);
10858 			gmt_M_free (GMT, T);
10859 			gmt_free_segment (GMT, &S);
10860 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Valid coordinate codes are [lcr][bmt] and z[+-]\n", option);
10861 			return (NULL);
10862 		}
10863 		if (p_mode & GMT_GOT_AZIM) {		/* Got center and azimuth of line; determine a suitable end point */
10864 			L = gmtsupport_determine_endpoint (GMT, S->data[GMT_X][0], S->data[GMT_Y][0], length, az, &S->data[GMT_X][1], &S->data[GMT_Y][1]);
10865 			if (p_mode & GMT_GOT_NP) step = L / (np - 1);
10866 		}
10867 		else if (p_mode & GMT_GOT_ORIENT) {	/* Got center and orientation of line; determine suitable end points */
10868 			L = gmtsupport_determine_endpoints (GMT, S->data[GMT_X], S->data[GMT_Y], length, az);
10869 			if (p_mode & GMT_GOT_NP) step = L / (np - 1);
10870 		}
10871 		else if (p_mode & GMT_GOT_RADIUS) {	/* Got center and a radius; determine circular path */
10872 			double x0, y0;
10873 			/* Determine np from the +i<inc> if +n was not set */
10874 			x0 = S->data[GMT_X][0];	y0 = S->data[GMT_Y][0];
10875 			if (p_mode & GMT_GOT_INC) {
10876 				double colat = (r / GMT->current.map.dist[GMT_MAP_DIST].scale);	/* Convert from chosen radius unit to meter or degree */
10877 				if (!GMT->current.map.dist[GMT_MAP_DIST].arc) colat /= GMT->current.proj.DIST_M_PR_DEG;	/* Convert meter to spherical degrees */
10878 				/* colat is the radius in spherical degrees (if geographic) */
10879 				step = (step / GMT->current.map.dist[GMT_MAP_DIST].scale);	/* Convert from chosen unit to meter or degree */
10880 				if (!GMT->current.map.dist[GMT_MAP_DIST].arc) step /= GMT->current.proj.DIST_M_PR_DEG;	/* Convert meter to spherical degrees */
10881 				/* If geographic then the length of the circle is 360 degeres of longitude scaled by the sine of the colatitude */
10882 				L = (gmt_M_is_geographic (GMT, GMT_IN)) ? sind (colat) * 360.0 : 2.0 * M_PI * r;
10883 				np = urint (L / step);
10884 			}
10885 			S->data[GMT_X] = gmt_M_memory (GMT, S->data[GMT_X], np, double);
10886 			S->data[GMT_Y] = gmt_M_memory (GMT, S->data[GMT_Y], np, double);
10887 			S->n_rows = gmtsupport_determine_circle (GMT, x0, y0, r, S->data[GMT_X], S->data[GMT_Y], np);
10888 			resample = false;	/* Since we already got our profile */
10889 		}
10890 		if (resample) S->n_rows = SH->n_alloc = gmt_resample_path (GMT, &S->data[GMT_X], &S->data[GMT_Y], S->n_rows, d_adjust_scl * step, mode);
10891 		if (get_distances) {	/* Compute cumulative distances along line */
10892 			gmt_M_free (GMT, S->data[GMT_Z]);	/* Free so we can alloc a new array */
10893 			S->data[GMT_Z] = gmt_dist_array (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows, true);
10894 			if (p_mode & GMT_GOT_ORIENT) {	/* Adjust distances to have 0 at specified origin */
10895 				L = 0.5 * S->data[GMT_Z][S->n_rows-1];	/* Half-way distance to remove */
10896 				for (k = 0; k < S->n_rows; k++) S->data[GMT_Z][k] -= L;
10897 			}
10898 			if (may_adjust) {	/* Adjust distances to equal longitudes or latitudes */
10899 				gmt_M_memcpy (S->data[GMT_Z], S->data[dcol], S->n_rows, double);
10900 				*dtype = (dcol == GMT_X) ? GMT_IS_LON : GMT_IS_LAT;
10901 			}
10902 		}
10903 		if (project) {	/* Project coordinates */
10904 			uint64_t k;
10905 			double x, y;
10906 			for (k = 0; k < S->n_rows; k++) {
10907 				gmt_geo_to_xy (GMT, S->data[GMT_X][k], S->data[GMT_Y][k], &x, &y);
10908 				S->data[GMT_X][k] = x;
10909 				S->data[GMT_Y][k] = y;
10910 			}
10911 		}
10912 		if (continuous && T->n_segments && doubleAlmostEqual (S->data[GMT_Y][0], last_y) && gmtsupport_same_longitude (S->data[GMT_X][0], last_x)) {
10913 			/* Need to append to previous segment after allocating more space */
10914 			struct GMT_DATASEGMENT *prev_S = T->segment[T->n_segments-1];
10915 			uint64_t start = prev_S->n_rows, add = S->n_rows - 1, rec;
10916 			if (gmt_alloc_segment (GMT, prev_S, prev_S->n_rows + S->n_rows - 1, prev_S->n_columns, GMT_NO_STRINGS, false)) {
10917 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmt_make_profiles: Cannot reallocate the existing segment");
10918 				T->segment[T->n_segments++] = S;	/* Hook into table */
10919 			}
10920 			else {	/* Copy over but avoid repeating the joint */
10921 				gmt_M_memcpy (&(prev_S->data[GMT_X][start]), &(S->data[GMT_X][1]), add, double);
10922 				gmt_M_memcpy (&(prev_S->data[GMT_Y][start]), &(S->data[GMT_Y][1]), add, double);
10923 				gmt_M_memcpy (&(prev_S->data[GMT_Z][start]), &(S->data[GMT_Z][1]), add, double);
10924 				for (rec = start; rec < prev_S->n_rows; rec++) prev_S->data[GMT_Z][rec] += prev_S->data[GMT_Z][start-1];
10925 				gmt_free_segment (GMT, &S);	/* Done with this guy */
10926 				S = prev_S;
10927 			}
10928 		}
10929 		else {	/* Not continuous, or first segment */
10930 			gmt_free_segment (GMT, &T->segment[T->n_segments]);	/* Done with this guy */
10931 			if (continuous) gmt_eliminate_lon_jumps (GMT, S->data[GMT_X], S->n_rows);	/* Avoid jumps in longitude due to joining */
10932 			T->segment[T->n_segments++] = S;	/* Hook into table */
10933 		}
10934 
10935 		last_x = S->data[GMT_X][S->n_rows-1];
10936 		last_y = S->data[GMT_Y][S->n_rows-1];
10937 
10938 		if (T->n_segments == TH->n_alloc) {	/* Allocate more space */
10939 			size_t old_n_alloc = TH->n_alloc;
10940 			TH->n_alloc <<= 1;
10941 			T->segment = gmt_M_memory (GMT, T->segment, TH->n_alloc, struct GMT_DATASEGMENT *);
10942 			gmt_M_memset (&(T->segment[old_n_alloc]), TH->n_alloc - old_n_alloc, struct GMT_DATASEGMENT *);	/* Set to NULL */
10943 		}
10944 	}
10945 	gmtlib_finalize_dataset (GMT, D);	/* Reallocate to fit */
10946 	gmt_set_dataset_minmax (GMT, D);	/* Determine min/max for each column */
10947 	return (D);
10948 }
10949 
10950 /*! . */
gmt_decorate_prep(struct GMT_CTRL * GMT,struct GMT_DECORATE * G,double xyz[2][3])10951 int gmt_decorate_prep (struct GMT_CTRL *GMT, struct GMT_DECORATE *G, double xyz[2][3]) {
10952 	/* G is pointer to the LABELED CONTOUR structure
10953 	 * xyz, if not NULL, have the (x,y,z) min and max values for a grid
10954 	 */
10955 
10956 	/* Prepares decorated line symbols machinery as needed */
10957 
10958 	unsigned int error = 0, dummy;
10959 	uint64_t k, i, seg, row, rec;
10960 	double x, y;
10961 
10962 	if (G->spacing && G->dist_kind == 1) {
10963 		GMT->current.map.dist[GMT_LABEL_DIST].func = GMT->current.map.dist[GMT_CONT_DIST].func;
10964 		GMT->current.map.dist[GMT_LABEL_DIST].scale = GMT->current.map.dist[GMT_CONT_DIST].scale;
10965 	}
10966 	if (G->dist_kind == 1 && gmt_M_is_cartesian (GMT, GMT_IN)) {
10967 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Map distance options requires a map projection.\n", G->flag);
10968 		error++;
10969 	}
10970 
10971 	if (G->crossing == GMT_DECORATE_XLINE) {
10972 		G->X = gmt_make_profiles (GMT, G->flag, G->option, G->do_interpolate, true, false, 0.0, GMT_TRACK_FILL, xyz, &dummy);
10973 	}
10974 	else if (G->crossing == GMT_DECORATE_XCURVE) {
10975 		unsigned int first = 0;
10976 		if (gmt_file_is_cache (GMT->parent, G->file)) {	/* Must be a cache file */
10977 			first = gmt_download_file_if_not_found (GMT, G->file, 0);
10978 		}
10979 		gmt_disable_bghio_opts (GMT);	/* Do not want any -b -g -h -i -o to affect the reading this file */
10980 		if ((G->X = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_LINE, GMT_READ_NORMAL, NULL, &G->file[first], NULL)) == NULL) {	/* Failure to read the file */
10981 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Crossing file %s does not exist or had no data records\n", G->flag, G->file);
10982 			error++;
10983 		}
10984 		else if (G->X->n_columns < 2 || G->X->n_records < 2) {
10985 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Crossing file %s does not have enough columns or records\n", G->flag, G->file);
10986 			GMT_Destroy_Data (GMT, &(G->X));
10987 			error++;
10988 		}
10989 		else {	/* Should be OK to use; note there is only one table here */
10990 			struct GMT_DATASEGMENT *S = NULL;
10991 			for (k = 0; k < G->X->table[0]->n_segments; k++) {
10992 				S = G->X->table[0]->segment[k];
10993 				for (i = 0; i < S->n_rows; i++) {	/* Project */
10994 					gmt_geo_to_xy (GMT, S->data[GMT_X][i], S->data[GMT_Y][i], &x, &y);
10995 					S->data[GMT_X][i] = x;
10996 					S->data[GMT_Y][i] = y;
10997 				}
10998 			}
10999 		}
11000 		gmt_reenable_bghio_opts (GMT);	/* Recover settings provided by user (if -b -g -h -i were used at all) */
11001 	}
11002 	else if (G->fixed) {
11003 		unsigned int first = 0;
11004 		struct GMT_DATASET *T = NULL;
11005 		struct GMT_DATASEGMENT *S = NULL;
11006 		double xy[2];
11007 		/* File is expected to have x,y coordinates and we will ignore anything beyond that */
11008 		if ((error = GMT_Set_Columns (GMT->parent, GMT_IN, 2, GMT_COL_FIX_NO_TEXT))) {
11009 			return (error);
11010 		}
11011 		if (gmt_file_is_cache (GMT->parent, G->file)) {	/* Must be a cache file */
11012 			first = gmt_download_file_if_not_found (GMT, G->file, 0);
11013 		}
11014 		gmt_disable_bghio_opts (GMT);	/* Do not want any -b -g -h -i -o to affect the reading from -F files */
11015 		if ((T = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_READ_NORMAL, NULL, &G->file[first], NULL)) == NULL) {
11016 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Could not open file %s\n", G->flag, G->file);
11017 			error++;
11018 			return (error);
11019 		}
11020 		gmt_reenable_bghio_opts (GMT);	/* Recover settings provided by user (if -b -g -h -i were used at all) */
11021 		if (T->n_columns < 2) {
11022 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Data file %s has only %" PRIu64 " data columns!\n", G->file, T->n_columns);
11023 			return (error);
11024 		}
11025 		G->f_xy[GMT_X] = gmt_M_memory (GMT, NULL, T->n_records, double);
11026 		G->f_xy[GMT_Y] = gmt_M_memory (GMT, NULL, T->n_records, double);
11027 		for (seg = rec = k = 0; seg < T->table[0]->n_segments; seg++) {
11028 			S = T->table[0]->segment[seg];	/* Current segment */
11029 			for (row = 0; row < S->n_rows; row++, rec++) {
11030 				xy[GMT_X] = S->data[GMT_X][row];
11031 				xy[GMT_Y] = S->data[GMT_Y][row];
11032 				gmt_map_outside (GMT, xy[GMT_X], xy[GMT_Y]);
11033 				if (abs (GMT->current.map.this_x_status) > 1 || abs (GMT->current.map.this_y_status) > 1) continue;	/* Outside map region */
11034 				gmt_geo_to_xy (GMT, xy[GMT_X], xy[GMT_Y], &G->f_xy[GMT_X][k], &G->f_xy[GMT_Y][k]);	/* Project -> xy inches */
11035 				k++;
11036 			}
11037 		}
11038 		G->f_n = (unsigned int)k;
11039 		if ((G->f_n = (unsigned int)k) == 0) {
11040 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Fixed position file %s does not have any data records\n",
11041 				G->flag, G->file);
11042 			error++;
11043 			gmt_M_free (GMT, G->f_xy[GMT_X]);
11044 			gmt_M_free (GMT, G->f_xy[GMT_Y]);
11045 		}
11046 		if (GMT_Destroy_Data (GMT->parent, &T) != GMT_NOERROR)
11047 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -%c:  Failed to free DATASET allocated to parse %s\n",
11048 				G->flag, G->file);
11049 	}
11050 
11051 	return (error);
11052 }
11053 
11054 /*! . */
gmt_contlabel_prep(struct GMT_CTRL * GMT,struct GMT_CONTOUR * G,double xyz[2][3])11055 int gmt_contlabel_prep (struct GMT_CTRL *GMT, struct GMT_CONTOUR *G, double xyz[2][3]) {
11056 	/* G is pointer to the LABELED CONTOUR structure
11057 	 * xyz, if not NULL, have the (x,y,z) min and max values for a grid
11058 	 */
11059 
11060 	/* Prepares contour labeling machinery as needed */
11061 
11062 	unsigned int error = 0, dummy;
11063 	uint64_t k, i, seg, row, rec;
11064 	double x, y;
11065 
11066 	gmt_contlabel_free (GMT, G);	/* In case we've been here before */
11067 
11068 	if (G->clearance_flag) {	/* Gave a percentage of fontsize as clearance */
11069 		G->clearance[GMT_X] = 0.01 * G->clearance[GMT_X] * G->font_label.size * GMT->session.u2u[GMT_PT][GMT_INCH];
11070 		G->clearance[GMT_Y] = 0.01 * G->clearance[GMT_Y] * G->font_label.size * GMT->session.u2u[GMT_PT][GMT_INCH];
11071 	}
11072 	if (G->label_type == GMT_LABEL_IS_FFILE && !G->fixed) {	/* Requires fixed file */
11073 		error++;
11074 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Labeling option +Lf requires the fixed label location setting\n", G->flag);
11075 	}
11076 	if (G->label_type == GMT_LABEL_IS_XFILE && G->crossing != GMT_CONTOUR_XCURVE) {	/* Requires cross file */
11077 		error++;
11078 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Labeling option +Lx requires the crossing lines setting\n", G->flag);
11079 	}
11080 	if (G->spacing && G->dist_kind == 1 && G->label_type == GMT_LABEL_IS_MDIST && G->dist_unit == 0) {	/* Did not specify unit - use same as in -G */
11081 		GMT->current.map.dist[GMT_LABEL_DIST].func = GMT->current.map.dist[GMT_CONT_DIST].func;
11082 		GMT->current.map.dist[GMT_LABEL_DIST].scale = GMT->current.map.dist[GMT_CONT_DIST].scale;
11083 	}
11084 	if ((G->dist_kind == 1 || G->label_type == GMT_LABEL_IS_MDIST) && gmt_M_is_cartesian (GMT, GMT_IN)) {
11085 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Map distance options requires a map projection.\n", G->flag);
11086 		error++;
11087 	}
11088 	if (G->angle_type == 0)
11089 		G->no_gap = (G->just < 5 || G->just > 7);	/* Don't clip contour if label is not in the way */
11090 	else if (G->angle_type == 1)
11091 		G->no_gap = ((G->just + 2)%4 != 0);	/* Don't clip contour if label is not in the way */
11092 
11093 	if (G->crossing == GMT_CONTOUR_XLINE) {
11094 		G->X = gmt_make_profiles (GMT, G->flag, G->option, G->do_interpolate, true, false, 0.0, GMT_TRACK_FILL, xyz, &dummy);
11095 	}
11096 	else if (G->crossing == GMT_CONTOUR_XCURVE) {
11097 		unsigned int first = 0;
11098 		if (gmt_file_is_cache (GMT->parent, G->file)) {	/* Must be a cache file */
11099 			first = gmt_download_file_if_not_found (GMT, G->file, 0);
11100 		}
11101 		if ((G->X = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_LINE, GMT_READ_NORMAL, NULL, &G->file[first], NULL)) == NULL) {	/* Failure to read the file */
11102 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Crossing file %s does not exist or had no data records\n", G->flag, G->file);
11103 			error++;
11104 		}
11105 		else if (G->X->n_columns < 2 || G->X->n_records < 2) {
11106 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Crossing file %s does not have enough columns or records\n", G->flag, G->file);
11107 			GMT_Destroy_Data (GMT, &(G->X));
11108 			error++;
11109 		}
11110 		else {	/* Should be OK to use; note there is only one table here */
11111 			struct GMT_DATASEGMENT *S = NULL;
11112 			for (k = 0; k < G->X->table[0]->n_segments; k++) {
11113 				S = G->X->table[0]->segment[k];
11114 				for (i = 0; i < S->n_rows; i++) {	/* Project */
11115 					gmt_geo_to_xy (GMT, S->data[GMT_X][i], S->data[GMT_Y][i], &x, &y);
11116 					S->data[GMT_X][i] = x;
11117 					S->data[GMT_Y][i] = y;
11118 				}
11119 			}
11120 		}
11121 	}
11122 	else if (G->fixed) {
11123 		unsigned int first = 0;
11124 		struct GMT_DATASET *T = NULL;
11125 		struct GMT_DATASEGMENT *S = NULL;
11126 		double xy[2];
11127 		/* Reading this way since file has coordinates and possibly a text label.
11128 		 * If G->label_type == GMT_LABEL_IS_FFILE then we also have text labels.
11129 		 * Since those could be numbers we force reading 2 columns and get anything
11130 		 * beyond that as text. */
11131 		if ((error = GMT_Set_Columns (GMT->parent, GMT_IN, 2, GMT_COL_FIX))) {
11132 			return (error);
11133 		}
11134 		if (gmt_file_is_cache (GMT->parent, G->file)) {	/* Must be a cache file */
11135 			first = gmt_download_file_if_not_found (GMT, G->file, 0);
11136 		}
11137 		if ((T = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_READ_NORMAL, NULL, &G->file[first], NULL)) == NULL) {
11138 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Could not open file %s\n", G->flag, G->file);
11139 			error++;
11140 			return (error);
11141 		}
11142 		if (T->n_columns < 2) {
11143 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Data file %s has only %" PRIu64 " data columns!\n", G->file, T->n_columns);
11144 			return (error);
11145 		}
11146 		/* Repackage this information into the G structure via f_xy and f_label arrays */
11147 		G->f_xy[GMT_X] = gmt_M_memory (GMT, NULL, T->n_records, double);
11148 		G->f_xy[GMT_Y] = gmt_M_memory (GMT, NULL, T->n_records, double);
11149 		if (T->type == GMT_READ_MIXED) G->f_label = gmt_M_memory (GMT, NULL, T->n_records, char *);
11150 		for (seg = rec = k = 0; seg < T->table[0]->n_segments; seg++) {
11151 			S = T->table[0]->segment[seg];	/* Current segment */
11152 			for (row = 0; row < S->n_rows; row++, rec++) {
11153 				xy[GMT_X] = S->data[GMT_X][row];	xy[GMT_Y] = S->data[GMT_Y][row];
11154 				gmt_map_outside (GMT, xy[GMT_X], xy[GMT_Y]);
11155 				if (abs (GMT->current.map.this_x_status) > 1 || abs (GMT->current.map.this_y_status) > 1) continue;	/* Outside map region */
11156 				gmt_geo_to_xy (GMT, xy[GMT_X], xy[GMT_Y], &G->f_xy[GMT_X][k], &G->f_xy[GMT_Y][k]);	/* Project -> xy inches */
11157 				if (S->text)	/* The label part if asked for */
11158 					G->f_label[k] = strdup (S->text[row]);
11159 				k++;
11160 			}
11161 		}
11162 		if ((G->f_n = (unsigned int)k) == 0) {
11163 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Fixed position file %s does not have any data records\n",
11164 			            G->flag, G->file);
11165 			error++;
11166 			gmt_M_free (GMT, G->f_xy[GMT_X]);
11167 			gmt_M_free (GMT, G->f_xy[GMT_Y]);
11168 			if (G->f_label) gmt_M_free (GMT, G->f_label);
11169 		}
11170 		if (GMT_Destroy_Data (GMT->parent, &T) != GMT_NOERROR)
11171 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Option -%c:  Failed to free DATASET allocated to parse %s\n",
11172 			            G->flag, G->file);
11173 	}
11174 
11175 	return (error);
11176 }
11177 
11178 /*! . */
gmt_contlabel_free(struct GMT_CTRL * GMT,struct GMT_CONTOUR * G)11179 void gmt_contlabel_free (struct GMT_CTRL *GMT, struct GMT_CONTOUR *G) {
11180 	uint64_t seg, j;
11181 	struct GMT_CONTOUR_LINE *L = NULL;
11182 
11183 	/* Free memory */
11184 
11185 	for (seg = 0; seg < G->n_segments; seg++) {
11186 		L = G->segment[seg];	/* Pointer to current segment */
11187 		for (j = 0; j < L->n_labels; j++) gmt_M_free (GMT, L->L[j].label);
11188 		gmt_M_free (GMT, L->L);
11189 		gmt_M_free (GMT, L->x);
11190 		gmt_M_free (GMT, L->y);
11191 		gmt_M_free (GMT, L->name);
11192 		gmt_M_free (GMT, L);
11193 	}
11194 	gmt_M_free (GMT, G->segment);
11195 	GMT_Destroy_Data (GMT->parent, &(G->X));
11196 	if (G->f_n) {	/* Array for fixed points */
11197 		gmt_M_free (GMT, G->f_xy[GMT_X]);
11198 		gmt_M_free (GMT, G->f_xy[GMT_Y]);
11199 		if (G->f_label) {
11200 			for (j = 0; j < G->f_n; j++) gmt_M_str_free (G->f_label[j]);
11201 			gmt_M_free (GMT, G->f_label);
11202 		}
11203 	}
11204 }
11205 
11206 /*! . */
gmt_symbol_free(struct GMT_CTRL * GMT,struct GMT_SYMBOL * S)11207 void gmt_symbol_free (struct GMT_CTRL *GMT, struct GMT_SYMBOL *S) {
11208 	/* Free memory used by the symbol structure in psxy[z] */
11209 	if (S->symbol == GMT_SYMBOL_QUOTED_LINE)
11210 		gmt_contlabel_free (GMT, &(S->G));
11211 	if (S->symbol == GMT_SYMBOL_DECORATED_LINE)
11212 		gmtsupport_decorate_free (GMT, &(S->D));
11213 }
11214 
gmt_contour_edge_init(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,unsigned int * n_edges)11215 unsigned int *gmt_contour_edge_init (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, unsigned int *n_edges) {
11216 	/* Create and return the edge bit flag array used to keep track of contours and return now many edges */
11217 	unsigned int *edge = NULL;
11218 	*n_edges = header->n_rows * (urint (ceil (header->n_columns / 16.0)));
11219 	edge = gmt_M_memory (GMT, NULL, *n_edges, unsigned int);
11220 	if (edge == NULL) *n_edges = 0;	/* Disaster, but at least return consistent values */
11221 	return (edge);
11222 }
11223 
11224 /*! . */
gmt_contours(struct GMT_CTRL * GMT,struct GMT_GRID * G,unsigned int smooth_factor,unsigned int int_scheme,int orient,unsigned int * edge,bool * first,double ** x,double ** y)11225 int64_t gmt_contours (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigned int smooth_factor, unsigned int int_scheme, int orient, unsigned int *edge, bool *first, double **x, double **y) {
11226 	/* The routine finds the zero-contour in the grd dataset.  it assumes that
11227 	 * no node has a value exactly == 0.0.  If more than max points are found
11228 	 * gmtsupport_trace_contour will try to allocate more memory in blocks of GMT_CHUNK points.
11229 	 * orient arranges the contour so that the values to the left of the contour is higher (orient = 1)
11230 	 * or lower (orient = -1) than the contour value.
11231 	 * Note: grd has a pad while edge does not!
11232 	 */
11233 
11234 	static unsigned int col_0, row_0, side;
11235 	unsigned int nans = 0, row, col;
11236 	int scol;
11237 	uint64_t n2, n_edges, offset;
11238 	int64_t n = 0;
11239 	double *x2 = NULL, *y2 = NULL;
11240 	static unsigned int bit[32];
11241 
11242 	n_edges = G->header->n_rows * (uint64_t) ceil (G->header->n_columns / 16.0);
11243 	offset = n_edges / 2;
11244 
11245 	/* Reset edge-flags to zero, if necessary */
11246 	if (*first) {	/* Set col_0,row_0 for southern boundary */
11247 		unsigned int i;
11248 		gmt_M_memset (edge, n_edges, unsigned int);
11249 		col_0 = side = 0;
11250 		row_0 = G->header->n_rows - 1;
11251 		for (i = 1, bit[0] = 1; i < 32; i++) bit[i] = bit[i-1] << 1;
11252 		*first = false;
11253 	}
11254 
11255 	if (side == 0) {	/* Southern boundary */
11256 		for (col = col_0, row = row_0; col < G->header->n_columns-1; col++) {
11257 			if ((n = gmtsupport_trace_contour (GMT, G, true, edge, x, y, col, row, 0, offset, bit, &nans))) {
11258 				if (orient) gmtsupport_orient_contour (G, *x, *y, n, orient);
11259 				if ((n = gmtsupport_smooth_contour (GMT, x, y, n, smooth_factor, int_scheme)) < 0) return n;
11260 				col_0 = col + 1;	row_0 = row;
11261 				return (n);
11262 			}
11263 		}
11264 		if (n == 0) {	/* No more crossing of southern boundary, go to next side (east) */
11265 			col_0 = G->header->n_columns - 2;
11266 			row_0 = G->header->n_rows - 1;
11267 			side++;
11268 		}
11269 	}
11270 
11271 	if (side == 1) {	/* Eastern boundary */
11272 		for (col = col_0, row = row_0; row > 0; row--) {
11273 			if ((n = gmtsupport_trace_contour (GMT, G, true, edge, x, y, col, row, 1, offset, bit, &nans))) {
11274 				if (orient) gmtsupport_orient_contour (G, *x, *y, n, orient);
11275 				if ((n = gmtsupport_smooth_contour (GMT, x, y, n, smooth_factor, int_scheme)) < 0) return n;
11276 				col_0 = col;	row_0 = row - 1;
11277 				return (n);
11278 			}
11279 		}
11280 		if (n == 0) {	/* No more crossing of eastern boundary, go to next side (north) */
11281 			col_0 = G->header->n_columns - 2;
11282 			row_0 = 1;
11283 			side++;
11284 		}
11285 	}
11286 
11287 	if (side == 2) {	/* Northern boundary */
11288 		for (col = scol = col_0, row = row_0; scol >= 0; col--, scol--) {
11289 			if ((n = gmtsupport_trace_contour (GMT, G, true, edge, x, y, col, row, 2, offset, bit, &nans))) {
11290 				if (orient) gmtsupport_orient_contour (G, *x, *y, n, orient);
11291 				if ((n = gmtsupport_smooth_contour (GMT, x, y, n, smooth_factor, int_scheme)) < 0) return n;
11292 				col_0 = col - 1;	row_0 = row;
11293 				return (n);
11294 			}
11295 		}
11296 		if (n == 0) {	/* No more crossing of northern boundary, go to next side (west) */
11297 			col_0 = 0;
11298 			row_0 = 1;
11299 			side++;
11300 		}
11301 	}
11302 
11303 	if (side == 3) {	/* Western boundary */
11304 		for (col = col_0, row = row_0; row < G->header->n_rows; row++) {
11305 			if ((n = gmtsupport_trace_contour (GMT, G, true, edge, x, y, col, row, 3, offset, bit, &nans))) {
11306 				if (orient) gmtsupport_orient_contour (G, *x, *y, n, orient);
11307 				if ((n = gmtsupport_smooth_contour (GMT, x, y, n, smooth_factor, int_scheme)) < 0) return n;
11308 				col_0 = col;	row_0 = row + 1;
11309 				return (n);
11310 			}
11311 		}
11312 		if (n == 0) {	/* No more crossing of western boundary, go to next side (vertical internals) */
11313 			col_0 = 1;
11314 			row_0 = 1;
11315 			side++;
11316 		}
11317 	}
11318 
11319 	if (side == 4) {	/* Then loop over interior boxes (vertical edges) */
11320 		for (row = row_0; row < G->header->n_rows; row++) {
11321 			for (col = col_0; col < G->header->n_columns-1; col++) {
11322                 if (row == 60 && col == 1)
11323                     n = 0;
11324 				if ((n = gmtsupport_trace_contour (GMT, G, true, edge, x, y, col, row, 3, offset, bit, &nans))) {
11325 					if (nans && (n2 = gmtsupport_trace_contour (GMT, G, false, edge, &x2, &y2, col-1, row, 1, offset, bit, &nans))) {
11326 						/* Must trace in other direction, then splice */
11327 						n = gmtsupport_splice_contour (GMT, x, y, n, x2, y2, n2);
11328 						gmt_M_free (GMT, x2);
11329 						gmt_M_free (GMT, y2);
11330 					}
11331 					if (orient) gmtsupport_orient_contour (G, *x, *y, n, orient);
11332 					if ((n = gmtsupport_smooth_contour (GMT, x, y, n, smooth_factor, int_scheme)) < 0) return n;
11333 					col_0 = col + 1;	row_0 = row;
11334 					return (n);
11335 				}
11336 			}
11337 			col_0 = 1;
11338 		}
11339 		if (n == 0) {	/* No more crossing of vertical internal edges, go to next side (horizontal internals) */
11340 			col_0 = 0;
11341 			row_0 = 1;
11342 			side++;
11343 		}
11344 	}
11345 
11346 	if (side == 5) {	/* Then loop over interior boxes (horizontal edges) */
11347 		for (row = row_0; row < G->header->n_rows; row++) {
11348 			for (col = col_0; col < G->header->n_columns-1; col++) {
11349 				if ((n = gmtsupport_trace_contour (GMT, G, true, edge, x, y, col, row, 2, offset, bit, &nans))) {
11350 					if (nans && (n2 = gmtsupport_trace_contour (GMT, G, false, edge, &x2, &y2, col-1, row, 0, offset, bit, &nans))) {
11351 						/* Must trace in other direction, then splice */
11352 						n = gmtsupport_splice_contour (GMT, x, y, n, x2, y2, n2);
11353 						gmt_M_free (GMT, x2);
11354 						gmt_M_free (GMT, y2);
11355 					}
11356 					if (orient) gmtsupport_orient_contour (G, *x, *y, n, orient);
11357 					if ((n = gmtsupport_smooth_contour (GMT, x, y, n, smooth_factor, int_scheme)) < 0) return n;
11358 					col_0 = col + 1;	row_0 = row;
11359 					return (n);
11360 				}
11361 			}
11362 			col_0 = 1;
11363 		}
11364 	}
11365 
11366 	/* Nothing found */
11367 	return (0);
11368 }
11369 
11370 /*! . */
gmt_prepare_contour(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,double z)11371 struct GMT_DATASEGMENT * gmt_prepare_contour (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, double z) {
11372 	/* Returns a segment with this contour */
11373 	unsigned int n_cols;
11374 	char header[GMT_BUFSIZ];
11375 	struct GMT_DATASEGMENT *S = NULL;
11376 
11377 	if (n < 2) return (NULL);
11378 
11379 	n_cols = (gmt_M_is_dnan (z)) ? 2 : 3;	/* psmask only dumps xy */
11380 	S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, n, n_cols, NULL, NULL);
11381 	if (n_cols == 3)
11382 		snprintf (header, GMT_BUFSIZ, "%g contour -Z%g", z, z);
11383 	else
11384 		sprintf (header, "clip contour");
11385 	S->header = strdup (header);
11386 
11387 	gmt_M_memcpy (S->data[GMT_X], x, n, double);
11388 	gmt_M_memcpy (S->data[GMT_Y], y, n, double);
11389 	if (n_cols == 3) gmt_M_setnval (S->data[GMT_Z], n, z);
11390 	S->n_rows = n;
11391 
11392 	return (S);
11393 }
11394 
11395 /*! . */
gmt_make_filename(struct GMT_CTRL * GMT,char * template,unsigned int fmt[],double z,bool closed,unsigned int count[])11396 char * gmt_make_filename (struct GMT_CTRL *GMT, char *template, unsigned int fmt[], double z, bool closed, unsigned int count[]) {
11397 	/* Produce a filename given the template and the running values.
11398 	 * Here, c, d, f stands for the O/C character, the running count,
11399 	 * and the contour level z.  When a running count is used it is
11400 	 * incremented.  If c is not used the only count[0] is used, else
11401 	 * we used count[0] for open and count[1] for closed contours. */
11402 
11403 	unsigned int i, n_fmt;
11404 	static char kind[2] = {'O', 'C'};
11405 	char file[PATH_MAX];
11406 	gmt_M_unused(GMT);
11407 
11408 	for (i = n_fmt = 0; i < 3; i++) if (fmt[i]) n_fmt++;
11409 	if (n_fmt == 0) return (NULL);	/* Will write single file [or to stdout] */
11410 
11411 	if (n_fmt == 1) {	/* Just need a single item */
11412 		if (fmt[0])	/* Just two kinds of files, closed and open */
11413 			snprintf (file, PATH_MAX, template, kind[closed]);
11414 		else if (fmt[1])	/* Just files with a single unique running number */
11415 			snprintf (file, PATH_MAX, template, count[0]++);
11416 		else	/* Contour level file */
11417 			snprintf (file, PATH_MAX, template, z);
11418 	}
11419 	else if (n_fmt == 2) {	/* Need two items, and f+c is not allowed */
11420 		if (fmt[0]) {	/* open/close is involved */
11421 			if (fmt[1] < fmt[0])	/* Files with a single unique running number and open/close */
11422 				snprintf (file, PATH_MAX, template, count[closed]++, kind[closed]);
11423 			else	/* Same in reverse order */
11424 				snprintf (file, PATH_MAX, template, kind[closed], count[closed]++);
11425 		}
11426 		else if (fmt[1] < fmt[2])	/* Files with a single unique running number and contour value */
11427 			snprintf (file, PATH_MAX, template, count[0]++, z);
11428 		else 	/* Same in reverse order */
11429 			snprintf (file, PATH_MAX, template, z, count[0]++);
11430 	}
11431 	else {	/* Combinations of three items */
11432 		if (fmt[0] < fmt[1] && fmt[1] < fmt[2])	/* Files with c, d, and f in that order */
11433 			snprintf (file, PATH_MAX, template, kind[closed], count[closed]++, z);
11434 		else if (fmt[0] < fmt[2] && fmt[2] < fmt[1])	/* Files with c, f, and d in that order */
11435 			snprintf (file, PATH_MAX, template, kind[closed], z, count[closed]++);
11436 		else if (fmt[1] < fmt[2] && fmt[2] < fmt[0])	/* Files with d, f, c in that order */
11437 			snprintf (file, PATH_MAX, template, count[closed]++, z, kind[closed]);
11438 		else if (fmt[1] < fmt[0] && fmt[0] < fmt[2])	/* Files with d, c, f in that order */
11439 			snprintf (file, PATH_MAX, template, count[closed]++, kind[closed], z);
11440 		else if (fmt[2] < fmt[0] && fmt[0] < fmt[1])	/* Files with f, c, d in that order */
11441 			snprintf (file, PATH_MAX, template, z, kind[closed], count[closed]++);
11442 		else						/* Files with f, d, c in that order */
11443 			snprintf (file, PATH_MAX, template, z, count[closed]++, kind[closed]);
11444 	}
11445 	return (strdup (file));
11446 }
11447 
11448 
11449 /* Function to split a floating point into nearest fraction, with maxden the largest allowable denominator.
11450  * Borrowed from https://www.ics.uci.edu/~eppstein/numth/frap.c */
11451 
gmtsupport_make_fraction(struct GMT_CTRL * GMT,double x0,int maxden,int * n,int * d)11452 GMT_LOCAL void gmtsupport_make_fraction (struct GMT_CTRL *GMT, double x0, int maxden, int *n, int *d) {
11453 	uint64_t m[2][2], ai;
11454 	double x = x0, e;
11455 	/* initialize matrix */
11456 	m[0][0] = m[1][1] = 1, m[0][1] = m[1][0] = 0;
11457 
11458 	/* loop finding terms until denom gets too big */
11459 	while (m[1][0] *  ( ai = (uint64_t)x ) + m[1][1] <= (uint64_t)maxden) {
11460 		uint64_t t = m[0][0] * ai + m[0][1];
11461 		m[0][1] = m[0][0];
11462 		m[0][0] = t;
11463 		t = m[1][0] * ai + m[1][1];
11464 		m[1][1] = m[1][0];
11465 		m[1][0] = t;
11466 		if (x == (double)ai) break;     // AF: division by zero
11467 		x = 1 / (x - (double) ai);
11468 		if (x > (double)0x7FFFFFFF) break;  // AF: representation failure
11469  	}
11470 
11471 	*n = m[0][0];	*d = m[1][0];
11472  	e = x0 - ((double) *n / (double) *d);
11473 	if (e > GMT_CONV4_LIMIT)
11474 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Bad fraction, error = %g\n", e);
11475 }
11476 
11477 /*! . */
gmt_sprintf_float(struct GMT_CTRL * GMT,char * string,char * format,double x)11478 void gmt_sprintf_float (struct GMT_CTRL *GMT, char *string, char *format, double x) {
11479 	/* Determines if %-apostrophe is used in the format for a float. If so, must temporarily switch to LC_NUMERIC=en_US */
11480 	char *use_locale = strstr (format, "%'");
11481 #ifdef HAVE_SETLOCALE
11482 	if (use_locale) setlocale (LC_NUMERIC, "en_US");
11483 #endif
11484 	if (GMT->current.plot.substitute_pi) {	/* Want to use pi when close to known multiples of pi. This variable is set per axis in gmt_xy_axis */
11485 		/* Any fraction of pi up to 1/20th allowed.
11486 		 * substitute_pi becomes true when items with "pi" are used in -R, -I etc. */
11487 		int k = 1, m, n;
11488 		char fmt[GMT_LEN16] = {""};
11489 		double f = fabs (x / M_PI);		/* Float multiples of pi */
11490 		if (fabs (f) < GMT_CONV4_LIMIT) {	/* Deal with special case of zero pi */
11491 			sprintf (string, "0");
11492 			return;
11493 		}
11494 		gmtsupport_make_fraction (GMT, f, 20, &n, &m);	/* Max 20th of pi */
11495 		string[0] = (x < 0.0) ? '-' : '+';	/* Place leading sign */
11496 		string[1] = '\0';			/* Chop off old string */
11497 		if (n > 1) {	/* Need an integer in front of pi */
11498 			k += snprintf (fmt, GMT_LEN16, "%d", n);
11499 			strcat (string, fmt);
11500 			//k += irint (floor (log10 ((double)n))) + 1;	/* Add how many decimals needed for n */
11501 		}
11502 		strcat (string, "@~p@~");	/* Place the pi symbol */
11503 		k += 5;	/* Add number of characters just placed to print pi */
11504 		if (m > 1) {	/* Add the fractions part */
11505 			k += snprintf (fmt, GMT_LEN16, "/%d", m);
11506 			strcat (string, fmt);
11507 			//k += irint (floor (log10 ((double)m))) + 2;	/* Add how many decimals needed for /m */
11508 		}
11509 		string[k] = '\0';	/* Truncate any old string */
11510 		//fprintf (stderr, "f = %g n = %d  m = %d.  String = %s\n", f, n, m, string);
11511 	}
11512 	else
11513 #ifndef WIN32
11514 		sprintf (string, format, x);
11515 #else
11516 	{	/* Windows doesn't support %' format */
11517 		if (use_locale) {
11518 			char *new_format = gmt_strrep(format, "%'", "%");
11519 			sprintf (string, new_format, x);
11520 			gmt_M_str_free (new_format);
11521 		} else
11522 			sprintf (string, format, x);
11523 	}
11524 #endif
11525 	if (use_locale) {
11526 #ifdef HAVE_SETLOCALE
11527 		setlocale (LC_NUMERIC, "C");	/* Undo the damage */
11528 #endif
11529 		if (strchr (string, ',') == NULL && fabs (x) > 1000.0 && fabs (x - irint (x)) < GMT_CONV8_LIMIT) {
11530 			/* System not capable of printf groups for integers so we insert those commas manually... */
11531 			char *tmp = strdup (string);
11532 			int n = 0, olen = (int)strlen (tmp), k = (x < 0) ? 1 : 0;
11533 			int nlen = olen + irint (floor (log10(fabs(x))/3.0));	/* Number of commas added */
11534 			string[nlen] = '\0';
11535 			/* We guild the string from back to front */
11536 			while (olen) {	/* While more letters in original string */
11537 				string[--nlen] = tmp[--olen];
11538 				if (++n == 3 && (olen-k) > 0) {
11539 					string[--nlen] = ',';
11540 					n = 0;
11541 				}
11542 			}
11543 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Expanded %s to %s\n", tmp, string);
11544 			gmt_M_str_free (tmp);
11545 		}
11546 	}
11547 }
11548 
11549 /*! . */
gmt_hold_contour(struct GMT_CTRL * GMT,double ** xxx,double ** yyy,uint64_t nn,double zval,char * label,char ctype,double cangle,bool closed,bool contour,struct GMT_CONTOUR * G)11550 void gmt_hold_contour (struct GMT_CTRL *GMT, double **xxx, double **yyy, uint64_t nn, double zval, char *label, char ctype, double cangle, bool closed, bool contour, struct GMT_CONTOUR *G) {
11551 	/* The xxx, yyy are expected to be projected x/y inches.
11552 	 * This function just makes sure that the xxx/yyy are continuous and do not have map jumps.
11553 	 * If there are jumps we find them and call the main gmtsupport_hold_contour_sub for each segment
11554 	 * contour is true for contours and false for quoted lines.
11555 	 */
11556 
11557 	uint64_t seg, first, n, *split = NULL;
11558 	double *xs = NULL, *ys = NULL, *xin = NULL, *yin = NULL;
11559 
11560 	if ((split = gmtlib_split_line (GMT, xxx, yyy, &nn, G->line_type)) == NULL) {	/* Just one long line */
11561 		gmtsupport_hold_contour_sub (GMT, xxx, yyy, nn, zval, label, ctype, cangle, closed, contour, G);
11562 		return;
11563 	}
11564 
11565 	/* Here we had jumps and need to call the _sub function once for each segment */
11566 
11567 	xin = *xxx;	yin = *yyy;
11568 	for (seg = 0, first = 0; seg <= split[0]; seg++) {	/* Number of segments are given by split[0] + 1 */
11569 		n = split[seg+1] - first;
11570 		xs = gmt_M_memory (GMT, NULL, n, double);
11571 		ys = gmt_M_memory (GMT, NULL, n, double);
11572 		gmt_M_memcpy (xs, &xin[first], n, double);
11573 		gmt_M_memcpy (ys, &yin[first], n, double);
11574 		gmtsupport_hold_contour_sub (GMT, &xs, &ys, n, zval, label, ctype, cangle, closed, contour, G);
11575 		gmt_M_free (GMT, xs);
11576 		gmt_M_free (GMT, ys);
11577 		first += n;	/* First point in the next segment */
11578 	}
11579 	gmt_M_free (GMT, split);
11580 }
11581 
11582 /*! . */
gmt_decorated_line(struct GMT_CTRL * GMT,double ** xxx,double ** yyy,uint64_t nn,struct GMT_DECORATE * G,struct GMT_DATASET * D,uint64_t seg)11583 void gmt_decorated_line (struct GMT_CTRL *GMT, double **xxx, double **yyy, uint64_t nn, struct GMT_DECORATE *G, struct GMT_DATASET *D, uint64_t seg) {
11584 	/* The xxx, yyy are expected to be projected x/y inches.
11585 	 * This function just makes sure that the xxx/yyy are continuous and do not have map jumps.
11586 	 * If there are jumps we find them and call the main gmtsupport_decorated_line_sub for each segment
11587 	 */
11588 
11589 	uint64_t *split = NULL;
11590 
11591 	if ((split = gmtlib_split_line (GMT, xxx, yyy, &nn, G->line_type)) == NULL)	/* Just one long line */
11592 		gmtsupport_decorated_line_sub (GMT, *xxx, *yyy, nn, G, D, seg);
11593 	else {
11594 		/* Here we had jumps and need to call the _sub function once for each segment */
11595 		uint64_t seg, first, n;
11596 		double *xin = *xxx, *yin = *yyy;
11597 		for (seg = 0, first = 0; seg <= split[0]; seg++) {	/* Number of segments are given by split[0] + 1 */
11598 			n = split[seg+1] - first;
11599 			gmtsupport_decorated_line_sub (GMT, &xin[first], &yin[first], n, G, D, seg);
11600 			first = n;	/* First point in next segment */
11601 		}
11602 		gmt_M_free (GMT, split);
11603 	}
11604 }
11605 
11606 /*! . */
gmt_get_plot_array(struct GMT_CTRL * GMT)11607 void gmt_get_plot_array (struct GMT_CTRL *GMT) {
11608 	/* Allocate more space for plot arrays */
11609 	GMT->current.plot.n_alloc = (GMT->current.plot.n_alloc == 0) ? GMT_CHUNK : (GMT->current.plot.n_alloc << 1);
11610 	GMT->current.plot.x = gmt_M_memory (GMT, GMT->current.plot.x, GMT->current.plot.n_alloc, double);
11611 	GMT->current.plot.y = gmt_M_memory (GMT, GMT->current.plot.y, GMT->current.plot.n_alloc, double);
11612 	GMT->current.plot.pen = gmt_M_memory (GMT, GMT->current.plot.pen, GMT->current.plot.n_alloc, unsigned int);
11613 }
11614 
11615 /*! . */
gmt_get_format(struct GMT_CTRL * GMT,double interval,char * unit,char * prefix,char * format)11616 int gmt_get_format (struct GMT_CTRL *GMT, double interval, char *unit, char *prefix, char *format) {
11617 	int i, j, ndec = 0;
11618 	bool general = false;
11619 	char text[GMT_BUFSIZ];
11620 	size_t s_length;
11621 
11622 	if (!strcmp (GMT->current.setting.format_float_map, "%.12g")) {	/* Default map format given means auto-detect decimals */
11623 
11624 		/* Find number of decimals needed in the format statement */
11625 
11626 		snprintf (text, GMT_BUFSIZ, GMT->current.setting.format_float_map, interval);
11627 		for (i = 0; text[i] && text[i] != '.'; i++);
11628 		if (text[i]) {	/* Found a decimal point */
11629 			for (j = i + 1; text[j] && text[j] != 'e'; j++);
11630 			ndec = j - i - 1;
11631 			if (text[j] == 'e') {	/* Exponential notation, modify ndec */
11632 				ndec -= atoi (&text[++j]);
11633 				if (ndec < 0) ndec = 0;	/* since a positive exponent could give -ve answer */
11634 			}
11635 		}
11636 		general = true;
11637 		strcpy (format, GMT->current.setting.format_float_map);
11638 	}
11639 
11640 	if (unit && unit[0]) {	/* Must append the unit string */
11641 		if (!strchr (unit, '%'))	/* No percent signs */
11642 			strncpy (text, unit, 80U);
11643 		else {
11644 			s_length = strlen(unit);
11645 			for (i = j = 0; i < (int)s_length; i++) {
11646 				text[j++] = unit[i];
11647 				if (unit[i] == '%') text[j++] = unit[i];
11648 			}
11649 			text[j] = 0;
11650 		}
11651 		if (ndec > 0)
11652 			sprintf (format, "%%.%df%s", ndec, text);
11653 		else
11654 			sprintf (format, "%s%s", GMT->current.setting.format_float_map, text);
11655 		if (ndec == 0) ndec = 1;	/* To avoid resetting format later */
11656 	}
11657 	else if (ndec > 0)
11658 		sprintf (format, "%%.%df", ndec);
11659 	else if (!general) {	/* Pull ndec from given format if .<precision> is given */
11660 		int len = (int)MIN(strlen(GMT->current.setting.format_float_map), GMT_LEN64) - 1;
11661 		for (i = 0, j = -1; j == -1 && i < len && GMT->current.setting.format_float_map[i]; i++)
11662 			if (i < len && GMT->current.setting.format_float_map[i] == '.') j = i;	/* The extra i > len needed to defeat cppcheck confusion */
11663 		if (j > -1) ndec = atoi (&GMT->current.setting.format_float_map[j+1]);
11664 		strcpy (format, GMT->current.setting.format_float_map);
11665 	}
11666 	if (prefix && prefix[0]) {	/* Must prepend the prefix string */
11667 		snprintf (text, GMT_BUFSIZ, "%s%s", prefix, format);
11668 		strcpy (format, text);
11669 	}
11670 	return (ndec);
11671 }
11672 
gmt_set_inside_mode(struct GMT_CTRL * GMT,struct GMT_DATASET * D,unsigned int mode)11673 void gmt_set_inside_mode (struct GMT_CTRL *GMT, struct GMT_DATASET *D, unsigned int mode) {
11674 	/* Determine if we use spherical or Cartesian function for in--on-out polygon tests */
11675 	static char *method[2] = {"Cartesian", "spherical"};
11676 	if (mode == GMT_IOO_SPHERICAL)	/* Force spherical */
11677 		GMT->current.proj.sph_inside = true;
11678 	else if (mode == GMT_IOO_CARTESIAN)	/* Force Cartesian */
11679 		GMT->current.proj.sph_inside = false;
11680 	else if (gmt_M_is_cartesian (GMT, GMT_IN))	/* If data is Cartesian then we do that */
11681 		GMT->current.proj.sph_inside = false;
11682 	else if (GMT->current.map.is_world)	/* Here we are dealing with geographic data that has 360 degree range */
11683 		GMT->current.proj.sph_inside = true;
11684 	else if (D) {	/* Geographic data less than 360 degree range in longitudes */
11685 		double lat[2];
11686 		lat[0] = D->min[GMT_Y]; lat[1] = D->max[GMT_Y];
11687 		if (doubleAlmostEqual (lat[0], -90.0) || doubleAlmostEqual (lat[1], +90.0))	/* Goes to a pole, must do spherical */
11688 			GMT->current.proj.sph_inside = true;
11689 		else {	/* Limited in lon and lat, can do Cartesian but must ensure polygons do not jump within range */
11690 			uint64_t tbl, seg, row;
11691 			unsigned int range;
11692 			struct GMT_DATASEGMENT *S = NULL;
11693 
11694 			GMT->current.proj.sph_inside = false;
11695 			if (D->min[GMT_X] >= 0.0 && D->max[GMT_X] > 0.0)
11696 				range = GMT_IS_0_TO_P360_RANGE;
11697 			else if (D->min[GMT_X] < 0.0 && D->max[GMT_X] <= 0.0)
11698 				range = GMT_IS_M360_TO_0_RANGE;
11699 			else
11700 				range = GMT_IS_M180_TO_P180_RANGE;
11701 			/* Ensure that all longitudes are in the correct range as required by gmt_non_zero_winding */
11702 			for (tbl = 0; tbl < D->n_tables; tbl++) {
11703 				for (seg = 0; seg < D->table[tbl]->n_segments; seg++) {
11704 					S = D->table[tbl]->segment[seg];	/* Shorthand */
11705 					for (row = 0; row < S->n_rows; row++)
11706 						gmt_lon_range_adjust (range, &S->data[GMT_X][row]);
11707 				}
11708 			}
11709 		}
11710 	}
11711 	else
11712 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Not enough information given to gmt_set_inside_mode.\n");
11713 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "A point's inside/outside status w.r.t. polygon(s) will be determined using a %s algorithm.\n", method[GMT->current.proj.sph_inside]);
11714 }
11715 
11716 /*! . */
gmt_non_zero_winding(struct GMT_CTRL * GMT,double xp,double yp,double * x_in,double * y_in,uint64_t n_path)11717 unsigned int gmt_non_zero_winding (struct GMT_CTRL *GMT, double xp, double yp, double *x_in, double *y_in, uint64_t n_path) {
11718 	/* Routine returns (2) if (xp,yp) is inside the
11719 	   polygon x[n_path], y[n_path], (0) if outside,
11720 	   and (1) if exactly on the path edge.
11721 	   Uses non-zero winding rule in Adobe PostScript
11722 	   Language reference manual, section 4.6 on Painting.
11723 	   Path should have been closed first, so that
11724 	   x[n_path-1] = x[0], and y[n_path-1] = y[0].
11725 
11726 	   This is version 2, trying to kill a bug
11727 	   in above routine:  If point is on X edge,
11728 	   fails to discover that it is on edge.
11729 
11730 	   We are imagining a ray extending "up" from the
11731 	   point (xp,yp); the ray has equation x = xp, y >= yp.
11732 	   Starting with crossing_count = 0, we add 1 each time
11733 	   the path crosses the ray in the +x direction, and
11734 	   subtract 1 each time the ray crosses in the -x direction.
11735 	   After traversing the entire polygon, if (crossing_count)
11736 	   then point is inside.  We also watch for edge conditions.
11737 
11738 	   If two or more points on the path have x[i] == xp, then
11739 	   we have an ambiguous case, and we have to find the points
11740 	   in the path before and after this section, and check them.
11741 	   */
11742 
11743 	uint64_t i, j, k, jend, crossing_count;
11744 	bool above, free = false;
11745 	double y_sect, *x, *y;
11746 
11747 	if (n_path < 3) return (GMT_OUTSIDE);	/* Cannot be inside a null set, a point or a straight line so default to outside */
11748 
11749 	if (gmt_polygon_is_open (GMT, x_in, y_in, n_path)) {
11750 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmt_non_zero_winding given non-closed polygon - must create closed polygon\n");
11751 		x = gmt_M_memory (GMT, NULL, n_path+1, double);
11752 		y = gmt_M_memory (GMT, NULL, n_path+1, double);
11753 		gmt_M_memcpy (x, x_in, n_path, double);	x[n_path] = x[0];	/* CLose the polygon */
11754 		gmt_M_memcpy (y, y_in, n_path, double);	y[n_path] = y[0];	/* CLose the polygon */
11755 		free = true;
11756 	}
11757 	else {	/* Can use input points */
11758 		x = x_in;
11759 		y = y_in;
11760 	}
11761 
11762 	above = false;
11763 	crossing_count = 0;
11764 
11765 	/* First make sure first point in path is not a special case:  */
11766 	j = jend = n_path - 1;
11767 	if (x[j] == xp) {
11768 		/* Trouble already.  We might get lucky:  */
11769 		if (y[j] == yp) goto on_edge_case;
11770 
11771 		/* Go backward down the polygon until x[i] != xp:  */
11772 		if (y[j] > yp) above = true;
11773 		i = j - 1;
11774 		while (x[i] == xp && i > 0) {
11775 			if (y[i] == yp) goto on_edge_case;
11776 			if (!(above) && y[i] > yp) above = true;
11777 			i--;
11778 		}
11779 
11780 		/* Now if i == 0 polygon is degenerate line x=xp;
11781 		   since we know xp,yp is inside bounding box,
11782 		   it must be on edge:  */
11783 		if (i == 0) goto on_edge_case;
11784 
11785 		/* Now we want to mark this as the end, for later:  */
11786 		jend = i;
11787 
11788 		/* Now if (j-i)>1 there are some segments the point could be exactly on:  */
11789 		for (k = i+1; k < j; k++) {
11790 			if ((y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp)) goto on_edge_case;
11791 		}
11792 
11793 
11794 		/* Now we have arrived where i is > 0 and < n_path-1, and x[i] != xp.
11795 			We have been using j = n_path-1.  Now we need to move j forward
11796 			from the origin:  */
11797 		j = 1;
11798 		while (x[j] == xp) {
11799 			if (y[j] == yp) goto on_edge_case;
11800 			if (!(above) && y[j] > yp) above = true;
11801 			j++;
11802 		}
11803 
11804 		/* Now at the worst, j == jstop, and we have a polygon with only 1 vertex
11805 			not at x = xp.  But now it doesn't matter, that would end us at
11806 			the main while below.  Again, if j>=2 there are some segments to check:  */
11807 		for (k = 0; k < j-1; k++) {
11808 			if ((y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp)) goto on_edge_case;
11809 		}
11810 
11811 
11812 		/* Finally, we have found an i and j with points != xp.  If (above) we may have crossed the ray:  */
11813 		if (above && x[i] < xp && x[j] > xp)
11814 			crossing_count++;
11815 		else if (above && x[i] > xp && x[j] < xp)
11816 			crossing_count--;
11817 
11818 		/* End nightmare scenario for x[0] == xp.  */
11819 	}
11820 
11821 	else {
11822 		/* Get here when x[0] != xp:  */
11823 		i = 0;
11824 		j = 1;
11825 		while (x[j] == xp && j < jend) {
11826 			if (y[j] == yp) goto on_edge_case;
11827 			if (!(above) && y[j] > yp) above = true;
11828 			j++;
11829 		}
11830 		/* Again, if j==jend, (i.e., 0) then we have a polygon with only 1 vertex
11831 			not on xp and we will branch out below.  */
11832 
11833 		/* if ((j-i)>2) the point could be on intermediate segments:  */
11834 		for (k = i+1; k < j-1; k++) {
11835 			if ((y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp)) goto on_edge_case;
11836 		}
11837 
11838 		/* Now we have x[i] != xp and x[j] != xp.
11839 			If (above) and x[i] and x[j] on opposite sides, we are certain to have crossed the ray.
11840 			If not (above) and (j-i)>1, then we have not crossed it.
11841 			If not (above) and j-i == 1, then we have to check the intersection point.  */
11842 
11843 		if (x[i] < xp && x[j] > xp) {
11844 			if (above)
11845 				crossing_count++;
11846 			else if ((j-i) == 1) {
11847 				y_sect = y[i] + (y[j] - y[i]) * ((xp - x[i]) / (x[j] - x[i]));
11848 				if (y_sect == yp) goto on_edge_case;
11849 				if (y_sect > yp) crossing_count++;
11850 			}
11851 		}
11852 		if (x[i] > xp && x[j] < xp) {
11853 			if (above)
11854 				crossing_count--;
11855 			else if ((j-i) == 1) {
11856 				y_sect = y[i] + (y[j] - y[i]) * ((xp - x[i]) / (x[j] - x[i]));
11857 				if (y_sect == yp) goto on_edge_case;
11858 				if (y_sect > yp) crossing_count--;
11859 			}
11860 		}
11861 
11862 		/* End easier case for x[0] != xp  */
11863 	}
11864 
11865 	/* Now MAIN WHILE LOOP begins:
11866 		Set i = j, and search for a new j, and do as before.  */
11867 
11868 	i = j;
11869 	while (i < jend) {
11870 		above = false;
11871 		j = i+1;
11872 		while (x[j] == xp) {
11873 			if (y[j] == yp) goto on_edge_case;
11874 			if (!(above) && y[j] > yp) above = true;
11875 			j++;
11876 		}
11877 		/* if ((j-i)>2) the point could be on intermediate segments:  */
11878 		for (k = i+1; k < j-1; k++) {
11879 			if ((y[k] <= yp && y[k+1] >= yp) || (y[k] >= yp && y[k+1] <= yp)) goto on_edge_case;
11880 		}
11881 
11882 		/* Now we have x[i] != xp and x[j] != xp.
11883 			If (above) and x[i] and x[j] on opposite sides, we are certain to have crossed the ray.
11884 			If not (above) and (j-i)>1, then we have not crossed it.
11885 			If not (above) and j-i == 1, then we have to check the intersection point.  */
11886 
11887 		if (x[i] < xp && x[j] > xp) {
11888 			if (above)
11889 				crossing_count++;
11890 			else if ((j-i) == 1) {
11891 				y_sect = y[i] + (y[j] - y[i]) * ((xp - x[i]) / (x[j] - x[i]));
11892 				if (y_sect == yp) goto on_edge_case;
11893 				if (y_sect > yp) crossing_count++;
11894 			}
11895 		}
11896 		if (x[i] > xp && x[j] < xp) {
11897 			if (above)
11898 				crossing_count--;
11899 			else if ((j-i) == 1) {
11900 				y_sect = y[i] + (y[j] - y[i]) * ((xp - x[i]) / (x[j] - x[i]));
11901 				if (y_sect == yp) goto on_edge_case;
11902 				if (y_sect > yp) crossing_count--;
11903 			}
11904 		}
11905 
11906 		/* That's it for this piece.  Advance i:  */
11907 
11908 		i = j;
11909 	}
11910 
11911 	if (free) { gmt_M_free (GMT, x); gmt_M_free (GMT, y); }
11912 
11913 	/* End of MAIN WHILE.  Get here when we have gone all around without landing on edge.  */
11914 
11915 	return ((crossing_count) ? GMT_INSIDE: GMT_OUTSIDE);
11916 
11917 on_edge_case:
11918 	if (free) { gmt_M_free (GMT, x); gmt_M_free (GMT, y); }
11919 
11920 	return (GMT_ONEDGE);
11921 }
11922 
11923 /*! . */
gmt_inonout(struct GMT_CTRL * GMT,double x,double y,struct GMT_DATASEGMENT * S)11924 unsigned int gmt_inonout (struct GMT_CTRL *GMT, double x, double y, struct GMT_DATASEGMENT *S) {
11925 	/* Front end for both spherical and Cartesian in-on-out functions.
11926  	 * Knows to check for polygons with holes as well. */
11927 	unsigned int side, side_h;
11928 	struct GMT_DATASEGMENT *H = NULL;
11929 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
11930 	struct GMT_DATASEGMENT_HIDDEN *SHnext = NULL;
11931 
11932 	if ((side = gmtsupport_inonout_sub (GMT, x, y, S)) <= GMT_ONEDGE) return (side);	/* Outside polygon or on perimeter, we are done */
11933 
11934 	/* Here, point is inside the polygon perimeter. See if there are holes */
11935 
11936 	if (GMT->current.io.OGR && (H = SH->next)) {	/* Must check for and skip if inside a hole */
11937 		side_h = GMT_OUTSIDE;	/* We are outside a hole until we are found to be inside it */
11938 		SHnext = gmt_get_DS_hidden (H);
11939 		while (side_h == GMT_OUTSIDE && H && SHnext->ogr && SHnext->ogr->pol_mode == GMT_IS_HOLE) {	/* Found a hole */
11940 			/* Must check if point is inside this hole polygon */
11941 			side_h = gmtsupport_inonout_sub (GMT, x, y, H);
11942 			H = SHnext->next;	/* Move to next polygon hole */
11943 			if (H) SHnext = gmt_get_DS_hidden (H);
11944 		}
11945 		if (side_h == GMT_INSIDE) side = GMT_OUTSIDE;	/* Inside one of the holes, hence outside polygon; go to next perimeter polygon */
11946 		if (side_h == GMT_ONEDGE) side = GMT_ONEDGE;	/* On path of one of the holes, hence on polygon path; update side */
11947 	}
11948 	else if ((H = SH->next)) {	/* Must also check non ogr polygones for holes */
11949 		side_h = GMT_OUTSIDE;	/* We are outside a hole until we are found to be inside it */
11950 		SHnext = gmt_get_DS_hidden (H);
11951 		while (side_h == GMT_OUTSIDE && H && SHnext->pol_mode == GMT_IS_HOLE) {	/* Found a hole */
11952 			/* Must check if point is inside this hole polygon */
11953 			side_h = gmtsupport_inonout_sub (GMT, x, y, H);
11954 			H = SHnext->next;	/* Move to next polygon hole */
11955 			if (H) SHnext = gmt_get_DS_hidden (H);
11956 		}
11957 		if (side_h == GMT_INSIDE) side = GMT_OUTSIDE;	/* Inside one of the holes, hence outside polygon; go to next perimeter polygon */
11958 		if (side_h == GMT_ONEDGE) side = GMT_ONEDGE;	/* On path of one of the holes, hence on polygon path; update side */
11959 	}
11960 
11961 	/* Here, point is inside or on edge, we return the value */
11962 	return (side);
11963 }
11964 
11965 /* GMT always compiles the standard Delaunay triangulation routine
11966  * based on the work by Dave Watson.  You may also link with the triangle.o
11967  * module from Jonathan Shewchuk, Berkeley U. by passing the compiler
11968  * directive -DTRIANGLE_D. The latter is much faster and also has options
11969  * for Voronoi construction (which Watson's function does not have).
11970  */
11971 
11972 /*! . */
gmt_delaunay(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n,int ** link)11973 int64_t gmt_delaunay (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n, int **link) {
11974 	if (GMT->current.setting.triangulate == GMT_TRIANGLE_SHEWCHUK) return (gmtsupport_delaunay_shewchuk (GMT, x_in, y_in, n, link));
11975 	if (GMT->current.setting.triangulate == GMT_TRIANGLE_WATSON)   return (gmtsupport_delaunay_watson    (GMT, x_in, y_in, n, link));
11976 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT_TRIANGULATE outside possible range! %d\n", GMT->current.setting.triangulate);
11977 	return (-1);
11978 }
11979 
11980 /*! . */
gmt_voronoi(struct GMT_CTRL * GMT,double * x_in,double * y_in,uint64_t n,double * wesn,unsigned int mode)11981 struct GMT_DATASET * gmt_voronoi (struct GMT_CTRL *GMT, double *x_in, double *y_in, uint64_t n, double *wesn, unsigned int mode) {
11982 	if (GMT->current.setting.triangulate == GMT_TRIANGLE_SHEWCHUK) return (gmtsupport_voronoi_shewchuk (GMT, x_in, y_in, n, wesn, mode));
11983 	if (GMT->current.setting.triangulate == GMT_TRIANGLE_WATSON)   return (gmtsupport_voronoi_watson    (GMT, x_in, y_in, n, wesn, mode));
11984 	GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT_TRIANGULATE outside possible range! %d\n", GMT->current.setting.triangulate);
11985 	return (NULL);
11986 }
11987 
11988 /*! . */
gmt_delaunay_free(struct GMT_CTRL * GMT,int ** link)11989 void gmt_delaunay_free (struct GMT_CTRL *GMT, int **link) {
11990 	/* Since one function uses gmt_M_memory and the other does not */
11991 	if (GMT->current.setting.triangulate == GMT_TRIANGLE_SHEWCHUK) gmt_M_str_free (*link);
11992 	if (GMT->current.setting.triangulate == GMT_TRIANGLE_WATSON) gmt_M_free (GMT, *link);
11993 }
11994 /*
11995  * This section holds functions used for setting boundary  conditions in
11996  * processing grd file data.
11997  *
11998  * This is a new feature of GMT v3.1.   My first draft of this (April 1998)
11999  * set only one row of padding.  Additional thought showed that the bilinear
12000  * invocation of bcr routines would not work properly on periodic end conditions
12001  * in this case.  So second draft (May 5-6, 1998) will set two rows of padding,
12002  * and I will also have to edit bcr so that it works for this case.  The GMT
12003  * bcr routines are currently used in grdsample, grdtrack, and grdview.
12004  *
12005  * I anticipate that later (GMT v5 ?) this code could (?) be modified to also
12006  * handle the boundary conditions needed by surface.
12007  *
12008  * The default boundary condition is derived from application of Green's
12009  * theorem to the conditions for minimizing curvature:
12010  * laplacian (f) = 0 on edges, and d[laplacian(f)]/dn = 0 on edges, where
12011  * n is normal to an edge.  We also set d2f/dxdy = 0 at corners.
12012  *
12013  * The new routines here allow the user to choose other specifications:
12014  *
12015  * Either
12016  *	one or both of
12017  *	data are periodic in (x_max - x_min)
12018  *	data are periodic in (y_max - y_min)
12019  *
12020  * Or
12021  *	data are a geographical grid.
12022  *
12023  * Periodicities assume that the min,max are compatible with the inc;
12024  * that is, (x_max - x_min)modulo(x_inc) ~= 0.0 within precision tolerances,
12025  * and similarly for y.  It is assumed that this is OK and that gmt_grd_RI_verify
12026  * was called during read grd and found to be OK.
12027  *
12028  * In the geographical case, if x_max - x_min < 360 we will use the default
12029  * boundary conditions, but if x_max - x_min >= 360 the 360 periodicity in x
12030  * will be used to set the x boundaries, and so we require 360modulo(x_inc)
12031  * == 0.0 within precision tolerance.  If y_max != 90 the north edge will
12032  * default, and similarly for y_min != -90.  If a y edge is at a pole and
12033  * x_max - x_min >= 360 then the geographical y uses a 180 degree phase
12034  * shift in the values, so we require 180modulo(x_inc) == 0.
12035  * Note that a grid-registered file will require that the entire row of
12036  * values representing a pole must all be equal, else d/dx != 0 which
12037  * is wrong.  So compatibility error-checking is built in.
12038  *
12039  * Note that a periodicity or polar boundary eliminates the need for
12040  * d2/dxdy = 0 at a corner.  There are no "corners" in those cases.
12041  *
12042  * Author:	W H F Smith
12043  * Date:	17 April 1998
12044  * Revised:	5  May 1998
12045  * Notes PW, April-2011: BCs are now set after a grid or image is read,
12046  * thus all programs load grids with all BCs set. Contents of old structs
12047  * GMT_BCR and GMT_EDGEINFO folded into GRDHEADER. Remaining functions
12048  * were renamed and are now
12049  * 	gmt_BC_init		- Determines and sets what BCs to use
12050  *	gmt_grd_BC_set		- Sets the BCs on a grid
12051  *	gmtlib_image_BC_set	- Sets the BCs on an image
12052  */
12053 
12054 /*! Initialize grid boundary conditions based on grid header and -n settings */
gmt_BC_init(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)12055 int gmt_BC_init (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
12056 	int i = 0, type;
12057 	bool same;
12058 	char *kind[5] = {"not set", "natural", "periodic", "geographic", "extended data"};
12059 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
12060 
12061 	if (HH->no_BC) return (GMT_NOERROR);	/* Told not to deal with BC stuff */
12062 
12063 	if (GMT->common.n.bc_set) {	/* Override BCs via -n+<BC> */
12064 		while (GMT->common.n.BC[i]) {
12065 			switch (GMT->common.n.BC[i]) {
12066 				case 'g':	/* Geographic sets everything */
12067 					HH->gn = HH->gs = true;
12068 					HH->BC[XLO] = HH->BC[XHI] = HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_GEO;
12069 					break;
12070 				case 'n':	/* Natural BCs */
12071 					if (GMT->common.n.BC[i+1] == 'x') { HH->BC[XLO] = HH->BC[XHI] = GMT_BC_IS_NATURAL; i++; }
12072 					else if (GMT->common.n.BC[i+1] == 'y') { HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_NATURAL; i++; }
12073 					else HH->BC[XLO] = HH->BC[XHI] = HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_NATURAL;
12074 					break;
12075 				case 'p':	/* Periodic BCs */
12076 					if (GMT->common.n.BC[i+1] == 'x') { HH->BC[XLO] = HH->BC[XHI] = GMT_BC_IS_PERIODIC; HH->nxp = 1; i++; }
12077 					else if (GMT->common.n.BC[i+1] == 'y') { HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_PERIODIC; HH->nyp = 1; i++; }
12078 					else { HH->BC[XLO] = HH->BC[XHI] = HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_PERIODIC; HH->nxp = HH->nyp = 1; }
12079 					break;
12080 				default:
12081 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot parse boundary condition %s\n", GMT->common.n.BC);
12082 					return (-1);
12083 					break;
12084 			}
12085 			i++;
12086 		}
12087 		if (HH->gn && !(HH->BC[XLO] == GMT_BC_IS_GEO && HH->BC[XHI] == GMT_BC_IS_GEO && HH->BC[YLO] == GMT_BC_IS_GEO && HH->BC[YHI] == GMT_BC_IS_GEO)) {
12088 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT boundary condition g overrides n[x|y] or p[x|y]\n");
12089 			HH->BC[XLO] = HH->BC[XHI] = HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_GEO;
12090 		}
12091 	}
12092 	else {	/* Determine BC based on whether grid is geographic or not */
12093 		type = (gmt_M_x_is_lon (GMT, GMT_IN)) ? GMT_BC_IS_GEO : GMT_BC_IS_NATURAL;
12094 		for (i = 0; i < 4; i++) if (HH->BC[i] == GMT_BC_IS_NOTSET) HH->BC[i] = type;
12095 	}
12096 
12097 	/* Check if geographic conditions can be used with this grid */
12098 	if (HH->gn && !gmt_grd_is_global (GMT, h)) {
12099 		/* User has requested geographical conditions, but grid is not global */
12100 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Longitude range too small; geographic boundary condition changed to natural.\n");
12101 		HH->nxp = HH->nyp = 0;
12102 		HH->gn  = HH->gs = false;
12103 		for (i = 0; i < 4; i++) if (HH->BC[i] == GMT_BC_IS_NOTSET) HH->BC[i] = GMT_BC_IS_NATURAL;
12104 	}
12105 	else if (gmt_grd_is_global (GMT, h)) {	/* Grid is truly global */
12106 		double xtest = fmod (180.0, h->inc[GMT_X]) * HH->r_inc[GMT_X];
12107 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Grid is considered to have a 360-degree longitude range.\n");
12108 		/* xtest should be within GMT_CONV4_LIMIT of zero or of one.  */
12109 		if (xtest > GMT_CONV4_LIMIT && xtest < (1.0 - GMT_CONV4_LIMIT) ) {
12110 			/* Error.  We need it to divide into 180 so we can phase-shift at poles.  */
12111 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "x_inc does not divide 180; geographic boundary condition changed to natural.\n");
12112 			HH->nxp = HH->nyp = 0;
12113 			HH->gn  = HH->gs = false;
12114 			for (i = 0; i < 4; i++) if (HH->BC[i] == GMT_BC_IS_NOTSET) HH->BC[i] = GMT_BC_IS_NATURAL;
12115 		}
12116 		else {
12117 			HH->nxp = urint (360.0 * HH->r_inc[GMT_X]);
12118 			HH->nyp = 0;
12119 			HH->gn = ((fabs(h->wesn[YHI] - 90.0)) < (GMT_CONV4_LIMIT * h->inc[GMT_Y]));
12120 			HH->gs = ((fabs(h->wesn[YLO] + 90.0)) < (GMT_CONV4_LIMIT * h->inc[GMT_Y]));
12121 			if (!HH->gs) HH->BC[YLO] = GMT_BC_IS_NATURAL;
12122 			if (!HH->gn) HH->BC[YHI] = GMT_BC_IS_NATURAL;
12123 		}
12124 	}
12125 	else {	/* Either periodic or natural */
12126 		if (HH->nxp != 0) HH->nxp = (h->registration == GMT_GRID_PIXEL_REG) ? h->n_columns : h->n_columns - 1;
12127 		if (HH->nyp != 0) HH->nyp = (h->registration == GMT_GRID_PIXEL_REG) ? h->n_rows : h->n_rows - 1;
12128 	}
12129 
12130 	if (HH->BC[XLO] == GMT_BC_IS_PERIODIC && HH->BC[XHI] == GMT_BC_IS_PERIODIC) {	/* Parameters needed for x-periodic, non-geographic grids */
12131 		GMT->common.n.periodic[GMT_X] = true;
12132 		GMT->common.n.range[GMT_X] = h->wesn[XHI] - h->wesn[XLO];
12133 		GMT->common.n.half_range[GMT_X] = 0.5 * GMT->common.n.range[GMT_X];
12134 	}
12135 	if (HH->BC[YLO] == GMT_BC_IS_PERIODIC && HH->BC[YHI] == GMT_BC_IS_PERIODIC) {	/* Parameters needed for y-periodic, non-geographic grids */
12136 		GMT->common.n.periodic[GMT_Y] = true;
12137 		GMT->common.n.range[GMT_Y] = h->wesn[YHI] - h->wesn[YLO];
12138 		GMT->common.n.half_range[GMT_Y] = 0.5 * GMT->common.n.range[GMT_Y];
12139 	}
12140 	for (i = 1, same = true; same && i < 4; i++) if (HH->BC[i] != HH->BC[i-1]) same = false;
12141 
12142 	if (same)
12143 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Chosen boundary condition for all edges: %s\n", kind[HH->BC[XLO]]);
12144 	else {
12145 		if (HH->BC[XLO] == HH->BC[XHI])
12146 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Chosen boundary condition for left and right edges: %s\n", kind[HH->BC[XLO]]);
12147 		else {
12148 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Chosen boundary condition for left   edge: %s\n", kind[HH->BC[XLO]]);
12149 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Chosen boundary condition for right  edge: %s\n", kind[HH->BC[XHI]]);
12150 		}
12151 		if (HH->BC[YLO] == HH->BC[YHI])
12152 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Chosen boundary condition for bottom and top edges: %s\n", kind[HH->BC[YLO]]);
12153 		else {
12154 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Chosen boundary condition for bottom edge: %s\n", kind[HH->BC[YLO]]);
12155 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Chosen boundary condition for top    edge: %s\n", kind[HH->BC[YHI]]);
12156 		}
12157 	}
12158 	/* Set this grid's interpolation parameters */
12159 
12160 	HH->bcr_interpolant = GMT->common.n.interpolant;
12161 	HH->bcr_threshold = GMT->common.n.threshold;
12162 	HH->bcr_n = (HH->bcr_interpolant == BCR_NEARNEIGHBOR) ? 1 : ((HH->bcr_interpolant == BCR_BILINEAR) ? 2 : 4);
12163 
12164 	return (GMT_NOERROR);
12165 }
12166 
12167 /*! . */
gmt_grd_BC_set(struct GMT_CTRL * GMT,struct GMT_GRID * G,unsigned int direction)12168 int gmt_grd_BC_set (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigned int direction) {
12169 	/* Set two rows of padding (pad[] can be larger) around data according
12170 	   to desired boundary condition info in that header.
12171 	   Returns -1 on problem, 0 on success.
12172 	   If either x or y is periodic, the padding is entirely set.
12173 	   However, if neither is true (this rules out geographical also)
12174 	   then all but three corner-most points in each corner are set.
12175 
12176 	   As written, not ready to use with "surface" for GMT 5, because
12177 	   assumes left/right is +/- 1 and down/up is +/- mx.  In "surface"
12178 	   the amount to move depends on the current mesh size, a parameter
12179 	   not used here.
12180 
12181 	   This is the revised, two-rows version (WHFS 6 May 1998).
12182 	*/
12183 
12184 	uint64_t mx;		/* Width of padded array; width as malloc'ed  */
12185 	uint64_t mxnyp;		/* distance to periodic constraint in j direction  */
12186 	uint64_t i, jmx;		/* Current i, j * mx  */
12187 	uint64_t nxp2;		/* 1/2 the xg period (180 degrees) in cells  */
12188 	uint64_t i180;		/* index to 180 degree phase shift  */
12189 	uint64_t iw, iwo1, iwo2, iwi1, ie, ieo1, ieo2, iei1;  /* see below  */
12190 	uint64_t jn, jno1, jno2, jni1, js, jso1, jso2, jsi1;  /* see below  */
12191 	uint64_t jno1k, jno2k, jso1k, jso2k, iwo1k, iwo2k, ieo1k, ieo2k;
12192 	uint64_t j1p, j2p;	/* j_o1 and j_o2 pole constraint rows  */
12193 	unsigned int n_skip, n_set;
12194 	unsigned int bok;		/* bok used to test that things are OK  */
12195 	bool set[4] = {true, true, true, true};
12196 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
12197 
12198 	char *kind[5] = {"not set", "natural", "periodic", "geographic", "extended data"};
12199 	char *edge[4] = {"left  ", "right ", "bottom", "top   "};
12200 
12201 	if (G->header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) return (GMT_NOERROR);	/* Only set up for real arrays */
12202 	if (HH->no_BC) return (GMT_NOERROR);	/* Told not to deal with BC stuff */
12203 	if (G->data == NULL) return (GMT_NOERROR);	/* Premature call; no grid data yet */
12204 
12205 	for (i = n_skip = 0; i < 4; i++) {
12206 		if (HH->BC[i] == GMT_BC_IS_DATA) {set[i] = false; n_skip++;}	/* No need to set since there is data in the pad area */
12207 	}
12208 	if (n_skip == 4) {	/* No need to set anything since there is data in the pad area on all sides */
12209 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: All boundaries set via extended data.\n");
12210 		return (GMT_NOERROR);
12211 	}
12212 
12213 	/* Check minimum size:  */
12214 	if (G->header->n_columns < 1 || G->header->n_rows < 1) {
12215 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Requires n_columns,n_rows at least 1.\n");
12216 		return (GMT_NOERROR);
12217 	}
12218 
12219 	/* Check that pad is at least 2 */
12220 	for (i = bok = 0; i < 4; i++) if (G->header->pad[i] < 2) bok++;
12221 	if (bok > 0) {
12222 		if (direction == GMT_IN) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Called with a pad < 2; skipped.\n");
12223 		return (GMT_NOERROR);
12224 	}
12225 
12226 	/* Initialize stuff:  */
12227 
12228 	mx = G->header->mx;
12229 	nxp2 = HH->nxp / 2;	/* Used for 180 phase shift at poles  */
12230 
12231 	iw = G->header->pad[XLO];	/* i for west-most data column */
12232 	iwo1 = iw - 1;		/* 1st column outside west  */
12233 	iwo2 = iwo1 - 1;	/* 2nd column outside west  */
12234 	iwi1 = iw + 1;		/* 1st column  inside west  */
12235 
12236 	ie = G->header->pad[XLO] + G->header->n_columns - 1;	/* i for east-most data column */
12237 	ieo1 = ie + 1;		/* 1st column outside east  */
12238 	ieo2 = ieo1 + 1;	/* 2nd column outside east  */
12239 	iei1 = ie - 1;		/* 1st column  inside east  */
12240 
12241 	jn = mx * G->header->pad[YHI];	/* j*mx for north-most data row  */
12242 	jno1 = jn - mx;		/* 1st row outside north  */
12243 	jno2 = jno1 - mx;	/* 2nd row outside north  */
12244 	jni1 = jn + mx;		/* 1st row  inside north  */
12245 
12246 	js = mx * (G->header->pad[YHI] + G->header->n_rows - 1);	/* j*mx for south-most data row  */
12247 	jso1 = js + mx;		/* 1st row outside south  */
12248 	jso2 = jso1 + mx;	/* 2nd row outside south  */
12249 	jsi1 = js - mx;		/* 1st row  inside south  */
12250 
12251 	mxnyp = mx * HH->nyp;
12252 
12253 	jno1k = jno1 + mxnyp;	/* data rows periodic to boundary rows  */
12254 	jno2k = jno2 + mxnyp;
12255 	jso1k = jso1 - mxnyp;
12256 	jso2k = jso2 - mxnyp;
12257 
12258 	iwo1k = iwo1 + HH->nxp;	/* data cols periodic to bndry cols  */
12259 	iwo2k = iwo2 + HH->nxp;
12260 	ieo1k = ieo1 - HH->nxp;
12261 	ieo2k = ieo2 - HH->nxp;
12262 
12263 	/* Duplicate rows and columns if n_columns or n_rows equals 1 */
12264 
12265 	if (G->header->n_columns == 1) for (i = jn+iw; i <= js+iw; i += mx) G->data[i-1] = G->data[i+1] = G->data[i];
12266 	if (G->header->n_rows == 1) for (i = jn+iw; i <= jn+ie; i++) G->data[i-mx] = G->data[i+mx] = G->data[i];
12267 
12268 	/* Check poles for grid case.  It would be nice to have done this
12269 		in GMT_boundcond_param_prep() but at that point the data
12270 		array isn't passed into that routine, and may not have been
12271 		read yet.  Also, as coded here, this bombs with error if
12272 		the pole data is wrong.  But there could be an option to
12273 		to change the condition to Natural in that case, with warning.  */
12274 
12275 	if (G->header->registration == GMT_GRID_NODE_REG) {	/* A pole can only be a grid node with gridline registration */
12276 		if (HH->gn) {	/* North pole case */
12277 			bok = 0;
12278 			if (gmt_M_is_fnan (G->data[jn + iw])) {	/* First is NaN so all should be NaN */
12279 				for (i = iw+1; i <= ie; i++) if (!gmt_M_is_fnan (G->data[jn + i])) bok++;
12280 			}
12281 			else {	/* First is not NaN so all should be identical */
12282 				for (i = iw+1; i <= ie; i++) if (G->data[jn + i] != G->data[jn + iw]) bok++;
12283 			}
12284 			if (bok > 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: %d (of %d) inconsistent grid values at North pole.\n", bok, G->header->n_columns);
12285 		}
12286 
12287 		if (HH->gs) {	/* South pole case */
12288 			bok = 0;
12289 			if (gmt_M_is_fnan (G->data[js + iw])) {	/* First is NaN so all should be NaN */
12290 				for (i = iw+1; i <= ie; i++) if (!gmt_M_is_fnan (G->data[js + i])) bok++;
12291 			}
12292 			else {	/* First is not NaN so all should be identical */
12293 				for (i = iw+1; i <= ie; i++) if (G->data[js + i] != G->data[js + iw]) bok++;
12294 			}
12295 			if (bok > 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: %d (of %d) inconsistent grid values at South pole.\n", bok, G->header->n_columns);
12296 		}
12297 	}
12298 
12299 	/* Start with the case that x is not periodic, because in that case we also know that y cannot be polar.  */
12300 
12301 	if (HH->nxp <= 0) {	/* x is not periodic  */
12302 
12303 		if (HH->nyp > 0) {	/* y is periodic  */
12304 
12305 			for (i = iw, bok = 0; i <= ie; ++i) {
12306 				if (G->header->registration == GMT_GRID_NODE_REG && !doubleAlmostEqualZero (G->data[jn+i], G->data[js+i]))
12307 					++bok;
12308 				if (set[YHI]) {
12309 					G->data[jno1 + i] = G->data[jno1k + i];
12310 					G->data[jno2 + i] = G->data[jno2k + i];
12311 				}
12312 				if (set[YLO]) {
12313 					G->data[jso1 + i] = G->data[jso1k + i];
12314 					G->data[jso2 + i] = G->data[jso2k + i];
12315 				}
12316 			}
12317 			if (bok > 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: %d (of %d) inconsistent grid values at South and North boundaries for repeated nodes.\n", bok, G->header->n_columns);
12318 
12319 			/* periodic Y rows copied.  Now do X naturals.
12320 				This is easy since y's are done; no corner problems.
12321 				Begin with Laplacian = 0, and include 1st outside rows
12322 				in loop, since y's already loaded to 2nd outside.  */
12323 
12324 			for (jmx = jno1; jmx <= jso1; jmx += mx) {
12325 				if (set[XLO]) G->data[jmx + iwo1] = (gmt_grdfloat)(4.0 * G->data[jmx + iw]) - (G->data[jmx + iw + mx] + G->data[jmx + iw - mx] + G->data[jmx + iwi1]);
12326 				if (set[XHI]) G->data[jmx + ieo1] = (gmt_grdfloat)(4.0 * G->data[jmx + ie]) - (G->data[jmx + ie + mx] + G->data[jmx + ie - mx] + G->data[jmx + iei1]);
12327 			}
12328 
12329 			/* Copy that result to 2nd outside row using periodicity.  */
12330 			if (set[XLO]) {
12331 				G->data[jno2 + iwo1] = G->data[jno2k + iwo1];
12332 				G->data[jso2 + iwo1] = G->data[jso2k + iwo1];
12333 			}
12334 			if (set[XHI]) {
12335 				G->data[jno2 + ieo1] = G->data[jno2k + ieo1];
12336 				G->data[jso2 + ieo1] = G->data[jso2k + ieo1];
12337 			}
12338 
12339 			/* Now set d[laplacian]/dx = 0 on 2nd outside column.  Include 1st outside rows in loop.  */
12340 			for (jmx = jno1; jmx <= jso1; jmx += mx) {
12341 				if (set[XLO]) G->data[jmx + iwo2] = (G->data[jmx + iw - mx] + G->data[jmx + iw + mx] + G->data[jmx + iwi1])
12342 					- (G->data[jmx + iwo1 - mx] + G->data[jmx + iwo1 + mx]) + (gmt_grdfloat)(5.0 * (G->data[jmx + iwo1] - G->data[jmx + iw]));
12343 				if (set[XHI]) G->data[jmx + ieo2] = (G->data[jmx + ie - mx] + G->data[jmx + ie + mx] + G->data[jmx + iei1])
12344 					- (G->data[jmx + ieo1 - mx] + G->data[jmx + ieo1 + mx]) + (gmt_grdfloat)(5.0 * (G->data[jmx + ieo1] - G->data[jmx + ie]));
12345 			}
12346 
12347 			/* Now copy that result also, for complete periodicity's sake  */
12348 			if (set[XLO]) {
12349 				G->data[jno2 + iwo2] = G->data[jno2k + iwo2];
12350 				G->data[jso2 + iwo2] = G->data[jso2k + iwo2];
12351 				HH->BC[XLO] = GMT_BC_IS_NATURAL;
12352 			}
12353 			if (set[XHI]) {
12354 				G->data[jno2 + ieo2] = G->data[jno2k + ieo2];
12355 				G->data[jso2 + ieo2] = G->data[jso2k + ieo2];
12356 				HH->BC[XHI] = GMT_BC_IS_NATURAL;
12357 			}
12358 
12359 			/* DONE with X not periodic, Y periodic case.  Fully loaded.  */
12360 			if (set[YLO] && set[YHI]) {
12361 				HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12362 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for bottom and top edges: %s\n", kind[HH->BC[YLO]]);
12363 			}
12364 			else if (set[YLO]) {
12365 				HH->BC[YLO] = GMT_BC_IS_PERIODIC;
12366 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
12367 			}
12368 			else if (set[YHI]) {
12369 				HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12370 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
12371 			}
12372 
12373 			return (GMT_NOERROR);
12374 		}
12375 		else {	/* Here begins the X not periodic, Y not periodic case  */
12376 
12377 			/* First, set corner points.  Need not merely Laplacian(f) = 0
12378 				but explicitly that d2f/dx2 = 0 and d2f/dy2 = 0.
12379 				Also set d2f/dxdy = 0.  Then can set remaining points.  */
12380 
12381 	/* d2/dx2 */	if (set[XLO]) G->data[jn + iwo1]   = (gmt_grdfloat)(2.0 * G->data[jn + iw] - G->data[jn + iwi1]);
12382 	/* d2/dy2 */	if (set[YHI]) G->data[jno1 + iw]   = (gmt_grdfloat)(2.0 * G->data[jn + iw] - G->data[jni1 + iw]);
12383 	/* d2/dxdy */	if (set[XLO] && set[YHI]) G->data[jno1 + iwo1] = G->data[jn + iwo1] + G->data[jno1 + iw] - G->data[jn + iw];
12384 
12385 	/* d2/dx2 */	if (set[XHI]) G->data[jn + ieo1]   = (gmt_grdfloat)(2.0 * G->data[jn + ie] - G->data[jn + iei1]);
12386 	/* d2/dy2 */	if (set[YHI]) G->data[jno1 + ie]   = (gmt_grdfloat)(2.0 * G->data[jn + ie] - G->data[jni1 + ie]);
12387 	/* d2/dxdy */	if (set[XHI] && set[YHI]) G->data[jno1 + ieo1] = G->data[jn + ieo1] + G->data[jno1 + ie] - G->data[jn + ie];
12388 
12389 	/* d2/dx2 */	if (set[XLO]) G->data[js + iwo1]   = (gmt_grdfloat)(2.0 * G->data[js + iw] - G->data[js + iwi1]);
12390 	/* d2/dy2 */	if (set[YLO]) G->data[jso1 + iw]   = (gmt_grdfloat)(2.0 * G->data[js + iw] - G->data[jsi1 + iw]);
12391 	/* d2/dxdy */	if (set[XLO] && set[YLO]) G->data[jso1 + iwo1] = G->data[js + iwo1] + G->data[jso1 + iw] - G->data[js + iw];
12392 
12393 	/* d2/dx2 */	if (set[XHI]) G->data[js + ieo1]   = (gmt_grdfloat)(2.0 * G->data[js + ie] - G->data[js + iei1]);
12394 	/* d2/dy2 */	if (set[YLO]) G->data[jso1 + ie]   = (gmt_grdfloat)(2.0 * G->data[js + ie] - G->data[jsi1 + ie]);
12395 	/* d2/dxdy */	if (set[XHI] && set[YLO]) G->data[jso1 + ieo1] = G->data[js + ieo1] + G->data[jso1 + ie] - G->data[js + ie];
12396 
12397 			/* Now set Laplacian = 0 on interior edge points, skipping corners:  */
12398 			for (i = iwi1; i <= iei1; i++) {
12399 				if (set[YHI]) G->data[jno1 + i] = (gmt_grdfloat)(4.0 * G->data[jn + i]) - (G->data[jn + i - 1] + G->data[jn + i + 1] + G->data[jni1 + i]);
12400 				if (set[YLO]) G->data[jso1 + i] = (gmt_grdfloat)(4.0 * G->data[js + i]) - (G->data[js + i - 1] + G->data[js + i + 1] + G->data[jsi1 + i]);
12401 			}
12402 			for (jmx = jni1; jmx <= jsi1; jmx += mx) {
12403 				if (set[XLO]) G->data[iwo1 + jmx] = (gmt_grdfloat)(4.0 * G->data[iw + jmx]) - (G->data[iw + jmx + mx] + G->data[iw + jmx - mx] + G->data[iwi1 + jmx]);
12404 				if (set[XHI]) G->data[ieo1 + jmx] = (gmt_grdfloat)(4.0 * G->data[ie + jmx]) - (G->data[ie + jmx + mx] + G->data[ie + jmx - mx] + G->data[iei1 + jmx]);
12405 			}
12406 
12407 			/* Now set d[Laplacian]/dn = 0 on all edge pts, including
12408 				corners, since the points needed in this are now set.  */
12409 			for (i = iw; i <= ie; i++) {
12410 				if (set[YHI]) G->data[jno2 + i] = G->data[jni1 + i] + (gmt_grdfloat)(5.0 * (G->data[jno1 + i] - G->data[jn + i]))
12411 					+ (G->data[jn + i - 1] - G->data[jno1 + i - 1]) + (G->data[jn + i + 1] - G->data[jno1 + i + 1]);
12412 				if (set[YLO]) G->data[jso2 + i] = G->data[jsi1 + i] + (gmt_grdfloat)(5.0 * (G->data[jso1 + i] - G->data[js + i]))
12413 					+ (G->data[js + i - 1] - G->data[jso1 + i - 1]) + (G->data[js + i + 1] - G->data[jso1 + i + 1]);
12414 			}
12415 			for (jmx = jn; jmx <= js; jmx += mx) {
12416 				if (set[XLO]) G->data[iwo2 + jmx] = G->data[iwi1 + jmx] + (gmt_grdfloat)(5.0 * (G->data[iwo1 + jmx] - G->data[iw + jmx]))
12417 					+ (G->data[iw + jmx - mx] - G->data[iwo1 + jmx - mx]) + (G->data[iw + jmx + mx] - G->data[iwo1 + jmx + mx]);
12418 				if (set[XHI]) G->data[ieo2 + jmx] = G->data[iei1 + jmx] + (gmt_grdfloat)(5.0 * (G->data[ieo1 + jmx] - G->data[ie + jmx]))
12419 					+ (G->data[ie + jmx - mx] - G->data[ieo1 + jmx - mx]) + (G->data[ie + jmx + mx] - G->data[ieo1 + jmx + mx]);
12420 			}
12421 			/* DONE with X not periodic, Y not periodic case.  Loaded all but three corner-most points at each corner.  */
12422 
12423 			for (i = n_set = 0; i < 4; i++) if (set[i]) {
12424 				n_set++;
12425 				HH->BC[i] = GMT_BC_IS_NATURAL;
12426 			}
12427 			if (n_set == 4) {
12428 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for all edges: %s\n", kind[HH->BC[XLO]]);
12429 			}
12430 			for (i = 0; i < 4; i++) if (set[i]) {
12431 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[i], kind[HH->BC[i]]);
12432 			}
12433 			return (GMT_NOERROR);
12434 		}
12435 		/* DONE with all X not periodic cases  */
12436 	}
12437 	else {	/* X is periodic.  Load x cols first, then do Y cases.  */
12438 		bool check_repeat = (G->header->registration == GMT_GRID_NODE_REG && HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT);
12439 		if (set[XLO]) HH->BC[XLO] = GMT_BC_IS_PERIODIC;
12440 		if (set[XHI]) HH->BC[XHI] = GMT_BC_IS_PERIODIC;
12441 		for (jmx = jn, bok = 0; jmx <= js; jmx += mx) {
12442 			if (check_repeat && !doubleAlmostEqualZero (G->data[jmx+iw], G->data[jmx+ie]))
12443 				++bok;
12444 			if (set[XLO]) {
12445 				G->data[iwo1 + jmx] = G->data[iwo1k + jmx];
12446 				G->data[iwo2 + jmx] = G->data[iwo2k + jmx];
12447 			}
12448 			if (set[XHI]) {
12449 				G->data[ieo1 + jmx] = G->data[ieo1k + jmx];
12450 				G->data[ieo2 + jmx] = G->data[ieo2k + jmx];
12451 			}
12452 		}
12453 		if (bok > 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: %d (of %d) inconsistent grid values at West and East boundaries for repeated nodes.\n", bok, G->header->n_rows);
12454 
12455 		if (HH->nyp > 0) {	/* Y is periodic.  copy all, including boundary cols:  */
12456 			for (i = iwo2, bok = 0; i <= ieo2; ++i) {
12457 				if (G->header->registration == GMT_GRID_NODE_REG && !doubleAlmostEqualZero (G->data[jn+i], G->data[js+i]))
12458 					++bok;
12459 				if (set[YHI]) {
12460 					G->data[jno1 + i] = G->data[jno1k + i];
12461 					G->data[jno2 + i] = G->data[jno2k + i];
12462 				}
12463 				if (set[YLO]) {
12464 					G->data[jso1 + i] = G->data[jso1k + i];
12465 					G->data[jso2 + i] = G->data[jso2k + i];
12466 				}
12467 			}
12468 			if (bok > 0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: %d (of %d) inconsistent grid values at South and North boundaries for repeated nodes.\n", bok, G->header->n_columns);
12469 			/* DONE with X and Y both periodic.  Fully loaded.  */
12470 
12471 			if (set[YLO] && set[YHI]) {
12472 				HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12473 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for bottom and top edges: %s\n", kind[HH->BC[YLO]]);
12474 			}
12475 			else if (set[YLO]) {
12476 				HH->BC[YLO] = GMT_BC_IS_PERIODIC;
12477 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
12478 			}
12479 			else if (set[YHI]) {
12480 				HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12481 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
12482 			}
12483 			return (GMT_NOERROR);
12484 		}
12485 
12486 		/* Do north (top) boundary:  */
12487 
12488 		if (HH->gn) {	/* Y is at north pole.  Phase-shift all, incl. bndry cols. */
12489 			if (G->header->registration == GMT_GRID_PIXEL_REG) {
12490 				j1p = jn;	/* constraint for jno1  */
12491 				j2p = jni1;	/* constraint for jno2  */
12492 			}
12493 			else {
12494 				j1p = jni1;		/* constraint for jno1  */
12495 				j2p = jni1 + mx;	/* constraint for jno2  */
12496 			}
12497 			for (i = iwo2; set[YHI] && i <= ieo2; i++) {
12498 				i180 = G->header->pad[XLO] + ((i + nxp2)%HH->nxp);
12499 				G->data[jno1 + i] = G->data[j1p + i180];
12500 				G->data[jno2 + i] = G->data[j2p + i180];
12501 			}
12502 			if (set[YHI]) {
12503 				HH->BC[YHI] = GMT_BC_IS_GEO;
12504 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
12505 			}
12506 		}
12507 		else {
12508 			/* Y needs natural conditions.  x bndry cols periodic.
12509 				First do Laplacian.  Start/end loop 1 col outside,
12510 				then use periodicity to set 2nd col outside.  */
12511 
12512 			for (i = iwo1; set[YHI] && i <= ieo1; i++) {
12513 				G->data[jno1 + i] = (gmt_grdfloat)(4.0 * G->data[jn + i]) - (G->data[jn + i - 1] + G->data[jn + i + 1] + G->data[jni1 + i]);
12514 			}
12515 			if (set[XLO] && set[YHI]) G->data[jno1 + iwo2] = G->data[jno1 + iwo2 + HH->nxp];
12516 			if (set[XHI] && set[YHI]) G->data[jno1 + ieo2] = G->data[jno1 + ieo2 - HH->nxp];
12517 
12518 
12519 			/* Now set d[Laplacian]/dn = 0, start/end loop 1 col out,
12520 				use periodicity to set 2nd out col after loop.  */
12521 
12522 			for (i = iwo1; set[YHI] && i <= ieo1; i++) {
12523 				G->data[jno2 + i] = G->data[jni1 + i] + (gmt_grdfloat)(5.0 * (G->data[jno1 + i] - G->data[jn + i]))
12524 					+ (G->data[jn + i - 1] - G->data[jno1 + i - 1]) + (G->data[jn + i + 1] - G->data[jno1 + i + 1]);
12525 			}
12526 			if (set[XLO] && set[YHI]) G->data[jno2 + iwo2] = G->data[jno2 + iwo2 + HH->nxp];
12527 			if (set[XHI] && set[YHI]) G->data[jno2 + ieo2] = G->data[jno2 + ieo2 - HH->nxp];
12528 
12529 			/* End of X is periodic, north (top) is Natural.  */
12530 			if (set[YHI]) {
12531 				HH->BC[YHI] = GMT_BC_IS_NATURAL;
12532 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
12533 			}
12534 		}
12535 
12536 		/* Done with north (top) BC in X is periodic case.  Do south (bottom)  */
12537 
12538 		if (HH->gs) {	/* Y is at south pole.  Phase-shift all, incl. bndry cols. */
12539 			if (G->header->registration == GMT_GRID_PIXEL_REG) {
12540 				j1p = js;	/* constraint for jso1  */
12541 				j2p = jsi1;	/* constraint for jso2  */
12542 			}
12543 			else {
12544 				j1p = jsi1;		/* constraint for jso1  */
12545 				j2p = jsi1 - mx;	/* constraint for jso2  */
12546 			}
12547 			for (i = iwo2; set[YLO] && i <= ieo2; i++) {
12548 				i180 = G->header->pad[XLO] + ((i + nxp2)%HH->nxp);
12549 				G->data[jso1 + i] = G->data[j1p + i180];
12550 				G->data[jso2 + i] = G->data[j2p + i180];
12551 			}
12552 			if (set[YLO]) {
12553 				HH->BC[YLO] = GMT_BC_IS_GEO;
12554 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
12555 			}
12556 		}
12557 		else {
12558 			/* Y needs natural conditions.  x bndry cols periodic.
12559 				First do Laplacian.  Start/end loop 1 col outside,
12560 				then use periodicity to set 2nd col outside.  */
12561 
12562 			for (i = iwo1; set[YLO] && i <= ieo1; i++) {
12563 				G->data[jso1 + i] = (gmt_grdfloat)(4.0 * G->data[js + i]) - (G->data[js + i - 1] + G->data[js + i + 1] + G->data[jsi1 + i]);
12564 			}
12565 			if (set[XLO] && set[YLO]) G->data[jso1 + iwo2] = G->data[jso1 + iwo2 + HH->nxp];
12566 			if (set[XHI] && set[YHI]) G->data[jso1 + ieo2] = G->data[jso1 + ieo2 - HH->nxp];
12567 
12568 
12569 			/* Now set d[Laplacian]/dn = 0, start/end loop 1 col out,
12570 				use periodicity to set 2nd out col after loop.  */
12571 
12572 			for (i = iwo1; set[YLO] && i <= ieo1; i++) {
12573 				G->data[jso2 + i] = G->data[jsi1 + i] + (gmt_grdfloat)(5.0 * (G->data[jso1 + i] - G->data[js + i]))
12574 					+ (G->data[js + i - 1] - G->data[jso1 + i - 1]) + (G->data[js + i + 1] - G->data[jso1 + i + 1]);
12575 			}
12576 			if (set[XLO] && set[YLO]) G->data[jso2 + iwo2] = G->data[jso2 + iwo2 + HH->nxp];
12577 			if (set[XHI] && set[YHI]) G->data[jso2 + ieo2] = G->data[jso2 + ieo2 - HH->nxp];
12578 
12579 			/* End of X is periodic, south (bottom) is Natural.  */
12580 			if (set[YLO]) {
12581 				HH->BC[YLO] = GMT_BC_IS_NATURAL;
12582 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
12583 			}
12584 		}
12585 
12586 		/* Done with X is periodic cases.  */
12587 
12588 		return (GMT_NOERROR);
12589 	}
12590 }
12591 
12592 /* Clipper to ensure a byte stays in 0-255 range */
12593 #if 1
gmtsupport_clip_to_byte(int byte)12594 static inline unsigned char gmtsupport_clip_to_byte (int byte) { if (byte < 0) return (0); else if (byte > 255) return (255); else return ((unsigned char)byte);}
12595 #else
12596 /* For debugging BC actions for images when the result is outside byte range  */
gmtsupport_clip_to_byte(int byte)12597 unsigned char gmtsupport_clip_to_byte (int byte) {
12598 	if (byte < 0) {
12599 		fprintf (stderr, "byte = %d\n", byte);
12600 		return (0);
12601 	}
12602 	if (byte > 255) {
12603 		fprintf (stderr, "byte = %d\n", byte);
12604 		return (255);
12605 	}
12606 	return ((unsigned char)byte);
12607 }
12608 #endif
12609 
12610 /*! . */
gmtlib_image_BC_set(struct GMT_CTRL * GMT,struct GMT_IMAGE * I)12611 int gmtlib_image_BC_set (struct GMT_CTRL *GMT, struct GMT_IMAGE *I) {
12612 	/* Set two rows of padding (pad[] can be larger) around data according
12613 	   to desired boundary condition info in edgeinfo.
12614 	   Returns -1 on problem, 0 on success.
12615 	   If either x or y is periodic, the padding is entirely set.
12616 	   However, if neither is true (this rules out geographical also)
12617 	   then all but three corner-most points in each corner are set.
12618 
12619 	   As written, not ready to use with "surface" for GMT v4, because
12620 	   assumes left/right is +/- 1 and down/up is +/- mx.  In "surface"
12621 	   the amount to move depends on the current mesh size, a parameter
12622 	   not used here.
12623 
12624 	   This is the revised, two-rows version (WHFS 6 May 1998).
12625 	   Based on GMT_boundcont_set but extended to multiple bands and using char * array
12626 	*/
12627 
12628 	int byte;
12629 	uint64_t mx;		/* Width of padded array; width as malloc'ed  */
12630 	uint64_t mxnyp;		/* distance to periodic constraint in j direction  */
12631 	uint64_t i, jmx;	/* Current i, j * mx  */
12632 	uint64_t nxp2;		/* 1/2 the xg period (180 degrees) in cells  */
12633 	uint64_t i180;		/* index to 180 degree phase shift  */
12634 	uint64_t iw, iwo1, iwo2, iwi1, ie, ieo1, ieo2, iei1;  /* see below  */
12635 	uint64_t jn, jno1, jno2, jni1, js, jso1, jso2, jsi1;  /* see below  */
12636 	uint64_t jno1k, jno2k, jso1k, jso2k, iwo1k, iwo2k, ieo1k, ieo2k;
12637 	uint64_t j1p, j2p;	/* j_o1 and j_o2 pole constraint rows  */
12638 	unsigned int n_skip, n_set;
12639 	unsigned int b, nb = I->header->n_bands;
12640 	unsigned int bok;		/* bok used to test that things are OK  */
12641 	bool set[4] = {true, true, true, true};
12642 	char *kind[5] = {"not set", "natural", "periodic", "geographic", "extended data"};
12643 	char *edge[4] = {"left  ", "right ", "bottom", "top   "};
12644 	unsigned char *img = NULL;
12645 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
12646 
12647 	if (I->header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) return (GMT_NOERROR);	/* Only set up for real arrays */
12648 
12649 	for (i = n_skip = 0; i < 4; i++) {
12650 		if (HH->BC[i] == GMT_BC_IS_DATA) {set[i] = false; n_skip++;}	/* No need to set since there is data in the pad area */
12651 	}
12652 	if (n_skip == 4) return (GMT_NOERROR);	/* No need to set anything since there is data in the pad area on all sides */
12653 	if (I->data == NULL) return (GMT_NOERROR);	/* Premature call; no image data yet */
12654 
12655 	/* Check minimum size:  */
12656 	if (I->header->n_columns < 1 || I->header->n_rows < 1) {
12657 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtlib_image_BC_set: Requires n_columns,n_rows at least 1.\n");
12658 		return (-1);
12659 	}
12660 
12661 	/* Check if pad is requested */
12662 	if (I->header->pad[0] < 2 ||  I->header->pad[1] < 2 ||  I->header->pad[2] < 2 ||  I->header->pad[3] < 2) {
12663 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtlib_image_BC_set: Pad not large enough for BC assignments; no BCs applied\n");
12664 		return (GMT_NOERROR);
12665 	}
12666 
12667 	/* Initialize stuff:  */
12668 
12669 	img = I->data;	/* Just a shorthand to keep expressions shorter */
12670 	mx = I->header->mx;
12671 	nxp2 = HH->nxp / 2;	/* Used for 180 phase shift at poles  */
12672 
12673 	iw = I->header->pad[XLO];	/* i for west-most data column */
12674 	iwo1 = iw - 1;		/* 1st column outside west  */
12675 	iwo2 = iwo1 - 1;	/* 2nd column outside west  */
12676 	iwi1 = iw + 1;		/* 1st column  inside west  */
12677 
12678 	ie = I->header->pad[XLO] + I->header->n_columns - 1;	/* i for east-most data column */
12679 	ieo1 = ie + 1;		/* 1st column outside east  */
12680 	ieo2 = ieo1 + 1;	/* 2nd column outside east  */
12681 	iei1 = ie - 1;		/* 1st column  inside east  */
12682 
12683 	jn = mx * I->header->pad[YHI];	/* j*mx for north-most data row  */
12684 	jno1 = jn - mx;		/* 1st row outside north  */
12685 	jno2 = jno1 - mx;	/* 2nd row outside north  */
12686 	jni1 = jn + mx;		/* 1st row  inside north  */
12687 
12688 	js = mx * (I->header->pad[YHI] + I->header->n_rows - 1);	/* j*mx for south-most data row  */
12689 	jso1 = js + mx;		/* 1st row outside south  */
12690 	jso2 = jso1 + mx;	/* 2nd row outside south  */
12691 	jsi1 = js - mx;		/* 1st row  inside south  */
12692 
12693 	mxnyp = mx * HH->nyp;
12694 
12695 	jno1k = jno1 + mxnyp;	/* data rows periodic to boundary rows  */
12696 	jno2k = jno2 + mxnyp;
12697 	jso1k = jso1 - mxnyp;
12698 	jso2k = jso2 - mxnyp;
12699 
12700 	iwo1k = iwo1 + HH->nxp;	/* data cols periodic to bndry cols  */
12701 	iwo2k = iwo2 + HH->nxp;
12702 	ieo1k = ieo1 - HH->nxp;
12703 	ieo2k = ieo2 - HH->nxp;
12704 
12705 	/* Duplicate rows and columns if n_columns or n_rows equals 1 */
12706 
12707 	if (I->header->n_columns == 1) for (i = jn+iw; i <= js+iw; i += mx) img[i-1] = img[i+1] = img[i];
12708 	if (I->header->n_rows == 1) for (i = jn+iw; i <= jn+ie; i++) img[i-mx] = img[i+mx] = img[i];
12709 
12710 	/* Check poles for grid case.  It would be nice to have done this
12711 		in GMT_boundcond_param_prep() but at that point the data
12712 		array isn't passed into that routine, and may not have been
12713 		read yet.  Also, as coded here, this bombs with error if
12714 		the pole data is wrong.  But there could be an option to
12715 		to change the condition to Natural in that case, with warning.  */
12716 
12717 	if (I->header->registration == GMT_GRID_NODE_REG) {	/* A pole can only be a grid node with gridline registration */
12718 		if (HH->gn) {	/* North pole case */
12719 			bok = 0;
12720 			for (i = iw+1; i <= ie; i++) for (b = 0; b < nb; b++) if (img[nb*(jn + i)+b] != img[nb*(jn + iw)+b]) bok++;
12721 			if (bok > 0) GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmtlib_image_BC_set: Inconsistent image values at North pole.\n");
12722 		}
12723 		if (HH->gs) {	/* South pole case */
12724 			bok = 0;
12725 			for (i = iw+1; i <= ie; i++) for (b = 0; b < nb; b++) if (img[nb*(js + i)+b] != img[nb*(js + iw)+b]) bok++;
12726 			if (bok > 0) GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmtlib_image_BC_set: Inconsistent grid values at South pole.\n");
12727 		}
12728 	}
12729 
12730 	/* Start with the case that x is not periodic, because in that case we also know that y cannot be polar.  */
12731 
12732 	if (HH->nxp <= 0) {	/* x is not periodic  */
12733 
12734 		if (HH->nyp > 0) {	/* y is periodic  */
12735 
12736 			for (i = iw; i <= ie; i++) {
12737 				for (b = 0; b < nb; b++) {
12738 					if (set[YHI]) {
12739 						img[nb*(jno1 + i)+b] = img[nb*(jno1k + i)+b];
12740 						img[nb*(jno2 + i)+b] = img[nb*(jno2k + i)+b];
12741 					}
12742 					if (set[YLO]) {
12743 						img[nb*(jso1 + i)+b] = img[nb*(jso1k + i)+b];
12744 						img[nb*(jso2 + i)+b] = img[nb*(jso2k + i)+b];
12745 					}
12746 				}
12747 			}
12748 
12749 			/* periodic Y rows copied.  Now do X naturals.
12750 				This is easy since y's are done; no corner problems.
12751 				Begin with Laplacian = 0, and include 1st outside rows
12752 				in loop, since y's already loaded to 2nd outside.  */
12753 
12754 			for (jmx = jno1; jmx <= jso1; jmx += mx) {
12755 				for (b = 0; b < nb; b++) {
12756 					if (set[XLO]) {
12757 						byte = irint (4.0 * img[nb*(jmx + iw)+b]) - (img[nb*(jmx + iw + mx)+b] + img[nb*(jmx + iw - mx)+b] + img[nb*(jmx + iwi1)+b]);
12758 						img[nb*(jmx + iwo1)+b] = gmtsupport_clip_to_byte (byte);
12759 					}
12760 					if (set[XHI]) {
12761 						byte = irint (4.0 * img[nb*(jmx + ie)+b]) - (img[nb*(jmx + ie + mx)+b] + img[nb*(jmx + ie - mx)+b] + img[nb*(jmx + iei1)+b]);
12762 						img[nb*(jmx + ieo1)+b] = gmtsupport_clip_to_byte (byte);
12763 					}
12764 				}
12765 			}
12766 
12767 			/* Copy that result to 2nd outside row using periodicity.  */
12768 			for (b = 0; b < nb; b++) {
12769 				if (set[XLO]) {
12770 					img[nb*(jno2 + iwo1)+b] = img[nb*(jno2k + iwo1)+b];
12771 					img[nb*(jso2 + iwo1)+b] = img[nb*(jso2k + iwo1)+b];
12772 				}
12773 				if (set[XHI]) {
12774 					img[nb*(jno2 + ieo1)+b] = img[nb*(jno2k + ieo1)+b];
12775 					img[nb*(jso2 + ieo1)+b] = img[nb*(jso2k + ieo1)+b];
12776 				}
12777 			}
12778 
12779 			/* Now set d[laplacian]/dx = 0 on 2nd outside column.  Include 1st outside rows in loop.  */
12780 			for (jmx = jno1; jmx <= jso1; jmx += mx) {
12781 				for (b = 0; b < nb; b++) {
12782 					if (set[XLO]) {
12783 						byte = irint ((img[nb*(jmx + iw - mx)+b] + img[nb*(jmx + iw + mx)+b] + img[nb*(jmx + iwi1)+b])
12784 							- (img[nb*(jmx + iwo1 - mx)+b] + img[nb*(jmx + iwo1 + mx)+b]) + (5.0 * (img[nb*(jmx + iwo1)+b] - img[nb*(jmx + iw)+b])));
12785 						img[nb*(jmx + iwo2)+b] = gmtsupport_clip_to_byte (byte);
12786 					}
12787 					if (set[XHI]) {
12788 						byte = irint ((img[nb*(jmx + ie - mx)+b] + img[nb*(jmx + ie + mx)+b] + img[nb*(jmx + iei1)+b])
12789 							- (img[nb*(jmx + ieo1 - mx)+b] + img[nb*(jmx + ieo1 + mx)+b]) + (5.0 * (img[nb*(jmx + ieo1)+b] - img[nb*(jmx + ie)+b])));
12790 						img[nb*(jmx + ieo2)+b] = gmtsupport_clip_to_byte (byte);
12791 					}
12792 				}
12793 			}
12794 
12795 			/* Now copy that result also, for complete periodicity's sake  */
12796 			for (b = 0; b < nb; b++) {
12797 				if (set[XLO]) {
12798 					img[nb*(jno2 + iwo2)+b] = img[nb*(jno2k + iwo2)+b];
12799 					img[nb*(jso2 + iwo2)+b] = img[nb*(jso2k + iwo2)+b];
12800 					HH->BC[XLO] = GMT_BC_IS_NATURAL;
12801 				}
12802 				if (set[XHI]) {
12803 					img[nb*(jno2 + ieo2)+b] = img[nb*(jno2k + ieo2)+b];
12804 					img[nb*(jso2 + ieo2)+b] = img[nb*(jso2k + ieo2)+b];
12805 					HH->BC[XHI] = GMT_BC_IS_NATURAL;
12806 				}
12807 			}
12808 
12809 			/* DONE with X not periodic, Y periodic case.  Fully loaded.  */
12810 			if (set[YLO] && set[YHI]) {
12811 				HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12812 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for bottom and top edge: %s\n", kind[HH->BC[YLO]]);
12813 			}
12814 			else if (set[YLO]) {
12815 				HH->BC[YLO] = GMT_BC_IS_PERIODIC;
12816 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
12817 			}
12818 			else if (set[YHI]) {
12819 				HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12820 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
12821 			}
12822 
12823 			return (GMT_NOERROR);
12824 		}
12825 		else {	/* Here begins the X not periodic, Y not periodic case  */
12826 
12827 			/* First, set corner points.  Need not merely Laplacian(f) = 0
12828 				but explicitly that d2f/dx2 = 0 and d2f/dy2 = 0.
12829 				Also set d2f/dxdy = 0.  Then can set remaining points.  */
12830 
12831 			for (b = 0; b < nb; b++) {
12832 				if (set[XLO]) {	/* d2/dx2 */
12833 					byte = irint (2.0 * img[nb*(jn + iw)+b] - img[nb*(jn + iwi1)+b]);
12834 					img[nb*(jn + iwo1)+b]   = gmtsupport_clip_to_byte (byte);
12835 				}
12836 				if (set[YHI]) {	/* d2/dy2 */
12837 					byte = irint (2.0 * img[nb*(jn + iw)+b] - img[nb*(jni1 + iw)+b]);
12838 					img[nb*(jno1 + iw)+b]   = gmtsupport_clip_to_byte (byte);
12839 				}
12840 				if (set[XLO] && set[YHI]) {	/* d2/dxdy */
12841 					byte = irint (img[nb*(jn + iwo1)+b] + img[nb*(jno1 + iw)+b] - img[nb*(jn + iw)+b]);
12842 					img[nb*(jno1 + iwo1)+b] = gmtsupport_clip_to_byte (byte);
12843 				}
12844 				if (set[XHI]) {	/* d2/dx2 */
12845 					byte = irint (2.0 * img[nb*(jn + ie)+b] - img[nb*(jn + iei1)+b]);
12846 					img[nb*(jn + ieo1)+b]   = gmtsupport_clip_to_byte (byte);
12847 				}
12848 				if (set[YHI]) {	/* d2/dy2 */
12849 					byte = irint (2.0 * img[nb*(jn + ie)+b] - img[nb*(jni1 + ie)+b]);
12850 					img[nb*(jno1 + ie)+b]   = gmtsupport_clip_to_byte (byte);
12851 				}
12852 				if (set[XHI] && set[YHI]) {	/* d2/dxdy */
12853 					byte = irint (img[nb*(jn + ieo1)+b] + img[nb*(jno1 + ie)+b] - img[nb*(jn + ie)+b]);
12854 					img[nb*(jno1 + ieo1)+b] = gmtsupport_clip_to_byte (byte);
12855 				}
12856 				if (set[XLO]) {	/* d2/dx2 */
12857 					byte = irint (2.0 * img[nb*(js + iw)+b] - img[nb*(js + iwi1)+b]);
12858 					img[nb*(js + iwo1)+b]   = gmtsupport_clip_to_byte (byte);
12859 				}
12860 				if (set[YLO]) {	/* d2/dy2 */
12861 					byte = irint (2.0 * img[nb*(js + iw)+b] - img[nb*(jsi1 + iw)+b]);
12862 					img[nb*(jso1 + iw)+b]   = gmtsupport_clip_to_byte (byte);
12863 				}
12864 				if (set[XLO] && set[YLO]) {	/* d2/dxdy */
12865 					byte = irint (img[nb*(js + iwo1)+b] + img[nb*(jso1 + iw)+b]  - img[nb*(js + iw)+b]);
12866 					img[nb*(jso1 + iwo1)+b] = gmtsupport_clip_to_byte (byte);
12867 				}
12868 				if (set[XHI]) {	/* d2/dx2 */
12869 					byte = irint (2.0 * img[nb*(js + ie)+b] - img[nb*(js + iei1)+b]);
12870 					img[nb*(js + ieo1)+b]   = gmtsupport_clip_to_byte (byte);
12871 				}
12872 				if (set[YLO]) {	/* d2/dy2 */
12873 					byte = irint (2.0 * img[nb*(js + ie)+b] - img[nb*(jsi1 + ie)+b]);
12874 					img[nb*(jso1 + ie)+b]   = gmtsupport_clip_to_byte (byte);
12875 				}
12876 				if (set[XHI] && set[YLO]) {	/* d2/dxdy */
12877 					byte = irint (img[nb*(js + ieo1)] + img[nb*(jso1 + ie)] - img[nb*(js + ie)+b]);
12878 					img[nb*(jso1 + ieo1)+b] = gmtsupport_clip_to_byte (byte);
12879 				}
12880 			}
12881 
12882 			/* Now set Laplacian = 0 on interior edge points, skipping corners:  */
12883 			for (i = iwi1; i <= iei1; i++) {
12884 				for (b = 0; b < nb; b++) {
12885 					if (set[YHI]) {
12886 						byte = irint (4.0 * img[nb*(jn + i)+b]) - (img[nb*(jn + i - 1)+b] + img[nb*(jn + i + 1)+b] + img[nb*(jni1 + i)+b]);
12887 						img[nb*(jno1 + i)+b] = gmtsupport_clip_to_byte (byte);
12888 					}
12889 					if (set[YLO]) {
12890 						byte = irint (4.0 * img[nb*(js + i)+b]) - (img[nb*(js + i - 1)+b] + img[nb*(js + i + 1)+b] + img[nb*(jsi1 + i)+b]);
12891 						img[nb*(jso1 + i)+b] = gmtsupport_clip_to_byte (byte);
12892 					}
12893 				}
12894 			}
12895 			for (jmx = jni1; jmx <= jsi1; jmx += mx) {
12896 				for (b = 0; b < nb; b++) {
12897 					if (set[XLO]) {
12898 						byte = irint (4.0 * img[nb*(iw + jmx)+b]) - (img[nb*(iw + jmx + mx)+b] + img[nb*(iw + jmx - mx)+b] + img[nb*(iwi1 + jmx)+b]);
12899 						img[nb*(iwo1 + jmx)+b] = gmtsupport_clip_to_byte (byte);
12900 					}
12901 					if (set[XHI]) {
12902 						byte = irint (4.0 * img[nb*(ie + jmx)+b]) - (img[nb*(ie + jmx + mx)+b] + img[nb*(ie + jmx - mx)+b] + img[nb*(iei1 + jmx)+b]);
12903 						img[nb*(ieo1 + jmx)+b] = gmtsupport_clip_to_byte (byte);
12904 					}
12905 				}
12906 			}
12907 
12908 			/* Now set d[Laplacian]/dn = 0 on all edge pts, including
12909 				corners, since the points needed in this are now set.  */
12910 			for (i = iw; i <= ie; i++) {
12911 				for (b = 0; b < nb; b++) {
12912 					if (set[YHI]) {
12913 						byte = irint (img[nb*(jni1 + i)+b] + (5.0 * (img[nb*(jno1 + i)+b] - img[nb*(jn + i)+b]))
12914 							+ (img[nb*(jn + i - 1)+b] - img[nb*(jno1 + i - 1)+b]) + (img[nb*(jn + i + 1)+b] - img[nb*(jno1 + i + 1)+b]));
12915 						img[nb*(jno2 + i)+b] = gmtsupport_clip_to_byte (byte);
12916 					}
12917 					if (set[YLO]) {
12918 						byte = irint (img[nb*(jsi1 + i)+b] + (5.0 * (img[nb*(jso1 + i)+b] - img[nb*(js + i)+b]))
12919 							+ (img[nb*(js + i - 1)+b] - img[nb*(jso1 + i - 1)+b]) + (img[nb*(js + i + 1)+b] - img[nb*(jso1 + i + 1)+b]));
12920 						img[nb*(jso2 + i)+b] = gmtsupport_clip_to_byte (byte);
12921 					}
12922 				}
12923 			}
12924 			for (jmx = jn; jmx <= js; jmx += mx) {
12925 				for (b = 0; b < nb; b++) {
12926 					if (set[XLO]) {
12927 						byte = irint (img[nb*(iwi1 + jmx)+b] + (5.0 * (img[nb*(iwo1 + jmx)+b] - img[nb*(iw + jmx)+b]))
12928 							+ (img[nb*(iw + jmx - mx)+b] - img[nb*(iwo1 + jmx - mx)+b]) + (img[nb*(iw + jmx + mx)+b] - img[nb*(iwo1 + jmx + mx)+b]));
12929 						img[nb*(iwo2 + jmx)+b] = gmtsupport_clip_to_byte (byte);
12930 					}
12931 					if (set[XHI]) {
12932 						byte = irint (img[nb*(iei1 + jmx)+b] + (5.0 * (img[nb*(ieo1 + jmx)+b] - img[nb*(ie + jmx)+b]))
12933 							+ (img[nb*(ie + jmx - mx)+b] - img[nb*(ieo1 + jmx - mx)+b]) + (img[nb*(ie + jmx + mx)+b] - img[nb*(ieo1 + jmx + mx)+b]));
12934 						img[nb*(ieo2 + jmx)+b] = gmtsupport_clip_to_byte (byte);
12935 					}
12936 				}
12937 			}
12938 			/* DONE with X not periodic, Y not periodic case.  Loaded all but three cornermost points at each corner.  */
12939 
12940 			for (i = n_set = 0; i < 4; i++) if (set[i]) {
12941 				HH->BC[i] = GMT_BC_IS_NATURAL;
12942 				n_set++;
12943 			}
12944 			if (n_set == 4)
12945 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for all edges: %s\n", kind[HH->BC[XLO]]);
12946 			else {
12947 				for (i = 0; i < 4; i++) if (set[i]) {
12948 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[i], kind[HH->BC[i]]);
12949 				}
12950 			}
12951 			return (GMT_NOERROR);
12952 		}
12953 		/* DONE with all X not periodic cases  */
12954 	}
12955 	else {	/* X is periodic.  Load x cols first, then do Y cases.  */
12956 		if (set[XLO]) HH->BC[XLO] = GMT_BC_IS_PERIODIC;
12957 		if (set[XHI]) HH->BC[XHI] = GMT_BC_IS_PERIODIC;
12958 
12959 		for (jmx = jn; jmx <= js; jmx += mx) {
12960 			for (b = 0; b < nb; b++) {
12961 				if (set[XLO]) {
12962 					img[nb*(iwo1 + jmx)+b] = img[nb*(iwo1k + jmx)+b];
12963 					img[nb*(iwo2 + jmx)+b] = img[nb*(iwo2k + jmx)+b];
12964 				}
12965 				if (set[XHI]) {
12966 					img[nb*(ieo1 + jmx)+b] = img[nb*(ieo1k + jmx)+b];
12967 					img[nb*(ieo2 + jmx)+b] = img[nb*(ieo2k + jmx)+b];
12968 				}
12969 			}
12970 		}
12971 
12972 		if (HH->nyp > 0) {	/* Y is periodic.  copy all, including boundary cols:  */
12973 			for (i = iwo2; i <= ieo2; i++) {
12974 				for (b = 0; b < nb; b++) {
12975 					if (set[YHI]) {
12976 						img[nb*(jno1 + i)+b] = img[nb*(jno1k + i)+b];
12977 						img[nb*(jno2 + i)+b] = img[nb*(jno2k + i)+b];
12978 					}
12979 					if (set[YLO]) {
12980 						img[nb*(jso1 + i)+b] = img[nb*(jso1k + i)+b];
12981 						img[nb*(jso2 + i)+b] = img[nb*(jso2k + i)+b];
12982 					}
12983 				}
12984 			}
12985 			/* DONE with X and Y both periodic.  Fully loaded.  */
12986 
12987 			if (set[YLO] && set[YHI]) {
12988 				HH->BC[YLO] = HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12989 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for bottom and top edge: %s\n", kind[HH->BC[YLO]]);
12990 			}
12991 			else if (set[YLO]) {
12992 				HH->BC[YLO] = GMT_BC_IS_PERIODIC;
12993 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
12994 			}
12995 			else if (set[YHI]) {
12996 				HH->BC[YHI] = GMT_BC_IS_PERIODIC;
12997 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
12998 			}
12999 			return (GMT_NOERROR);
13000 		}
13001 
13002 		/* Do north (top) boundary:  */
13003 
13004 		if (HH->gn) {	/* Y is at north pole.  Phase-shift all, incl. bndry cols. */
13005 			if (I->header->registration == GMT_GRID_PIXEL_REG) {
13006 				j1p = jn;	/* constraint for jno1  */
13007 				j2p = jni1;	/* constraint for jno2  */
13008 			}
13009 			else {
13010 				j1p = jni1;		/* constraint for jno1  */
13011 				j2p = jni1 + mx;	/* constraint for jno2  */
13012 			}
13013 			for (i = iwo2; set[YHI] && i <= ieo2; i++) {
13014 				i180 = I->header->pad[XLO] + ((i + nxp2)%HH->nxp);
13015 				for (b = 0; b < nb; b++) {
13016 					img[nb*(jno1 + i)+b] = img[nb*(j1p + i180)+b];
13017 					img[nb*(jno2 + i)+b] = img[nb*(j2p + i180)+b];
13018 				}
13019 			}
13020 			if (set[YHI]) {
13021 				HH->BC[YHI] = GMT_BC_IS_GEO;
13022 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
13023 			}
13024 		}
13025 		else {
13026 			/* Y needs natural conditions.  x bndry cols periodic.
13027 				First do Laplacian.  Start/end loop 1 col outside,
13028 				then use periodicity to set 2nd col outside.  */
13029 
13030 			for (i = iwo1; set[YHI] && i <= ieo1; i++) {
13031 				for (b = 0; b < nb; b++) {
13032 					byte = irint ((4.0 * img[nb*(jn + i)+b]) - (img[nb*(jn + i - 1)+b] + img[nb*(jn + i + 1)+b] + img[nb*(jni1 + i)+b]));
13033 					img[nb*(jno1 + i)+b] = gmtsupport_clip_to_byte (byte);
13034 				}
13035 			}
13036 			for (b = 0; b < nb; b++) {
13037 				if (set[XLO] && set[YHI]) img[nb*(jno1 + iwo2)+b] = img[nb*(jno1 + iwo2 + HH->nxp)+b];
13038 				if (set[XHI] && set[YHI]) img[nb*(jno1 + ieo2)+b] = img[nb*(jno1 + ieo2 - HH->nxp)+b];
13039 			}
13040 
13041 			/* Now set d[Laplacian]/dn = 0, start/end loop 1 col out,
13042 				use periodicity to set 2nd out col after loop.  */
13043 
13044 			for (i = iwo1; set[YHI] && i <= ieo1; i++) {
13045 				for (b = 0; b < nb; b++) {
13046 					byte = irint (img[nb*(jni1 + i)+b] + (5.0 * (img[nb*(jno1 + i)+b] - img[nb*(jn + i)+b]))
13047 						+ (img[nb*(jn + i - 1)+b] - img[nb*(jno1 + i - 1)+b]) + (img[nb*(jn + i + 1)+b] - img[nb*(jno1 + i + 1)+b]));
13048 					img[nb*(jno2 + i)+b] = gmtsupport_clip_to_byte (byte);
13049 				}
13050 			}
13051 			for (b = 0; b < nb; b++) {
13052 				if (set[XLO] && set[YHI]) img[nb*(jno2 + iwo2)+b] = img[nb*(jno2 + iwo2 + HH->nxp)+b];
13053 				if (set[XHI] && set[YHI]) img[nb*(jno2 + ieo2)+b] = img[nb*(jno2 + ieo2 - HH->nxp)+b];
13054 			}
13055 
13056 			/* End of X is periodic, north (top) is Natural.  */
13057 			if (set[YHI]) {
13058 				HH->BC[YHI] = GMT_BC_IS_NATURAL;
13059 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YHI], kind[HH->BC[YHI]]);
13060 			}
13061 		}
13062 
13063 		/* Done with north (top) BC in X is periodic case.  Do south (bottom)  */
13064 
13065 		if (HH->gs) {	/* Y is at south pole.  Phase-shift all, incl. bndry cols. */
13066 			if (I->header->registration == GMT_GRID_PIXEL_REG) {
13067 				j1p = js;	/* constraint for jso1  */
13068 				j2p = jsi1;	/* constraint for jso2  */
13069 			}
13070 			else {
13071 				j1p = jsi1;		/* constraint for jso1  */
13072 				j2p = jsi1 - mx;	/* constraint for jso2  */
13073 			}
13074 			for (i = iwo2; set[YLO] && i <= ieo2; i++) {
13075 				i180 = I->header->pad[XLO] + ((i + nxp2)%HH->nxp);
13076 				for (b = 0; b < nb; b++) {
13077 					img[nb*(jso1 + i)+b] = img[nb*(j1p + i180)+b];
13078 					img[nb*(jso2 + i)+b] = img[nb*(j2p + i180)+b];
13079 				}
13080 			}
13081 			if (set[YLO]) {
13082 				HH->BC[YLO] = GMT_BC_IS_GEO;
13083 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
13084 			}
13085 		}
13086 		else {
13087 			/* Y needs natural conditions.  x bndry cols periodic.
13088 				First do Laplacian.  Start/end loop 1 col outside,
13089 				then use periodicity to set 2nd col outside.  */
13090 
13091 			for (i = iwo1; set[YLO] && i <= ieo1; i++) {
13092 				for (b = 0; b < nb; b++) {
13093 					byte = irint ((4.0 * img[nb*(js + i)+b]) - (img[nb*(js + i - 1)+b] + img[nb*(js + i + 1)+b] + img[nb*(jsi1 + i)+b]));
13094 					img[nb*(jso1 + i)+b] = gmtsupport_clip_to_byte (byte);
13095 				}
13096 			}
13097 			for (b = 0; b < nb; b++) {
13098 				if (set[XLO] && set[YLO]) img[nb*(jso1 + iwo2)+b] = img[nb*(jso1 + iwo2 + HH->nxp)+b];
13099 				if (set[XHI] && set[YHI]) img[nb*(jso1 + ieo2)+b] = img[nb*(jso1 + ieo2 - HH->nxp)+b];
13100 			}
13101 
13102 
13103 			/* Now set d[Laplacian]/dn = 0, start/end loop 1 col out,
13104 				use periodicity to set 2nd out col after loop.  */
13105 
13106 			for (i = iwo1; set[YLO] && i <= ieo1; i++) {
13107 				for (b = 0; b < nb; b++) {
13108 					byte = irint (img[nb*(jsi1 + i)+b] + (5.0 * (img[nb*(jso1 + i)+b] - img[nb*(js + i)+b]))
13109 						+ (img[nb*(js + i - 1)+b] - img[nb*(jso1 + i - 1)+b]) + (img[nb*(js + i + 1)+b] - img[nb*(jso1 + i + 1)+b]));
13110 					img[nb*(jso2 + i)+b] = gmtsupport_clip_to_byte (byte);
13111 				}
13112 			}
13113 			for (b = 0; b < nb; b++) {
13114 				if (set[XLO] && set[YLO]) img[nb*(jso2 + iwo2)+b] = img[nb*(jso2 + iwo2 + HH->nxp)+b];
13115 				if (set[XHI] && set[YHI]) img[nb*(jso2 + ieo2)+b] = img[nb*(jso2 + ieo2 - HH->nxp)+b];
13116 			}
13117 
13118 			/* End of X is periodic, south (bottom) is Natural.  */
13119 			if (set[YLO]) {
13120 				HH->BC[YLO] = GMT_BC_IS_NATURAL;
13121 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmtlib_image_BC_set: Set boundary condition for %s edge: %s\n", edge[YLO], kind[HH->BC[YLO]]);
13122 			}
13123 		}
13124 
13125 		/* Done with X is periodic cases.  */
13126 
13127 		return (GMT_NOERROR);
13128 	}
13129 }
13130 
gmt_cube_BC_set(struct GMT_CTRL * GMT,struct GMT_CUBE * U,unsigned int direction)13131 int gmt_cube_BC_set (struct GMT_CTRL *GMT, struct GMT_CUBE *U, unsigned int direction) {
13132 	int error = GMT_NOERROR;
13133 	unsigned int k;
13134 	struct GMT_GRID *G = gmt_create_grid (GMT);	/* Create a dummy temporary grid structure */
13135 
13136 	gmt_copy_gridheader (GMT, G->header, U->header);
13137 
13138 	for (k = 0; k < U->header->n_bands; k++) {	/* Do each layer BC separately */
13139 		G->data = &(U->data[k*U->header->size]);	/* Start of next 2-D layer */
13140 		if (gmt_M_err_pass (GMT, gmt_grd_BC_set (GMT, G, direction), "Cube memory")) {	/* Set boundary conditions */
13141 			error = GMT_GRID_BC_ERROR;
13142 			goto cube_clean_up;
13143 		}
13144 	}
13145 
13146 cube_clean_up:
13147 	G->data = NULL;
13148 	gmt_free_grid (GMT, &G, true);
13149 
13150 	return (error);
13151 }
13152 
13153 /*! . */
gmt_y_out_of_bounds(struct GMT_CTRL * GMT,int * j,struct GMT_GRID_HEADER * h,bool * wrap_180)13154 bool gmt_y_out_of_bounds (struct GMT_CTRL *GMT, int *j, struct GMT_GRID_HEADER *h, bool *wrap_180) {
13155 	/* Adjusts the j (y-index) value if we are dealing with some sort of periodic boundary
13156 	* condition.  If a north or south pole condition we must "go over the pole" and access
13157 	* the longitude 180 degrees away - this is achieved by passing the wrap_180 flag; the
13158 	* shifting of longitude is then deferred to gmt_x_out_of_bounds.
13159 	* If no periodicities are present then nothing happens here.  If we end up being
13160 	* out of bounds we return true (and main program can take action like continue);
13161 	* otherwise we return false.
13162 	* Note: *j may be negative on input.
13163 	*/
13164 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
13165 
13166 	gmt_M_unused(GMT);
13167 	if ((*j) < 0) {	/* Depending on BC's we wrap around or we are above the top of the domain */
13168 		if (HH->gn) {	/* N Polar condition - adjust j and set wrap flag */
13169 			(*j) = abs (*j) - h->registration;
13170 			(*wrap_180) = true;	/* Go "over the pole" */
13171 		}
13172 		else if (HH->nyp) {	/* Periodic in y */
13173 			(*j) += HH->nyp;
13174 			(*wrap_180) = false;
13175 		}
13176 		else
13177 			return (true);	/* We are outside the range */
13178 	}
13179 	else if ((*j) >= (int)h->n_rows) {	/* Depending on BC's we wrap around or we are below the bottom of the domain */
13180 		if (HH->gs) {	/* S Polar condition - adjust j and set wrap flag */
13181 			(*j) += h->registration - 2;
13182 			(*wrap_180) = true;	/* Go "over the pole" */
13183 		}
13184 		else if (HH->nyp) {	/* Periodic in y */
13185 			(*j) -= HH->nyp;
13186 			(*wrap_180) = false;
13187 		}
13188 		else
13189 			return (true);	/* We are outside the range */
13190 	}
13191 	else
13192 		(*wrap_180) = false;
13193 
13194 	return (false);	/* OK, we are inside grid now for sure */
13195 }
13196 
13197 /*! . */
gmt_x_out_of_bounds(struct GMT_CTRL * GMT,int * i,struct GMT_GRID_HEADER * h,bool wrap_180)13198 bool gmt_x_out_of_bounds (struct GMT_CTRL *GMT, int *i, struct GMT_GRID_HEADER *h, bool wrap_180) {
13199 	/* Adjusts the i (x-index) value if we are dealing with some sort of periodic boundary
13200 	* condition.  If a north or south pole condition we must "go over the pole" and access
13201 	* the longitude 180 degrees away - this is achieved by examining the wrap_180 flag and take action.
13202 	* If no periodicities are present and we end up being out of bounds we return true (and
13203 	* main program can take action like continue); otherwise we return false.
13204 	* Note: *i may be negative on input.
13205 	*/
13206 	struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
13207 
13208 	/* Depending on BC's we wrap around or leave as is. */
13209 
13210 	gmt_M_unused(GMT);
13211 	if ((*i) < 0) {	/* Potentially outside to the left of the domain */
13212 		if (HH->nxp)	/* Periodic in x - always inside grid */
13213 			(*i) += HH->nxp;
13214 		else	/* Sorry, you're outside */
13215 			return (true);
13216 	}
13217 	else if ((*i) >= (int)h->n_columns) {	/* Potentially outside to the right of the domain */
13218 		if (HH->nxp)	/* Periodic in x -always inside grid */
13219 			(*i) -= HH->nxp;
13220 		else	/* Sorry, you're outside */
13221 			return (true);
13222 	}
13223 
13224 	if (wrap_180) (*i) = ((*i) + (HH->nxp / 2)) % HH->nxp;	/* Must move 180 degrees */
13225 
13226 	return (false);	/* OK, we are inside grid now for sure */
13227 }
13228 
13229 /*! . */
gmt_row_col_out_of_bounds(struct GMT_CTRL * GMT,double * in,struct GMT_GRID_HEADER * h,unsigned int * row,unsigned int * col)13230 bool gmt_row_col_out_of_bounds (struct GMT_CTRL *GMT, double *in, struct GMT_GRID_HEADER *h, unsigned int *row, unsigned int *col) {
13231 	/* Return false and pass back unsigned row,col if inside region, or return true (outside) */
13232 	int signed_row, signed_col;
13233 	gmt_M_unused(GMT);
13234 	signed_row = (int)gmt_M_grd_y_to_row (GMT, in[GMT_Y], h);
13235 	if (signed_row < 0) return (true);
13236 	signed_col = (int)gmt_M_grd_x_to_col (GMT, in[GMT_X], h);
13237 	if (signed_col < 0) return (true);
13238 	*row = signed_row;
13239 	if (*row >= h->n_rows) return (true);
13240 	*col = signed_col;
13241 	if (*col >= h->n_columns) return (true);
13242 	return (false);	/* Inside the node region */
13243 }
13244 
13245 /*! . */
gmt_set_xy_domain(struct GMT_CTRL * GMT,double wesn_extended[],struct GMT_GRID_HEADER * h)13246 void gmt_set_xy_domain (struct GMT_CTRL *GMT, double wesn_extended[], struct GMT_GRID_HEADER *h) {
13247 	double off;
13248 	/* Sets the domain boundaries to be used to determine if x,y coordinates
13249 	 * are outside the domain of a grid.  If gridline-registered then the
13250 	 * domain is extended by 0.5 the grid interval.  Note that points with
13251 	 * x == x_max and y == y_max are considered inside.
13252 	 */
13253 
13254 	off = 0.5 * (1 - h->registration);
13255 	if (gmt_M_x_is_lon (GMT, GMT_IN) && gmt_grd_is_global (GMT, h))	/* Global longitude range */
13256 		wesn_extended[XLO] = h->wesn[XLO], wesn_extended[XHI] = h->wesn[XHI];
13257 	else
13258 		wesn_extended[XLO] = h->wesn[XLO] - off * h->inc[GMT_X], wesn_extended[XHI] = h->wesn[XHI] + off * h->inc[GMT_X];
13259 	/* Latitudes can be extended provided we are not at the poles */
13260 	wesn_extended[YLO] = h->wesn[YLO] - off * h->inc[GMT_Y], wesn_extended[YHI] = h->wesn[YHI] + off * h->inc[GMT_Y];
13261 	if (gmt_M_y_is_lat (GMT, GMT_IN)) {
13262 		if (wesn_extended[YLO] < -90.0) wesn_extended[YLO] = -90.0;
13263 		if (wesn_extended[YHI] > +90.0) wesn_extended[YHI] = +90.0;
13264 	}
13265 }
13266 
13267 /*! . */
gmt_x_is_outside(struct GMT_CTRL * GMT,double * x,double left,double right)13268 bool gmt_x_is_outside (struct GMT_CTRL *GMT, double *x, double left, double right) {
13269 	/* Determines if this x is inside the effective x-domain.  This is normally
13270 	 * west to east, but when gridding is concerned it can be extended by +-0.5 * dx
13271 	 * for gridline-registered grids.  Also, if x is longitude we must check for
13272 	 * wrap-arounds by 360 degrees, and x may be modified accordingly.
13273 	 */
13274 	if (gmt_M_is_dnan (*x)) return (true);
13275 	if (gmt_M_x_is_lon (GMT, GMT_IN)) {	/* Periodic longitude test */
13276 		while ((*x) > left) (*x) -= 360.0;	/* Make sure we start west or west */
13277 		while ((*x) < left) (*x) += 360.0;	/* See if we are outside east */
13278 		return (((*x) > right) ? true : false);
13279 	}
13280 	else	/* Cartesian test */
13281 		return (((*x) < left || (*x) > right) ? true : false);
13282 }
13283 
13284 /*! . */
gmt_getinset(struct GMT_CTRL * GMT,char option,char * in_text,struct GMT_MAP_INSET * B)13285 int gmt_getinset (struct GMT_CTRL *GMT, char option, char *in_text, struct GMT_MAP_INSET *B) {
13286 	/* Parse the map inset option, which comes in two flavors:
13287 	 * 1) -D<xmin/xmax/ymin/ymax>[+r][+s<file>][+u<unit>]
13288 	 * 2) -Dg|j|J|n|x<refpoint>+w<width>[<u>][/<height>[<u>]][+j<justify>][+o<dx>[/<dy>]][+s<file>]
13289 	 *    Note: the [+s<file>] is only valid in classic mode (via psbasemap)
13290 	 *
13291 	 * For backwards compatibility we also check the deprecated form of (1):
13292 	 *    [<unit>]<xmin/xmax/ymin/ymax>
13293 	 */
13294 	unsigned int col_type[2], k = 0, error = 0;
13295 	int n;
13296 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, txt_d[GMT_LEN256] = {""};
13297 	char string[GMT_BUFSIZ] = {""}, text[GMT_BUFSIZ] = {""}, oldshit[GMT_LEN128] = {""};
13298 	struct GMT_MAP_PANEL *save_panel = B->panel;	/* In case it was set and we wipe it below with gmt_M_memset */
13299 
13300 	if (!in_text || in_text[0] == '\0') {
13301 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No argument given\n", option);
13302 		return GMT_PARSE_ERROR;
13303 	}
13304 	gmt_M_memset (B, 1, struct GMT_MAP_INSET);
13305 	B->panel = save_panel;	/* In case it is not NULL */
13306 
13307 	if (gmtsupport_ensure_new_mapinset_syntax (GMT, option, in_text, text, oldshit)) return (1);	/* This recasts any old syntax using new syntax and gives a warning */
13308 
13309 	/* Determine if we got an reference point or a region */
13310 
13311 	if (strchr (GMT_REFPOINT_CODES, text[0])) {	/* Did the reference point thing. */
13312 		/* Syntax is -Dg|j|J|n|x<refpoint>+w<width>[/<height>][+j<justify>][+o<dx>[/<dy>]][+s<file>], with +s<file> only in classic mode */
13313 		unsigned int last;
13314 		char *q[2] = {NULL, NULL};
13315 		size_t len;
13316 		if ((B->refpoint = gmt_get_refpoint (GMT, text, option)) == NULL) {
13317 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Map inset reference point was not accepted\n", option);
13318 			gmt_refpoint_syntax (GMT, "D", NULL, GMT_ANCHOR_INSET, 1);
13319 			return (1);	/* Failed basic parsing */
13320 		}
13321 
13322 		if (gmt_validate_modifiers (GMT, B->refpoint->args, option, "jostw", GMT_MSG_ERROR)) return (1);
13323 
13324 		/* Reference point args are +w<width>[/<height>][+j<justify>][+o<dx>[/<dy>]][+s<file>][+t]. */
13325 		/* Required modifier +w */
13326 		if (gmt_get_modifier (B->refpoint->args, 'w', string)) {
13327 			if (string[0] == '\0') {	/* Got nutin' */
13328 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No dimensions given to +w modifier\n", option);
13329 				error++;
13330 			}
13331 			else {	/* Gave some arguments */
13332 				n = sscanf (string, "%[^/]/%s", txt_a, txt_b);
13333 				/* First deal with inset dimensions and horizontal vs vertical */
13334 				/* Handle either <unit><width>/<height> or <width>/<height> */
13335 				q[GMT_X] = txt_a;	q[GMT_Y] = txt_b;
13336 				last = (n == 1) ? GMT_X : GMT_Y;
13337 				for (k = GMT_X; k <= last; k++) {
13338 					len = strlen (q[k]) - 1;
13339 					if (strchr (GMT_LEN_UNITS2, q[k][len])) {	/* Got dimensions in these units */
13340 						B->unit = q[k][len];
13341 						q[k][len] = 0;
13342 					}
13343 					B->dim[k] = (B->unit) ? atof (q[k]) : gmt_M_to_inch (GMT, q[k]);
13344 				}
13345 				if (last == GMT_X) B->dim[GMT_Y] = B->dim[GMT_X];
13346 			}
13347 		}
13348 		/* Optional modifiers +j, +o, +s */
13349 		if (gmt_get_modifier (B->refpoint->args, 'j', string))	{	/* Got justification of item w.r.t. reference point */
13350 			if (string[0] == '\0') {	/* Got nutin' */
13351 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No justification argument given to +j modifier\n", option);
13352 				error++;
13353 			}
13354 			else
13355 				B->justify = gmt_just_decode (GMT, string, PSL_BL);
13356 		}
13357 		else	/* With -Dj or -DJ, set default to reference justify point, else BL */
13358 			B->justify = gmt_M_just_default (GMT, B->refpoint, PSL_BL);
13359 		if (gmt_get_modifier (B->refpoint->args, 'o', string)) {	/* Got offsets from reference point */
13360 			if ((n = gmt_get_pair (GMT, string, GMT_PAIR_DIM_DUP, B->off)) < 0) {
13361 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Failed to parse offset arguments for +o modifier\n", option);
13362 				error++;
13363 			}
13364 		}
13365 		if (gmt_get_modifier (B->refpoint->args, 's', string)) {
13366 			if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Not valid in modern node */
13367 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  +s modifier is valid in classic mode only\n", option);
13368 				error++;
13369 			}
13370 			else if (string[0] == '\0') {	/* Got nutin' */
13371 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No filename given to +s modifier\n", option);
13372 				error++;
13373 			}
13374 			else
13375 				B->file = strdup (string);
13376 		}
13377 		if (gmt_get_modifier (B->refpoint->args, 't', string))
13378 				B->translate = true;
13379 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Map inset attributes: justify = %d, dx = %g dy = %g\n", B->justify, B->off[GMT_X], B->off[GMT_Y]);
13380 	}
13381 	else {	/* Did the [<unit>]<xmin/xmax/ymin/ymax> thing - this is exact so justify, offsets do not apply. */
13382 		char *c = NULL, p[GMT_LEN128] = {""};
13383 		unsigned int pos;
13384 		/* Syntax is -D<xmin/xmax/ymin/ymax>[+s<file>][+t][+u<unit>] or old -D[<unit>]<xmin/xmax/ymin/ymax>[+s<file>][+t] */
13385 		if ((c = gmt_first_modifier (GMT, text, "rsu"))) {
13386 			/* Syntax is -D<xmin/xmax/ymin/ymax>[+r][+s<file>][+t][+u<unit>] */
13387 			pos = 0;	/* Reset to start of new word */
13388 			while (gmt_getmodopt (GMT, option, c, "rstu", &pos, p, &error) && error == 0) {
13389 				switch (p[0]) {
13390 					case 'r': B->oblique = true;	break;
13391 					case 's':
13392 						if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Not valid in modern node */
13393 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  +s modifier is valid in classic mode only\n", option);
13394 							error++;
13395 						}
13396 						else
13397 							B->file = strdup (&p[1]);
13398 						break;
13399 					case 't': B->translate = true;	break;
13400 					case 'u': B->unit = p[1]; break;
13401 					default: break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
13402 				}
13403 			}
13404 			c[0] = '\0';	/* Chop off all modifiers so other items can be determined */
13405 		}
13406 		else if (strchr (GMT_LEN_UNITS2, text[0])) {	/* Deprecated args: Got a rectangular region given in these units */
13407 			/* -D<unit>]<xmin/xmax/ymin/ymax> */
13408 			B->unit = text[0];
13409 			k = 1;
13410 		}
13411 		/* Decode the w/e/s/n part */
13412 		if ((n = sscanf (&text[k], "%[^/]/%[^/]/%[^/]/%s", txt_a, txt_b, txt_c, txt_d)) != 4) {
13413 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Must specify w/e/s/n or <unit>xmin/xmax/ymin/ymax\n", option);
13414 			return (GMT_PARSE_ERROR);
13415 		}
13416 		if (c) c[0] = '+';	/* Restore original argument */
13417 		col_type[GMT_X] = gmt_M_type (GMT, GMT_IN, GMT_X);	/* Set correct input types */
13418 		col_type[GMT_Y] = gmt_M_type (GMT, GMT_IN, GMT_Y);
13419 		if (k == 0) {	/* Got geographic w/e/s/n or <w/s/e/n>r */
13420 			n = (int)strlen(txt_d) - 1;
13421 			if (B->oblique || txt_d[n] == 'r') {	/* Got <w/s/e/n>r for rectangular box */
13422 				if (!B->oblique) txt_d[n] = '\0';
13423 				error += gmt_verify_expectations (GMT, col_type[GMT_X], gmt_scanf (GMT, txt_a, col_type[GMT_X], &B->wesn[XLO]), txt_a);
13424 				error += gmt_verify_expectations (GMT, col_type[GMT_Y], gmt_scanf (GMT, txt_b, col_type[GMT_Y], &B->wesn[YLO]), txt_b);
13425 				error += gmt_verify_expectations (GMT, col_type[GMT_X], gmt_scanf (GMT, txt_c, col_type[GMT_X], &B->wesn[XHI]), txt_c);
13426 				error += gmt_verify_expectations (GMT, col_type[GMT_Y], gmt_scanf (GMT, txt_d, col_type[GMT_Y], &B->wesn[YHI]), txt_d);
13427 				if (!B->oblique) txt_d[n] = 'r';
13428 				B->oblique = true;
13429 			}
13430 			else {	/* Got  w/e/s/n for box that follows meridians and parallels, which may or may not be rectangular */
13431 				error += gmt_verify_expectations (GMT, col_type[GMT_X], gmt_scanf (GMT, txt_a, col_type[GMT_X], &B->wesn[XLO]), txt_a);
13432 				error += gmt_verify_expectations (GMT, col_type[GMT_X], gmt_scanf (GMT, txt_b, col_type[GMT_X], &B->wesn[XHI]), txt_b);
13433 				error += gmt_verify_expectations (GMT, col_type[GMT_Y], gmt_scanf (GMT, txt_c, col_type[GMT_Y], &B->wesn[YLO]), txt_c);
13434 				error += gmt_verify_expectations (GMT, col_type[GMT_Y], gmt_scanf (GMT, txt_d, col_type[GMT_Y], &B->wesn[YHI]), txt_d);
13435 			}
13436 		}
13437 		else {	/* Got projected xmin/xmax/ymin/ymax for rectangular box */
13438 			B->wesn[XLO] = atof (txt_a);	B->wesn[XHI] = atof (txt_b);
13439 			B->wesn[YLO] = atof (txt_c);	B->wesn[YHI] = atof (txt_d);
13440 		}
13441 	}
13442 
13443 	B->plot = true;
13444 	if (oldshit[0] && gmt_getpanel (GMT, 'F', oldshit, &(B->panel))) {
13445 		gmt_mappanel_syntax (GMT, 'F', "Specify the rectangular panel attributes for map inset", 3);
13446 		error++;
13447 	}
13448 	return (error);
13449 }
13450 
gmt_getscale(struct GMT_CTRL * GMT,char option,char * text,struct GMT_MAP_SCALE * ms)13451 int gmt_getscale (struct GMT_CTRL *GMT, char option, char *text, struct GMT_MAP_SCALE *ms) {
13452 	/* This function parses the -L map scale syntax:
13453 	 *   -L[g|j|J|n|x]<refpoint>+c[/<slon>]/<slat>+w<length>[e|f|M|n|k|u][+a<align>][+f][+j<just>][+l<label>][+u]
13454 	 * If the required +w is not present we call the backwards compatible parsert for the previous map scale syntax.
13455 	 * An optional background panel is handled by a separate option (typically -F). */
13456 
13457 	int error = 0, n;
13458 	bool vertical = false;
13459 	char string[GMT_BUFSIZ] = {""};
13460 	struct GMT_MAP_PANEL *save_panel = ms->panel;	/* In case it was set and we wipe it below with gmt_M_memset */
13461 
13462 	if (!text || text[0] == '\0') {
13463 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No argument given\n", option);
13464 		return GMT_PARSE_ERROR;
13465 	}
13466 	vertical = ms->vertical;
13467 	if (!strstr (text, "+w")) return gmtsupport_getscale_old (GMT, option, text, ms);	/* Old-style args */
13468 
13469 	gmt_M_memset (ms, 1, struct GMT_MAP_SCALE);
13470 	ms->panel = save_panel;	/* In case it is not NULL */
13471 	ms->measure = 'k';	/* Default distance unit is km */
13472 	ms->alignment = (vertical) ? 'r' : 't';	/* Default label placement is on top for map scale and right for vertical scale */
13473 
13474 	if ((ms->refpoint = gmt_get_refpoint (GMT, text, option)) == NULL) {
13475 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Scale reference point was not accepted\n", option);
13476 		gmt_refpoint_syntax (GMT, "L", NULL, GMT_ANCHOR_MAPSCALE, 3);
13477 		return (1);	/* Failed basic parsing */
13478 	}
13479 
13480 	/* refpoint->args are now +c[/<slon>]/<slat>+w<length>[e|f|M|n|k|u][+a<align>][+f][+j<just>][+l<label>][+o<dx>[/<dy>]][+u][+v]. */
13481 
13482 	if (gmt_validate_modifiers (GMT, ms->refpoint->args, option, "acfjlouwv", GMT_MSG_ERROR)) return (1);
13483 
13484 	/* Required modifier is +w */
13485 
13486 	if (gmt_M_is_geographic (GMT, GMT_IN)) ms->origin[GMT_X] = ms->origin[GMT_Y] = GMT->session.d_NaN;	/* One or both may need to set after gmt_map_setup is called */
13487 	if (gmt_get_modifier (ms->refpoint->args, 'c', string)) {	/* Specific scale origin */
13488 		ms->origin_mode = GMT_SCALE_ORIGIN_GIVEN;	/* Presumably we are specifying the actual origin lat/lon */
13489 		if (gmt_M_is_cartesian (GMT, GMT_IN))	/* No use */
13490 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Not allowed for Cartesian projections\n", option);
13491 		else if (string[0] == '\0')	/* No argument means pick map center */
13492 			ms->origin_mode = GMT_SCALE_ORIGIN_MIDDLE;
13493 		else if (strchr (string, '/')) {	/* Got both lon and lat for scale */
13494 			if ((n = gmt_get_pair (GMT, string, GMT_PAIR_COORD, ms->origin)) < 2) {
13495 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Failed to parse scale <longitude>/<latitude> for +c modifier\n", option);
13496 				error++;
13497 			}
13498 			else if (fabs (ms->origin[GMT_X]) > 360.0) {
13499 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Scale longitude is out of range for +c modifier\n", option);
13500 				error++;
13501 			}
13502 		}
13503 		else {	/* Just got latitude scale */
13504 			if (gmt_verify_expectations (GMT, GMT_IS_LAT, gmt_scanf (GMT, string, GMT_IS_LON, &(ms->origin[GMT_Y])), string)) {
13505 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Failed to parse scale latitude for +c modifier\n", option);
13506 				error++;
13507 			}
13508 		}
13509 		if (ms->origin_mode == GMT_SCALE_ORIGIN_GIVEN && fabs (ms->origin[GMT_Y]) > 90.0) {
13510 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Scale latitude is out of range for +c modifier\n", option);
13511 			error++;
13512 		}
13513 	}
13514 	else	/* Pick the same location for scale origin as the placement of the scale */
13515 		ms->origin_mode = GMT_SCALE_ORIGIN_PLACE;
13516 
13517 	if (gmt_get_modifier (ms->refpoint->args, 'w', string)) {	/* Get bar length */
13518 		if (string[0] == '\0') {	/* Got nutin' */
13519 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No length argument given to +w modifier\n", option);
13520 			error++;
13521 		}
13522 		else if (gmt_M_is_geographic (GMT, GMT_IN) && isalpha ((int)string[n = (int)strlen (string) - 1])) {	/* Letter at end of distance value */
13523 			ms->measure = string[n];
13524 			if (gmt_M_compat_check (GMT, 4) && ms->measure == 'm') {
13525 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Distance unit m is deprecated; use M for statute miles\n");
13526 				ms->measure = 'M';
13527 			}
13528 			if (strchr (GMT_LEN_UNITS2, ms->measure))	/* Gave a valid distance unit */
13529 				string[n] = '\0';
13530 			else {
13531 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Valid distance units are %s\n", option, GMT_LEN_UNITS2_DISPLAY);
13532 				error++;
13533 			}
13534 		}
13535 		ms->length = atof (string);	/* Length as given, no unit scaling here */
13536 		if (ms->length <= 0.0) {
13537 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Length must be positive\n", option);
13538 			error++;
13539 		}
13540 	}
13541 	else {
13542 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Scale length modifier +w<length> is required\n", option);
13543 		error++;
13544 	}
13545 	/* Optional modifiers +a, +f, +j, +l, +o, +u, +v */
13546 	if (gmt_get_modifier (ms->refpoint->args, 'f', NULL)) {	/* Do fancy label */
13547 		if (gmt_M_is_cartesian (GMT, GMT_IN)) {	/* Not allowed' */
13548 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No fancy map scale modifier allowed for Cartesian projections\n", option);
13549 			error++;
13550 		}
13551 		else
13552 			ms->fancy = true;
13553 	}
13554 	if (gmt_get_modifier (ms->refpoint->args, 'v', NULL)) {	/* Ask for vertical Cartesian scale */
13555 		if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* Not allowed' */
13556 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No vertical scale modifier allowed for geographic projections\n", option);
13557 			error++;
13558 		}
13559 		else
13560 			ms->vertical = true;
13561 	}
13562 	if (gmt_get_modifier (ms->refpoint->args, 'j', string))	{	/* Got justification of item w.r.t. reference point */
13563 		if (string[0] == '\0') {	/* Got nutin' */
13564 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No justification argument given to +j modifier\n", option);
13565 			error++;
13566 		}
13567 		else
13568 			ms->justify = gmt_just_decode (GMT, string, PSL_MC);
13569 	}
13570 	else {	/* With -Dj or -DJ, set default to reference (mirrored) justify point, else MC */
13571 		ms->justify = gmt_M_just_default (GMT, ms->refpoint, PSL_MC);
13572 		if (vertical || ms->vertical || gmt_M_is_cartesian (GMT, GMT_IN)) {
13573 			double out_offset = GMT->current.setting.map_label_offset[GMT_Y] + GMT->current.setting.map_frame_width;
13574 			double in_offset  = GMT->current.setting.map_label_offset[GMT_Y];
13575 			switch (ms->refpoint->justify) {        /* Autoset +a, +o when placed centered on a side: Note: +a, +o may overrule this later */
13576 				case PSL_TC:
13577 					ms->off[GMT_Y] = (ms->refpoint->mode == GMT_REFPOINT_JUST_FLIP) ? out_offset : in_offset;
13578 					break;
13579 				case PSL_BC:
13580 					ms->off[GMT_Y] = (ms->refpoint->mode == GMT_REFPOINT_JUST_FLIP) ? out_offset : in_offset;
13581 					break;
13582 				case PSL_ML:
13583 					ms->alignment = 'l';        /* left side */
13584 					ms->off[GMT_X] = (ms->refpoint->mode == GMT_REFPOINT_JUST_FLIP) ? out_offset : in_offset;
13585 					break;
13586 				case PSL_MR:
13587 					ms->off[GMT_X] = (ms->refpoint->mode == GMT_REFPOINT_JUST_FLIP) ? out_offset : in_offset;
13588 					break;
13589 				default:
13590 					break;
13591 			}
13592 		}
13593 	}
13594 	if (gmt_get_modifier (ms->refpoint->args, 'a', string)) {	/* Set alignment */
13595 		ms->alignment = string[0];
13596 		if (!strchr ("lrtb", ms->alignment)) {
13597 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Valid label alignments (+a) are l|r|t|b\n", option);
13598 			error++;
13599 		}
13600 	}
13601 	if (gmt_get_modifier (ms->refpoint->args, 'l', string)) {	/* Add label */
13602 		if (string[0]) strncpy (ms->label, string, GMT_LEN64-1);
13603 		ms->do_label = true;
13604 	}
13605 	if (gmt_get_modifier (ms->refpoint->args, 'o', string)) {	/* Got offsets from reference point */
13606 		if ((n = gmt_get_pair (GMT, string, GMT_PAIR_DIM_DUP, ms->off)) < 0) {
13607 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Failed to parse offset arguments for +o modifier\n", option);
13608 			error++;
13609 		}
13610 	}
13611 	if (gmt_get_modifier (ms->refpoint->args, 'u', NULL))	/* Add units to annotations */
13612 		ms->unit = true;
13613 	if (error)
13614 		gmt_mapscale_syntax (GMT, 'L', "Draw a map scale centered on specified reference point.");
13615 	if (vertical || gmt_M_is_cartesian (GMT, GMT_IN))
13616 		ms->measure = '\0';	/* No units for Cartesian data */
13617 
13618 	ms->plot = true;
13619 	return (error);
13620 }
13621 
13622 /*! . */
gmt_getrose(struct GMT_CTRL * GMT,char option,char * text,struct GMT_MAP_ROSE * ms)13623 int gmt_getrose (struct GMT_CTRL *GMT, char option, char *text, struct GMT_MAP_ROSE *ms) {
13624 	unsigned int error = 0, k, order[4] = {3,1,0,2};
13625 	int n;
13626 	char txt_a[GMT_LEN256] = {""}, string[GMT_LEN256] = {""};
13627 	struct GMT_MAP_PANEL *save_panel = ms->panel;	/* In case it was set and we wipe it below with gmt_M_memset */
13628 
13629 	/* SYNTAX is -Td|m[g|j|n|x]<refpoint>+w<width>[+f[<kind>]][+i<pen>][+j<justify>][+l<w,e,s,n>][+m[<dec>[/<dlabel>]]][+o<dx>[/<dy>]][+p<pen>][+t<ints>]
13630 	 * 1)  +f: fancy direction rose, <kind> = 1,2,3 which is the level of directions [1].
13631 	 * 2)  Tm: magnetic rose.  Optionally, append +d<dec>[/<dlabel>], where <dec> is magnetic declination and dlabel its label [no declination info].
13632 	 * If  -Tm, optionally set annotation interval with +t
13633 	 */
13634 
13635 	if (!text || text[0] == '\0') {
13636 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: No argument given\n", option);
13637 		return GMT_PARSE_ERROR;
13638 	}
13639 	gmt_M_memset (ms, 1, struct GMT_MAP_ROSE);
13640 	ms->panel = save_panel;	/* In case it is not NULL */
13641 	if (!strstr (text, "+w")) return gmtsupport_getrose_old (GMT, option, text, ms);	/* Old-style args */
13642 
13643 	/* Assign default values */
13644 	ms->type = GMT_ROSE_DIR_PLAIN;
13645 	ms->size = 0.0;
13646 	ms->a_int[GMT_ROSE_PRIMARY] = 30.0;	ms->f_int[GMT_ROSE_PRIMARY] = 5.0;	ms->g_int[GMT_ROSE_PRIMARY] = 1.0;
13647 	ms->a_int[GMT_ROSE_SECONDARY] = 30.0;	ms->f_int[GMT_ROSE_SECONDARY] = 5.0;	ms->g_int[GMT_ROSE_SECONDARY] = 1.0;
13648 
13649 	switch (text[0]) {
13650 		case 'd': ms->type = GMT_ROSE_DIR_PLAIN; break;
13651 		case 'm': ms->type = GMT_ROSE_MAG; break;
13652 		default:
13653 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Append d for directional and m for magnetic rose\n", option);
13654 			return (-1);
13655 			break;
13656 	}
13657 	if ((ms->refpoint = gmt_get_refpoint (GMT, &text[1], option)) == NULL) {
13658 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Map rose reference point was not accepted\n", option);
13659 		gmt_refpoint_syntax (GMT, "Td|m", NULL, GMT_ANCHOR_MAPROSE, 3);
13660 		return (1);	/* Failed basic parsing */
13661 	}
13662 
13663 	if (gmt_validate_modifiers (GMT, ms->refpoint->args, option, "dfijloptw", GMT_MSG_ERROR)) return (1);
13664 
13665 	/* Get required +w modifier */
13666 	if (gmt_get_modifier (ms->refpoint->args, 'w', string))	{	/* Get rose dimensions */
13667 		if (string[0] == '\0') {	/* Got nutin' */
13668 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  No width argument given to +w modifier\n", option);
13669 			error++;
13670 		}
13671 		else
13672 			ms->size = gmt_M_to_inch (GMT, string);
13673 	}
13674 	else {
13675 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c:  Rose dimension modifier +w<length> is required\n", option);
13676 		error++;
13677 	}
13678 	/* Get optional +d, +f, +i, +j, +l, +o, +p, +t, +w modifier */
13679 	if (gmt_get_modifier (ms->refpoint->args, 'd', string)) {	/* Want magnetic directions */
13680 		if (ms->type != GMT_ROSE_MAG) {
13681 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c:  Cannot specify +d<info> when -Td is selected\n", option);
13682 			return (-1);
13683 		}
13684 		if (string[0]) {	/* Got optional arguments */
13685 			if (strchr (string, '/'))	/* Got both declimation and label */
13686 				sscanf (string, "%[^/]/%s", txt_a, ms->dlabel);
13687 			else
13688 				strcpy (txt_a, string);
13689 			error += gmt_verify_expectations (GMT, GMT_IS_LON, gmt_scanf (GMT, txt_a, GMT_IS_LON, &ms->declination), txt_a);
13690 			ms->kind = 2;	/* Flag that we got declination parameters */
13691 		}
13692 		else
13693 			ms->kind = 1;	/* Flag that we did not get declination parameters */
13694 	}
13695 	if (gmt_get_modifier (ms->refpoint->args, 'f', string)) {	/* Want fancy rose, optionally set what kind */
13696 		if (ms->type == GMT_ROSE_MAG) {
13697 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c:  Cannot give both +f and +m\n", option);
13698 			error++;
13699 		}
13700 		ms->type = GMT_ROSE_DIR_FANCY;
13701 		ms->kind = (string[0]) ? atoi (string) : 1;
13702 		if (ms->kind < 1 || ms->kind > 3) {
13703 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c:  Modifier +f<kind> must be 1, 2, or 3\n", option);
13704 			error++;
13705 		}
13706 	}
13707 	if (gmt_get_modifier (ms->refpoint->args, 'i', string)) {
13708 		if (string[0] && gmt_getpen (GMT, string, &ms->pen[GMT_ROSE_PRIMARY])) error++;
13709 		ms->draw_circle[GMT_ROSE_PRIMARY] = true;
13710 	}
13711 	if (gmt_get_modifier (ms->refpoint->args, 'j', string))	{	/* Got justification of item w.r.t. reference point */
13712 		if (string[0] == '\0') {	/* Got nutin' */
13713 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c:  No justification argument given to +j modifier\n", option);
13714 			error++;
13715 		}
13716 		else
13717 			ms->justify = gmt_just_decode (GMT, string, PSL_MC);
13718 	}
13719 	else	/* With -Dj or -DJ, set default to reference (mirriored) justify point, else MC */
13720 		ms->justify = gmt_M_just_default (GMT, ms->refpoint, PSL_MC);
13721 	if (gmt_get_modifier (ms->refpoint->args, 'l', string)) {	/* Set labels +lw,e,s,n*/
13722 		ms->do_label = true;
13723 		if (string[0] == 0) {	/* Want default labels */
13724 			strcpy (ms->label[0], GMT->current.language.cardinal_name[2][2]);
13725 			strcpy (ms->label[1], GMT->current.language.cardinal_name[2][1]);
13726 			strcpy (ms->label[2], GMT->current.language.cardinal_name[2][3]);
13727 			strcpy (ms->label[3], GMT->current.language.cardinal_name[2][0]);
13728 		}
13729 		else {	/* Decode w,e,s,n strings */
13730 			size_t len;
13731 			unsigned int n_comma = 0;
13732 			char *tmp = string, *pp = NULL;
13733 			/* Check we got 3 commas */
13734 			for (len = strlen (string); len > 0; len--) if (string[len-1] == ',') n_comma++;
13735 			k = 0;
13736 			while (k < 4 && (pp = strsep (&tmp, ",")) != NULL) {	/* Get the four labels */
13737 				if (pp[0] == '\0' || strcmp (pp, "-")) strncpy (ms->label[order[k]], pp, GMT_LEN64-1);
13738 				k++;
13739 			}
13740 			if (n_comma < 3 && k != 4) {	/* Ran out of labels */
13741 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: 4 Labels must be given via modifier +lw,e,s,n\n", option);
13742 				error++;
13743 			}
13744 		}
13745 	}
13746 	if (gmt_get_modifier (ms->refpoint->args, 'o', string)) {	/* Got offsets from reference point */
13747 		if ((n = gmt_get_pair (GMT, string, GMT_PAIR_DIM_DUP, ms->off)) < 0) {
13748 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c:  Failed to parse offset arguments for +o modifier\n", option);
13749 			error++;
13750 		}
13751 	}
13752 	if (gmt_get_modifier (ms->refpoint->args, 'p', string)) {
13753 		if (string[0] && gmt_getpen (GMT, string, &ms->pen[GMT_ROSE_SECONDARY])) error++;
13754 		ms->draw_circle[GMT_ROSE_SECONDARY] = true;
13755 	}
13756 	if (gmt_get_modifier (ms->refpoint->args, 't', string)) {	/* Set intervals */
13757 		n = sscanf (string, "%lf/%lf/%lf/%lf/%lf/%lf",
13758 			&ms->a_int[GMT_ROSE_SECONDARY], &ms->f_int[GMT_ROSE_SECONDARY], &ms->g_int[GMT_ROSE_SECONDARY],
13759 			&ms->a_int[GMT_ROSE_PRIMARY], &ms->f_int[GMT_ROSE_PRIMARY], &ms->g_int[GMT_ROSE_PRIMARY]);
13760 		if (!(n == 3 || n == 6)) {
13761 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c:  Modifier +t<intervals> expects 3 or 6 intervals\n", option);
13762 			error++;
13763 		}
13764 	}
13765 	ms->plot = true;
13766 	return (error);
13767 }
13768 
gmt_get_pair(struct GMT_CTRL * GMT,char * string,unsigned int mode,double par[])13769 int gmt_get_pair (struct GMT_CTRL *GMT, char *string, unsigned int mode, double par[]) {
13770 	/* Read 2 coordinates or 0-2 dimensions, converted to inch.  Action depends on mode:
13771 	 * mode == GMT_PAIR_COORD:	Expect two coordinates, error otherwise.
13772 	 * mode == GMT_PAIR_DIM_EXACT:	Expect two dimensions, error otherwise.
13773 	 * mode == GMT_PAIR_DIM_DUP:	Expect 0-2 dimensions, if 1 is found set 2 == 1.
13774 	 * mode == GMT_PAIR_DIM_NODUP:	Expect 0-2 dimensions, no duplications.
13775 	 *				Any defaults placed in par will survive.
13776 	 */
13777 	int n, k;
13778 	/* Wrapper around GMT_Get_Values when we know input is either coordinates or plot dimensions */
13779 	if ((n = GMT_Get_Values (GMT->parent, string, par, 2)) < 0) return n;	/* Parsing error */
13780 	if ((mode == GMT_PAIR_COORD || mode == GMT_PAIR_DIM_EXACT) && n != 2) {
13781 		char *kind[2] = {"coordinates", "dimensions"};
13782 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Parsing error: Expected two %s\n", kind[mode]);
13783 		return -1;
13784 	}
13785 	if (mode != GMT_PAIR_COORD) {	/* Since GMT_Get_Values returns default project length unit we scale to inches */
13786 		for (k = 0; k < n; k++)
13787 			par[k] *= GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_INCH];
13788 	}
13789 	if (mode == GMT_PAIR_DIM_DUP && n == 1)
13790 		par[GMT_Y] = par[GMT_X];	/* Duplicate when second is not given */
13791 	return n;	/* We return items parsed even if duplication means there might be 2 */
13792 }
13793 
13794 /*! . */
gmt_getpanel(struct GMT_CTRL * GMT,char option,char * text,struct GMT_MAP_PANEL ** Pptr)13795 int gmt_getpanel (struct GMT_CTRL *GMT, char option, char *text, struct GMT_MAP_PANEL **Pptr) {
13796 	/* Gets the specifications for a rectangular panel w/optional reflection that is
13797 	 * used as background for legends, logos, images, and map scales. */
13798 
13799 	unsigned int pos = 0, n_errors = 0;
13800 	int n;
13801 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, p[GMT_BUFSIZ] = {""};
13802 	struct GMT_MAP_PANEL *P = NULL;
13803 
13804 	if (*Pptr) {	/* Already a panel structure there! */
13805 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_getpanel: Given a non-null panel pointer!\n");
13806 		return 1;
13807 	}
13808 	/* Initialize the panel settings first */
13809 	P = gmt_M_memory (GMT, NULL, 1, struct GMT_MAP_PANEL);
13810 	P->radius = GMT->session.u2u[GMT_PT][GMT_INCH] * GMT_FRAME_RADIUS;	/* 6 pt */
13811 	gmt_init_fill (GMT, &P->fill, -1.0, -1.0, -1.0);			/* Default is no fill unless specified */
13812 	gmt_init_fill (GMT, &P->sfill, gmt_M_is255 (127), gmt_M_is255 (127), gmt_M_is255 (127));	/* Default if gray shade is used */
13813 	(void) gmt_getpen (GMT, "thicker,black", &P->pen1);			/* Heavier pen for main outline */
13814 	P->pen2 = GMT->current.setting.map_default_pen;			/* Thinner pen for optional inner outline */
13815 	P->debug_pen = GMT->current.setting.map_default_pen;			/* Thinner pen for optional inner outline */
13816 	P->gap = GMT->session.u2u[GMT_PT][GMT_INCH] * GMT_FRAME_GAP;	/* Default is 2p */
13817 	/* Initialize the panel clearances */
13818 	P->padding[XLO] = GMT->session.u2u[GMT_PT][GMT_INCH] * GMT_FRAME_CLEARANCE;	/* Default is 4p */
13819 	for (pos = XHI; pos <= YHI; pos++) P->padding[pos] = P->padding[XLO];
13820 	P->off[GMT_X] = P->padding[XLO];	/* Set the shadow offsets [Default is (4p, -4p)] */
13821 	P->off[GMT_Y] = -P->off[GMT_X];
13822 
13823 	if (text == NULL || text[0] == 0) {	/* Blank arg means draw outline with default pen */
13824 		P->mode = GMT_PANEL_OUTLINE;
13825 		*Pptr = P;	/* Pass out the pointer to the panel attributes */
13826 		return 0;
13827 	}
13828 	pos = 0;
13829 	while (gmt_getmodopt (GMT, option, text, "cidgprs", &pos, p, &n_errors) && n_errors == 0) {	/* Looking for +c, [+d], +g, +i, +p, +r, +s */
13830 		switch (p[0]) {
13831 			case 'c':	/* Clearance will expand the rectangle by specified amounts */
13832 				n = GMT_Get_Values (GMT->parent, &p[1], P->padding, 4);
13833 				if (n == 1)	/* Same round in all directions */
13834 					P->padding[XHI] = P->padding[YLO] = P->padding[YHI] = P->padding[XLO];
13835 				else if (n == 2) {	/* Separate round in x and y */
13836 					P->padding[YLO] = P->padding[YHI] = P->padding[XHI];
13837 					P->padding[XHI] = P->padding[XLO];
13838 				}
13839 				else if (n != 4){
13840 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Bad number of increment to modifier +%c.\n", option, p[0]);
13841 					n_errors++;
13842 				}
13843 				/* Since GMT_Get_Values returns in default project length unit, convert to inch */
13844 				for (n = 0; n < 4; n++) P->padding[n] *= GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_INCH];
13845 				P->clearance = true;
13846 				break;
13847 			case 'd':	/* debug mode for developers */
13848 				P->debug = true;
13849 				if (p[1] && gmt_getpen (GMT, &p[1], &P->debug_pen)) n_errors++;
13850 				break;
13851 			case 'g':	/* Set fill */
13852 				if (!p[1] || gmt_getfill (GMT, &p[1], &P->fill)) n_errors++;
13853 				P->mode |= GMT_PANEL_FILL;
13854 				break;
13855 			case 'i':	/* Secondary pen info */
13856 				P->mode |= GMT_PANEL_INNER;
13857 				if (p[1]) {	/* Gave 1-2 attributes */
13858 					n = sscanf (&p[1], "%[^/]/%s", txt_a, txt_b);
13859 					if (n == 2) {	/* Got both gap and pen */
13860 						P->gap = gmt_M_to_inch (GMT, txt_a);
13861 						if (gmt_getpen (GMT, txt_b, &P->pen2)) n_errors++;
13862 					}
13863 					else	/* Only got pen; use default gap */
13864 						if (gmt_getpen (GMT, txt_a, &P->pen2)) n_errors++;
13865 				}
13866 				break;
13867 			case 'p':	/* Set outline and optionally change primary pen info */
13868 				if (p[1] && gmt_getpen (GMT, &p[1], &P->pen1)) n_errors++;
13869 				P->mode |= GMT_PANEL_OUTLINE;
13870 				break;
13871 			case 'r':	/* Corner radius of rounded rectangle */
13872 				if (p[1]) P->radius = gmt_M_to_inch (GMT, &p[1]);
13873 				P->mode |= GMT_PANEL_ROUNDED;
13874 				break;
13875 			case 's':	/* Get shade settings */
13876 				if (p[1]) {
13877 					n = sscanf (&p[1], "%[^/]/%[^/]/%s", txt_a, txt_b, txt_c);
13878 					if (n == 1) {	/* Just got a new fill */
13879 						if (gmt_getfill (GMT, txt_a, &P->sfill)) n_errors++;
13880 					}
13881 					else if (n == 2) {	/* Just got a new offset */
13882 						if (gmt_get_pair (GMT, &p[1], GMT_PAIR_DIM_DUP, P->off) < 0) n_errors++;
13883 					}
13884 					else if (n == 3) {	/* Got offset and fill */
13885 						P->off[GMT_X] = gmt_M_to_inch (GMT, txt_a);
13886 						P->off[GMT_Y] = gmt_M_to_inch (GMT, txt_b);
13887 						if (gmt_getfill (GMT, txt_c, &P->sfill)) n_errors++;
13888 					}
13889 					else n_errors++;
13890 				}
13891 				P->mode |= GMT_PANEL_SHADOW;
13892 				break;
13893 			default:
13894 				n_errors++;
13895 				break;
13896 		}
13897 	}
13898 	*Pptr = P;	/* Pass out the pointer to the panel attributes */
13899 	return ((int)n_errors);
13900 }
13901 
13902 /*! . */
gmt_minmaxinc_verify(struct GMT_CTRL * GMT,double min,double max,double inc,double slop)13903 unsigned int gmt_minmaxinc_verify (struct GMT_CTRL *GMT, double min, double max, double inc, double slop) {
13904 	double range;
13905 	gmt_M_unused(GMT);
13906 
13907 	/* Check for how compatible inc is with the range max - min.
13908 	   We will tolerate a fractional sloppiness <= slop.  The
13909 	   return values are:
13910 	   0 : Everything is ok
13911 	   1 : range is not a whole multiple of inc (within assigned slop)
13912 	   2 : the range (max - min) is < 0
13913 	   3 : inc is <= 0
13914 	*/
13915 
13916 	if (inc <= 0.0) return (3);
13917 
13918 	range = max - min;
13919 	if (range < 0.0) return (2);
13920 
13921 	range = (fmod (range / inc, 1.0));
13922 	if (range > slop && range < (1.0 - slop)) return (1);
13923 	return 0;
13924 }
13925 
13926 /*! . */
gmt_str_tolower(char * value)13927 void gmt_str_tolower (char *value) {
13928 	/* Convert entire string to lower case */
13929 	int i, c;
13930 	for (i = 0; value[i]; i++) {
13931 		c = (int)value[i];
13932 		value[i] = (char) tolower (c);
13933 	}
13934 }
13935 
13936 /*! . */
gmt_str_toupper(char * value)13937 void gmt_str_toupper (char *value) {
13938 	/* Convert entire string to upper case */
13939 	int i, c;
13940 	for (i = 0; value[i]; i++) {
13941 		c = (int)value[i];
13942 		value[i] = (char) toupper (c);
13943 	}
13944 }
13945 
13946 /*! . */
gmt_str_setcase(struct GMT_CTRL * GMT,char * value,int mode)13947 void gmt_str_setcase (struct GMT_CTRL *GMT, char *value, int mode) {
13948 	if (mode == 0) return;	/* Do nothing */
13949 	if (mode == -1)
13950 		gmt_str_tolower (value);
13951 	else if (mode == +1)
13952 		gmt_str_toupper (value);
13953 	else
13954 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Bad mode (%d) passed to gmt_str_setcase\n", mode);
13955 }
13956 
gmt_first_modifier(struct GMT_CTRL * GMT,char * string,const char * sep)13957 char *gmt_first_modifier (struct GMT_CTRL *GMT, char *string, const char *sep) {
13958 	/* Return pointer to the first modifier +<char>, with three conditions:
13959 	 * 1. <char> must be one of the letters in sep,
13960 	 * 2. We must not be inside quoted text, i.e., "my +shit title",
13961 	 * 3. The + must not be escaped, i.e., +t"My \+shit text"
13962 	 *
13963 	 * If the modifier is not found we returns NULL.  This is not an error
13964 	 * but means we did not find anything.
13965 	 */
13966 	size_t len, k = 0;
13967 	bool inside_quote = false, done = false;
13968 	if (sep == NULL) {
13969 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_first_modifier: No separation codes given\n");
13970 		return NULL;
13971 	}
13972 
13973 	if (string == NULL) return NULL;
13974 	len = strlen (string);
13975 	inside_quote = (string[0] == '\"' || string[0] == '\'');
13976 	while (!done) {
13977 		while (k < len && (inside_quote || string[k] != '+' || (k > 0 && string[k-1] == '\\'))) {
13978 			k++;
13979 			if (string[k] == '\"' || string[k] == '\'') inside_quote = !inside_quote;
13980 		}
13981 		k++;	/* Advance to character after the + */
13982 		if (k >= len) return NULL;
13983 		if (strchr (sep, string[k]))
13984 			done = true;
13985 		else {
13986 			if (isalpha (string[k])) GMT_Report (GMT->parent, GMT_MSG_WARNING, "Modifier +%c detected but not a valid modifier! - ignored\n", string[k]);
13987 			return NULL;
13988 		}
13989 	}
13990 	return (k > 0 && k < len) ? &string[k-1] : NULL;
13991 }
13992 
gmtlib_last_valid_file_modifier(struct GMTAPI_CTRL * API,char * filename,const char * mods)13993 char *gmtlib_last_valid_file_modifier (struct GMTAPI_CTRL *API, char* filename, const char *mods) {
13994 	/* A filename make have sequences that could look like a GMT modifier but is actually just part
13995 	 * of the filename, e.g. My.File+o44.grd.  We do not want to catch such sequences as modifiers and
13996 	 * parse them.  The strategy we use is to start from the end of the filename and work backwards
13997 	 * until the first non-expected "modifier" is found, then return pointer to first actual modifier
13998 	 * found, or NULL.  Any modifier will be of the form +?[<arg>], where ? is a lower or upper-case
13999 	 * letter, and <arg> is optional. */
14000 	bool go = true;	/* Keep scanning backwards until we find an unrecognized modifier */
14001 	bool allow_a;	/* True when "a" is a valid argument to the modifier */
14002 	char *modifiers = NULL;	/* Pointer to the start of valid modifiers in this filename, or NULL */
14003 	size_t k = strlen (filename);	/* k will serve as the index of the current character in the filename string */
14004 	gmt_M_unused (API);
14005 
14006 	while (filename[k] != '+' && k) k--;	/* Wind back to last found plus sign */
14007 	if (k == 0 || filename[k+1] == '\0') go = false;	/* No modifiers found at all, or we found a single trailing + in the name (e.g., my.test+) */
14008 	while (go) {	/* Here, filename[k] == '+' and k > 0 */
14009 		if (isalpha (filename[k+1]) && strchr (mods, filename[k+1])) {	/* This is a valid file modifier */
14010 			modifiers = &filename[k];	/* Update the pointer the the current start of valid modifiers */
14011 			k--;	/* Step one back from the '+' character; k could become 0 if filename was just valid modifiers (...) */
14012 			while (filename[k] != '+' && k) k--;	/* Wind back to last found plus sign as long as we are not in first position */
14013 			if (k == 0) go = false;	/* Ran out of characters */
14014 		}
14015 		else	/* Not a valid modifier or just part of something like a positive number - stop our scanning */
14016 			go = false;
14017 	}
14018 	if (modifiers) {	/* Check that we have modifiers only and not stuff like +o.myextension */
14019 		bool error = false;
14020 		k = 0;
14021 		while (!error && modifiers[k]) {
14022 			if (modifiers[k] == '+') {
14023 				k++;
14024 				allow_a = false;	/* a is not a valid number */
14025 				switch (modifiers[k]) {	/* The modifier code */
14026 					case 'o': case 's':	/* +o<offset>|a,  +s<scale>|a */
14027 						allow_a = true;	/* Argument "a" for auto is OK for these modifiers */
14028 						/* Fall through on purpose */
14029 					case 'h': case 'i': case 'n': 	/* +h[<hinge>], +i<inc>, +n<nodata> */
14030 						k++;	/* Move to start of argument, next modifier, or NULL */
14031 						while (modifiers[k] && modifiers[k] != '+' && strchr ("-+.0123456789eE", modifiers[k])) k++;	/* Skip a numerical argument */
14032 						if (allow_a && modifiers[k] == 'a') k++;	/* Ok with a for this modifier */
14033 						if (!(modifiers[k] == '\0' || modifiers[k] == '+')) error = true;	/* Means we found non-numbers after valid modifier */
14034 						break;
14035 					case 'u': case 'U':	/* +u<unit>, +U<unit */
14036 						k++;	/* Move to start of argument, next modifier, or NULL */
14037 						if (modifiers[k] == '\0' || strchr (GMT_LEN_UNITS2, modifiers[k]) == NULL)
14038 							error = true;	/* Missing the unit argument */
14039 						else	/* Got valid unit */
14040 							k++;
14041 						break;
14042 					default:	/* Modifier not from the global grid/CPT list */
14043 						error = true;
14044 						break;
14045 				}
14046 			}
14047 			else	/* Found some junk */
14048 				error = true;
14049 		}
14050 		if (error) {
14051 			GMT_Report (API, GMT_MSG_WARNING, "Your filename %s have what appears as valid GMT modifiers (from list +%s) but are embedded rather than appended to the filename - modifiers ignored\n", filename, mods);
14052 			modifiers = NULL;
14053 		}
14054 	}
14055 	return (modifiers);	/* Pass out the start of the valid modifiers or NULL */
14056 }
14057 
14058 #if 0
14059 /* Was used to test the tokenizing of +<code>[<args] in cases
14060  * where <args> may contain an escaped or quoted +, etc.
14061  * To test, just add option case -N in psbasemap and call
14062  * psbasemap -N<combination of +a+b+c+d with/without args. */
14063 void gmt_testing (struct GMT_CTRL *GMT, char *string) {
14064 	char *c = NULL, p[GMT_BUFSIZ] = {""}, *sep = "abcde";
14065 	unsigned int pos = 0, uerr = 0;
14066 	c = gmt_first_modifier (GMT, string, sep);
14067 	while (gmt_getmodopt (GMT, 'N', c, sep, &pos, p, &uerr) && uerr == 0) {
14068 		switch (p[0]) {
14069 			case 'a': fprintf (stderr, "From +%c Got %s\n", p[0], (p[1]) ? &p[1] : "nothing");	break;
14070 			case 'b': fprintf (stderr, "From +%c Got %s\n", p[0], (p[1]) ? &p[1] : "nothing");	break;
14071 			case 'c': fprintf (stderr, "From +%c Got %s\n", p[0], (p[1]) ? &p[1] : "nothing");	break;
14072 			case 'd': fprintf (stderr, "From +%c Got %s\n", p[0], (p[1]) ? &p[1] : "nothing");	break;
14073 			default: fprintf (stderr, "Got up empty\n");	break;
14074 		}
14075 	}
14076 }
14077 #endif
14078 
14079 /*! . */
gmt_assign_text(struct GMT_CTRL * GMT,char * p)14080 char *gmt_assign_text (struct GMT_CTRL *GMT, char *p) {
14081 	/* Deals with duplicating a string and skipping any enclosing quotes.
14082 	 * Note: p is expected to be the result from gmt_getmodopt, hence the p[1] stuff */
14083 	char *txt = NULL;
14084 	gmt_M_unused(GMT);
14085 	if (strchr ("\"\'", p[1]) && p[1] == p[strlen(p)-1]) { /* Eliminate quotes */
14086 		txt = strdup (&p[2]);
14087 		txt[strlen(txt)-1] = '\0';
14088 	}
14089 	else	/* Normal title */
14090 		txt = strdup (&p[1]);
14091 	return (txt);
14092 }
14093 
14094 /*! . */
gmt_getmodopt(struct GMT_CTRL * GMT,const char option,const char * string,const char * sep,unsigned int * pos,char * token,unsigned int * err)14095 unsigned int gmt_getmodopt (struct GMT_CTRL *GMT, const char option, const char *string, const char *sep, unsigned int *pos, char *token, unsigned int *err) {
14096 	/* Breaks string into tokens separated by one of more modifier separator
14097 	 * characters (in sep) following a "+".  E.g., if you are looking for a
14098 	 * set of modifiers +a, +g<arg>, and +z, set sep = "agz" and any other
14099 	 * +<char> will fail.  Set *pos to 0 before first call.
14100 	 * If any of your <arg> may contain a + (e.g., +t"My +ve experience") then you can
14101 	 * 1) Escape the plus with backslash (e.g., +t"My \+ve experience")
14102 	 * 2) Put everything in quotes (e.g., +t'"My +ve experience"') as we
14103 	 *    will ignore plus-signs inside quotes.  You may also switch around
14104 	 *    and do +t"'My +ve experience'" as long as quotes match properly.
14105 	 * Returns 1 if it finds a token and 0 if no more tokens left.
14106 	 * pos is updated and token is returned.  char *token must point
14107 	 * to memory of length >= strlen (string).
14108 	 * string is not changed by gmt_getmodopt.
14109 	 */
14110 
14111 	unsigned int i, j, string_len;
14112 	bool done = false, in_quote = false;
14113 	gmt_M_unused(GMT);
14114 
14115 	if (string == NULL) return 0;	/* Nothing to do */
14116 
14117 	string_len = (unsigned int)strlen (string);
14118 	token[0] = 0;	/* Initialize token to NULL in case we are at end */
14119 
14120 	while (!done) {
14121 		/* Wind up *pos to first + sign NOT inside quotes OR escaped */
14122 		while (string[*pos] && (in_quote || string[*pos] != '+' || ((*pos) > 0 && string[(*pos)-1] == '\\'))) {
14123 			(*pos)++;
14124 			if (string[*pos] == '\"' || string[*pos] == '\'') in_quote = !in_quote;	/* Watch if we enter quoted text */
14125 		}
14126 
14127 		if (*pos >= string_len || string_len == 0) return 0;	/* Got NULL string or no more string left to search */
14128 
14129 		(*pos)++;	/* Go and examine if next char is one of requested modifier options */
14130 		done = (strchr (sep, (int)string[*pos]) != NULL);	/* true if this is our guy */
14131 		if (!done && err) {	/* Unrecognized modifier is an error, unless err is NULL */
14132 			if (option)
14133 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unrecognized modifier +%c\n", option, string[*pos]);
14134 			else
14135 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized modifier +%c\n", string[*pos]);
14136 			(*err)++;
14137 		}
14138 	}
14139 
14140 	/* Search for next +sep occurrence */
14141 	/* Search for next +char occurrence */
14142 	i = *pos; j = 0;
14143 	//while (string[i] && (in_quote || string[i] != '+' || (i && string[i-1] == '\\') || !strchr (sep, (int)string[i+1]))) {
14144 	while (string[i] && (in_quote || string[i] != '+' || (i && string[i-1] == '\\'))) {
14145 		if (string[i+1] != '+' || string[i] != '\\') token[j++] = string[i];	/* Do not return the escape char */
14146 		i++;	/* Go to next character */
14147 		if (string[i] == '\"' || string[i] == '\'') in_quote = !in_quote;	/* Check when we get past the quoted section */
14148 	}
14149 	token[j] = 0;	/* Add terminating \0 */
14150 	if (j > 2 && token[1] == '\"' && token[j-1] == '\"') { /* The whole thing was given in quotes */
14151 		memmove (&token[1], &token[2], strlen(token)-3);
14152 		token[j-2] = 0;
14153 	}
14154 
14155 	*pos = i;
14156 
14157 	return 1;
14158 }
14159 
14160 /*! . */
gmtlib_get_map_interval(struct GMT_CTRL * GMT,unsigned int type,struct GMT_PLOT_AXIS_ITEM * T)14161 double gmtlib_get_map_interval (struct GMT_CTRL *GMT, unsigned int type, struct GMT_PLOT_AXIS_ITEM *T) {
14162 	switch (T->unit) {
14163 		case 'd':	/* arc Degrees */
14164 			return (T->interval);
14165 			break;
14166 		case 'm':	/* arc Minutes */
14167 			return (T->interval * GMT_MIN2DEG);
14168 			break;
14169 		case 'c':	/* arc Seconds [deprecated] */
14170 			if (gmt_M_compat_check (GMT, 4)) {
14171 				GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Second interval unit c is deprecated; use s instead\n");
14172 			}
14173 			else
14174 				return (T->interval);
14175 			/* Intentionally fall through - to 's' */
14176 		case 's':	/* arc Seconds or time seconds */
14177 			return ((type == GMT_TIME) ? T->interval : T->interval * GMT_SEC2DEG);
14178 			break;
14179 		default:
14180 			return (T->interval);
14181 			break;
14182 	}
14183 }
14184 
14185 /*! . */
gmt_just_validate(struct GMT_CTRL * GMT,char * key,char * def)14186 int gmt_just_validate (struct GMT_CTRL *GMT, char *key, char *def) {
14187 	/* Ensure given justification key is kosher.  If not, then
14188 	 * use def unless NULL, else error */
14189 	if ((strchr ("LCRlcr", key[0]) && strchr ("BMTbmt", key[1])) ||
14190 	    (strchr ("LCRlcr", key[1]) && strchr ("BMTbmt", key[0]))) return 0;	/* We are good */
14191 	if (def == NULL) return GMT_RUNTIME_ERROR;	/* No fallback */
14192 	GMT_Report (GMT->parent, GMT_MSG_WARNING, "Malformed justification/position code %s replaced by %s\n", key, def);
14193 	strncpy (key, def, 2U);	/* Override using default code */
14194 	return 0;
14195 }
14196 
14197 /*! . */
gmt_just_to_code(struct GMT_CTRL * GMT,int justify,char * key)14198 void gmt_just_to_code (struct GMT_CTRL *GMT, int justify, char *key) {
14199 	/* Converts justify integer code to string.  Assume justify is valid */
14200 	static char *hor = {"LCR"}, *ver = {"BMT"};
14201 	int i, j;
14202 	gmt_M_unused (GMT);
14203 	i = (justify % 4) - 1;
14204 	j = justify / 4;
14205 	assert (i >= 0 && i <= 2);
14206 	assert (j >= 0 && j <= 2);
14207 	key[0] = hor[i];
14208 	key[1] = ver[j];
14209 }
14210 
14211 /*! . */
gmt_just_decode(struct GMT_CTRL * GMT,char * key,int def)14212 int gmt_just_decode (struct GMT_CTRL *GMT, char *key, int def) {
14213 	/* Converts justification info (key) like BL (bottom left) to justification indices
14214 	 * def = default value.
14215 	 * When def % 4 = 0, horizontal position must be specified
14216 	 * When def / 4 = 3, vertical position must be specified
14217 	 */
14218 	int i, j;
14219 	size_t k;
14220 	const size_t s_length = strlen(key);
14221 
14222 	if (isdigit ((int)key[0])) {	/* Apparently got one of the 1-11 codes */
14223 		i = atoi(key);
14224 		if (i < PSL_BL || i > PSL_TR || (i%4) == 0) i = -99;
14225 		return (i);
14226 	}
14227 
14228 	i = def % 4;
14229 	j = def / 4;
14230 	for (k = 0; k < s_length; k++) {
14231 		switch (key[k]) {
14232 			case 'b': case 'B':	/* Bottom baseline */
14233 				j = 0;
14234 				break;
14235 			case 'm': case 'M':	/* Middle baseline */
14236 				j = 1;
14237 				break;
14238 			case 't': case 'T':	/* Top baseline */
14239 				j = 2;
14240 				break;
14241 			case 'l': case 'L':	/* Left Justified */
14242 				i = 1;
14243 				break;
14244 			case 'c': case 'C':	/* Center Justified */
14245 				i = 2;
14246 				break;
14247 			case 'r': case 'R':	/* Right Justified */
14248 				i = 3;
14249 				break;
14250 			default:
14251 				return (-99);
14252 		}
14253 	}
14254 
14255 	if (i == 0) {
14256 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Horizontal text justification not set, defaults to L(eft)\n");
14257 		i = 1;
14258 	}
14259 	if (j == 3) {
14260 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Vertical text justification not set, defaults to B(ottom)\n");
14261 		j = 0;
14262 	}
14263 
14264 	return (j * 4 + i);
14265 }
14266 
14267 /*! . */
gmt_smart_justify(struct GMT_CTRL * GMT,int just,double angle,double dx,double dy,double * x_shift,double * y_shift,unsigned int mode)14268 void gmt_smart_justify (struct GMT_CTRL *GMT, int just, double angle, double dx, double dy, double *x_shift, double *y_shift, unsigned int mode) {
14269 	/* mode = 2: Assume a radius offset so that corner shifts are adjusted by 1/sqrt(2) */
14270 	double s, c, xx, yy, f;
14271 	gmt_M_unused(GMT);
14272 	f = (mode == 2) ? 1.0 / M_SQRT2 : 1.0;
14273 	sincosd (angle, &s, &c);
14274 	xx = (2 - (just%4)) * dx * f;	/* Smart shift in x */
14275 	yy = (1 - (just/4)) * dy * f;	/* Smart shift in x */
14276 	*x_shift += c * xx - s * yy;	/* Must account for angle of label */
14277 	*y_shift += s * xx + c * yy;
14278 }
14279 
14280 /*! . */
gmt_verify_expectations(struct GMT_CTRL * GMT,unsigned int wanted,unsigned int got,char * item)14281 unsigned int gmt_verify_expectations (struct GMT_CTRL *GMT, unsigned int wanted, unsigned int got, char *item) {
14282 	/* Compare what we wanted with what we got and see if it is OK */
14283 	unsigned int error = 0;
14284 
14285 	if (wanted == GMT_IS_UNKNOWN) {	/* No expectations set */
14286 		switch (got) {
14287 			case GMT_IS_ABSTIME:	/* Found a T in the string - ABSTIME ? */
14288 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "%s appears to be an Absolute Time String: ", item);
14289 				if (gmt_M_is_geographic (GMT, GMT_IN))
14290 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "This is not allowed for a map projection\n");
14291 				else
14292 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "You must specify time data type with option -f.\n");
14293 				error++;
14294 				break;
14295 
14296 			case GMT_IS_GEO:	/* Found a : in the string - GEO ? */
14297 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "%s appears to be a Geographical Location String: ", item);
14298 				if (GMT->current.proj.projection_GMT == GMT_LINEAR)
14299 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "You should append d to the -Jx or -JX projection for geographical data.\n");
14300 				else
14301 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "You should specify geographical data type with option -f.\n");
14302 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Will proceed assuming geographical input data.\n");
14303 				break;
14304 
14305 			case GMT_IS_LON:	/* Found a : in the string and then W or E - LON ? */
14306 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "%s appears to be a Geographical Longitude String: ", item);
14307 				if (GMT->current.proj.projection_GMT == GMT_LINEAR)
14308 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "You should append d to the -Jx or -JX projection for geographical data.\n");
14309 				else
14310 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "You should specify geographical data type with option -f.\n");
14311 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Will proceed assuming geographical input data.\n");
14312 				break;
14313 
14314 			case GMT_IS_LAT:	/* Found a : in the string and then S or N - LAT ? */
14315 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "%s appears to be a Geographical Latitude String: ", item);
14316 				if (GMT->current.proj.projection_GMT == GMT_LINEAR)
14317 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "You should append d to the -Jx or -JX projection for geographical data.\n");
14318 				else
14319 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "You should specify geographical data type with option -f.\n");
14320 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Will proceed assuming geographical input data.\n");
14321 				break;
14322 
14323 			case GMT_IS_FLOAT:
14324 				break;
14325 			default:
14326 				break;
14327 		}
14328 	}
14329 	else {
14330 		switch (got) {
14331 			case GMT_IS_NAN:
14332 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not decode %s, return NaN.\n", item);
14333 				error++;
14334 				break;
14335 
14336 			case GMT_IS_LAT:
14337 				if (wanted == GMT_IS_LON) {
14338 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Expected longitude, but %s is a latitude!\n", item);
14339 					error++;
14340 				}
14341 				break;
14342 
14343 			case GMT_IS_LON:
14344 				if (wanted == GMT_IS_LAT) {
14345 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Expected latitude, but %s is a longitude!\n", item);
14346 					error++;
14347 				}
14348 				break;
14349 			default:
14350 				break;
14351 		}
14352 	}
14353 	return (error);
14354 }
14355 
14356 /*! . */
gmt_list_custom_symbols(struct GMT_CTRL * GMT)14357 void gmt_list_custom_symbols (struct GMT_CTRL *GMT) {
14358 	/* Opens up GMT->init.custom_symbols.lis and displays the list of custom symbols */
14359 
14360 	FILE *fp = NULL;
14361 	char list[GMT_LEN256] = {""}, buffer[GMT_BUFSIZ] = {""};
14362 	struct GMTAPI_CTRL *API = GMT->parent;
14363 
14364 	/* Open the list in $GMT->session.SHAREDIR */
14365 
14366 	gmt_getsharepath (GMT, "custom", "gmt_custom_symbols", ".conf", list, R_OK);
14367 	if ((fp = fopen (list, "r")) == NULL) {
14368 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open file %s\n", list);
14369 		return;
14370 	}
14371 
14372 	GMT_Usage (API, 3, "Available custom symbols (See Appendix N):");
14373 	gmt_message (GMT, "       ---------------------------------------------------------\n");
14374 	while (fgets (buffer, GMT_BUFSIZ, fp)) if (!(buffer[0] == '#' || buffer[0] == 0))
14375 		gmt_message (GMT, "       %s", buffer);
14376 	fclose (fp);
14377 	gmt_message (GMT, "       ---------------------------------------------------------\n");
14378 }
14379 
14380 /*! . */
gmtlib_rotate2D(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,double x0,double y0,double angle,double xp[],double yp[])14381 void gmtlib_rotate2D (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, double x0, double y0, double angle, double xp[], double yp[]) {
14382 	/* Cartesian rotation of x,y in the plane by angle followed by translation by (x0, y0) */
14383 	uint64_t i;
14384 	double s, c;
14385 	gmt_M_unused(GMT);
14386 
14387 	sincosd (angle, &s, &c);
14388 	for (i = 0; i < n; i++) {	/* Coordinate transformation: Rotate and add new (x0, y0) offset */
14389 		xp[i] = x0 + x[i] * c - y[i] * s;
14390 		yp[i] = y0 + x[i] * s + y[i] * c;
14391 	}
14392 }
14393 
14394 /*! . */
gmtlib_get_arc(struct GMT_CTRL * GMT,double x0,double y0,double r,double dir1,double dir2,double ** x,double ** y)14395 unsigned int gmtlib_get_arc (struct GMT_CTRL *GMT, double x0, double y0, double r, double dir1, double dir2, double **x, double **y) {
14396 	/* Create an array with a circular arc. r in inches, angles in degrees */
14397 
14398 	unsigned int i, n;
14399 	double da, s, c, *xx = NULL, *yy = NULL;
14400 
14401 	n = urint (D2R * fabs (dir2 - dir1) * r / GMT->current.setting.map_line_step);
14402 	if (n < 2) n = 2;	/* To prevent division by 0 below */
14403 	xx = gmt_M_memory (GMT, NULL, n, double);
14404 	yy = gmt_M_memory (GMT, NULL, n, double);
14405 	da = (dir2 - dir1) / (n - 1);
14406 	for (i = 0; i < n; i++) {
14407 		sincosd (dir1 + i * da, &s, &c);
14408 		xx[i] = x0 + r * c;
14409 		yy[i] = y0 + r * s;
14410 	}
14411 	*x = xx;
14412 	*y = yy;
14413 
14414 	return (n);
14415 }
14416 
14417 /*! . */
gmt_init_track(struct GMT_CTRL * GMT,double y[],uint64_t n,struct GMT_XSEGMENT ** S)14418 int gmt_init_track (struct GMT_CTRL *GMT, double y[], uint64_t n, struct GMT_XSEGMENT **S) {
14419 	/* gmt_init_track accepts the y components of an x-y track of length n and returns an array of
14420 	 * line segments that have been sorted on the minimum y-coordinate
14421 	 */
14422 
14423 	uint64_t a, b, nl = n - 1;
14424 	struct GMT_XSEGMENT *L = NULL;
14425 
14426 	if (nl == 0) {
14427 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "nl = 0 in gmt_init_track\n");
14428 		return 0;
14429 	}
14430 
14431 	L = gmt_M_memory (GMT, NULL, nl, struct GMT_XSEGMENT);
14432 
14433 	for (a = 0, b = 1; b < n; a++, b++) {
14434 		if (y[b] < y[a]) {
14435 			L[a].start = b;
14436 			L[a].stop = a;
14437 		}
14438 		else {
14439 			L[a].start = a;
14440 			L[a].stop = b;
14441 		}
14442 	}
14443 
14444 	/* Sort on minimum y-coordinate, if tie then on 2nd coordinate */
14445 	qsort_r (L, nl, sizeof (struct GMT_XSEGMENT), gmtsupport_ysort, y);
14446 
14447 	*S = L;
14448 
14449 	return (GMT_NOERROR);
14450 }
14451 
14452 /*! . */
gmt_crossover(struct GMT_CTRL * GMT,double xa[],double ya[],uint64_t * sa0,struct GMT_XSEGMENT A[],uint64_t na,double xb[],double yb[],uint64_t * sb0,struct GMT_XSEGMENT B[],uint64_t nb,bool internal,bool geo,struct GMT_XOVER * X)14453 uint64_t gmt_crossover (struct GMT_CTRL *GMT, double xa[], double ya[], uint64_t *sa0, struct GMT_XSEGMENT A[], uint64_t na, double xb[], double yb[], uint64_t *sb0, struct GMT_XSEGMENT B[], uint64_t nb, bool internal, bool geo, struct GMT_XOVER *X) {
14454 	size_t nx_alloc;
14455 	uint64_t nx, this_a, this_b, xa_start = 0, xa_stop = 0, xb_start = 0, xb_stop = 0, ta_start = 0, ta_stop = 0, tb_start, tb_stop, n_seg_a, n_seg_b;
14456 	bool new_a, new_b, new_a_time = false, xa_OK = false, xb_OK = false;
14457 	uint64_t *sa = NULL, *sb = NULL;
14458 	double del_xa, del_xb, del_ya, del_yb, i_del_xa, i_del_xb, i_del_ya, i_del_yb, slp_a, slp_b, xc, yc, tx_a, tx_b;
14459 	double xshift = 0.0;	/* This may become +/-360 or 0 depending on longitude shifts for geo, else 0 */
14460 	double dx_ab;		/* Be careful due to longitude jumps are lurking everywhere; use this variable for that */
14461 
14462 	if (na < 2 || nb < 2) return (0);	/* Need at least 2 points to make a segment */
14463 
14464 	if (geo) {	/* Since our algorithm is Cartesian we must do some extra checking to prevent shits */
14465 		double amin, amax, bmin, bmax;
14466 		gmtlib_get_lon_minmax (GMT, xa, na, &amin, &amax);
14467 		gmtlib_get_lon_minmax (GMT, xb, nb, &bmin, &bmax);
14468 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "First line lon range: %g %g  Second line lon range: %g %g\n", amin, amax, bmin, bmax);
14469 		if (gmt_M_360_range (amin, amax))
14470 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "First geographic line has 360-degree range.  This may fool the Cartesian crossover algorithm\n");
14471 		if (!internal && gmt_M_360_range (bmin, bmax))
14472 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Second geographic line has 360-degree range.  This may fool the Cartesian crossover algorithm\n");
14473 		gmt_eliminate_lon_jumps (GMT, xa, na);
14474 		if (!internal) gmt_eliminate_lon_jumps (GMT, xb, nb);
14475 	}
14476 
14477 	this_a = this_b = nx = 0;
14478 	new_a = new_b = true;
14479 	nx_alloc = GMT_SMALL_CHUNK;
14480 
14481 	n_seg_a = na - 1;
14482 	n_seg_b = nb - 1;
14483 
14484 	/* Assign pointers to segment info given, or initialize zero arrays if not given */
14485 	sa = (sa0) ? sa0 : gmt_M_memory (GMT, NULL, na, uint64_t);
14486 	sb = (sb0) ? sb0 : gmt_M_memory (GMT, NULL, nb, uint64_t);
14487 
14488 	gmtsupport_x_alloc (GMT, X, nx_alloc, true);
14489 
14490 	while (this_a < n_seg_a && yb[B[this_b].start] > ya[A[this_a].stop]) this_a++;	/* Go to first possible A segment */
14491 
14492 	while (this_a < n_seg_a) {
14493 
14494 		/* First check for internal neighboring segments which cannot cross */
14495 
14496 		if (internal && (this_a == this_b || (A[this_a].stop == B[this_b].start || A[this_a].start == B[this_b].stop) || (A[this_a].start == B[this_b].start || A[this_a].stop == B[this_b].stop))) {	/* Neighboring segments cannot cross */
14497 			this_b++;
14498 			new_b = true;
14499 		}
14500 		else if (yb[B[this_b].start] > ya[A[this_a].stop]) {	/* Reset this_b and go to next A */
14501 			this_b = n_seg_b;
14502 		}
14503 		else if (yb[B[this_b].stop] < ya[A[this_a].start]) {	/* Must advance B in y-direction */
14504 			this_b++;
14505 			new_b = true;
14506 		}
14507 		else if (sb[B[this_b].stop] != sb[B[this_b].start]) {	/* Must advance B in y-direction since this segment crosses multiseg boundary*/
14508 			this_b++;
14509 			new_b = true;
14510 		}
14511 		else {	/* Current A and B segments overlap in y-range */
14512 
14513 			if (new_a) {	/* Must sort this A new segment in x so xa_start points to smallest x */
14514 				if (xa[A[this_a].stop] < xa[A[this_a].start]) {
14515 					xa_start = A[this_a].stop;
14516 					xa_stop  = A[this_a].start;
14517 				}
14518 				else {
14519 					xa_start = A[this_a].start;
14520 					xa_stop  = A[this_a].stop;
14521 				}
14522 				new_a = false;
14523 				new_a_time = true;
14524 				xa_OK = (sa[xa_start] == sa[xa_stop]);	/* false if we cross between multiple segments */
14525 			}
14526 
14527 			if (new_b) {	/* Must sort this new B segment in x so xb_start points to smallest x */
14528 				if (xb[B[this_b].stop] < xb[B[this_b].start]) {
14529 					xb_start = B[this_b].stop;
14530 					xb_stop  = B[this_b].start;
14531 				}
14532 				else {
14533 					xb_start = B[this_b].start;
14534 					xb_stop  = B[this_b].stop;
14535 				}
14536 				xb_OK = (sb[xb_start] == sb[xb_stop]);	/* false if we cross between multiple segments */
14537 			}
14538 
14539 			/* OK, first check for any overlap in x range */
14540 
14541 			if (xa_OK && xb_OK && gmtsupport_x_overlap (xa, xb, &xa_start, &xa_stop, &xb_start, &xb_stop, geo, &xshift)) {
14542 
14543 				/* We have segment overlap in x.  Now check if the two segments cross. Note:
14544 				 * For geographic coordinates, the xa and xb for these two segments will be
14545 				 * offset by xshift [0 for Cartesian].  Thus we add xshift to each end of a segment
14546 				 * so the values have no 360-degree offset relative to the coordinates in xb.  */
14547 
14548 				if (geo) {	/* Do this once for this segment comparisons */
14549 					xa[xa_start] += xshift;
14550 					xa[xa_stop]  += xshift;
14551 				}
14552 				del_xa = xa[xa_stop] - xa[xa_start];	/* xshift would cancel out */
14553 				del_xb = xb[xb_stop] - xb[xb_start];
14554 				del_ya = ya[xa_stop] - ya[xa_start];
14555 				del_yb = yb[xb_stop] - yb[xb_start];
14556 
14557 				if (gmt_M_is_zero (del_xa)) {	/* Vertical A segment: Special case */
14558 					if (gmt_M_is_zero (del_xb)) {	/* Two vertical segments with some overlap */
14559 						double y4[4];
14560 						/* Assign as crossover the middle of the overlapping segments */
14561 						X->x[nx] = xa[xa_start];
14562 						y4[0] = ya[xa_start];	y4[1] = ya[xa_stop];	y4[2] = yb[xb_start];	y4[3] = yb[xb_stop];
14563 						gmt_sort_array (GMT, y4, 4, GMT_DOUBLE);
14564 						if (!doubleAlmostEqual (y4[1], y4[2])) {
14565 							X->y[nx] = 0.5 * (y4[1] + y4[2]);
14566 							X->xnode[0][nx] = 0.5 * (xa_start + xa_stop);
14567 							X->xnode[1][nx] = 0.5 * (xb_start + xb_stop);
14568 							nx++;
14569 						}
14570 					}
14571 					else {	/* B is not vertical so we can solve for intersection */
14572 						dx_ab = xa[xa_start] - xb[xb_start];
14573 						i_del_xb = 1.0 / del_xb;
14574 						yc = yb[xb_start] + dx_ab * del_yb * i_del_xb;
14575 						if (!(yc < ya[A[this_a].start] || yc > ya[A[this_a].stop])) {	/* Did cross within the segment extents */
14576 							/* Only accept xover if occurring before segment end (in time) */
14577 
14578 							if (xb_start < xb_stop) {
14579 								tb_start = xb_start;	/* B Node first in time */
14580 								tb_stop = xb_stop;	/* B Node last in time */
14581 							}
14582 							else {
14583 								tb_start = xb_stop;	/* B Node first in time */
14584 								tb_stop = xb_start;	/* B Node last in time */
14585 							}
14586 							if (new_a_time) {
14587 								if (xa_start < xa_stop) {
14588 									ta_start = xa_start;	/* A Node first in time */
14589 									ta_stop = xa_stop;	/* A Node last in time */
14590 								}
14591 								else {
14592 									ta_start = xa_stop;	/* A Node first in time */
14593 									ta_stop = xa_start;	/* A Node last in time */
14594 								}
14595 								new_a_time = false;
14596 							}
14597 
14598 							dx_ab = xa[xa_start] - xb[tb_start];
14599 							tx_a = ta_start + fabs ((yc - ya[ta_start]) / del_ya);
14600 							tx_b = tb_start + fabs (dx_ab) * i_del_xb;
14601 							if (tx_a < ta_stop && tx_b < tb_stop) {
14602 								X->x[nx] = xa[xa_start];
14603 								X->y[nx] = yc;
14604 								X->xnode[0][nx] = tx_a;
14605 								X->xnode[1][nx] = tx_b;
14606 								nx++;
14607 							}
14608 						}
14609 					}
14610 				}
14611 				else if (gmt_M_is_zero (del_xb)) {	/* Vertical B segment: Special case */
14612 					dx_ab = xb[xb_start] - xa[xa_start];
14613 					i_del_xa = 1.0 / del_xa;
14614 					yc = ya[xa_start] + dx_ab * del_ya * i_del_xa;
14615 					if (!(yc < yb[B[this_b].start] || yc > yb[B[this_b].stop])) {	/* Did cross within the segment extents */
14616 						/* Only accept xover if occurring before segment end (in time) */
14617 
14618 						if (xb_start < xb_stop) {
14619 							tb_start = xb_start;	/* B Node first in time */
14620 							tb_stop = xb_stop;	/* B Node last in time */
14621 						}
14622 						else {
14623 							tb_start = xb_stop;	/* B Node first in time */
14624 							tb_stop = xb_start;	/* B Node last in time */
14625 						}
14626 						if (new_a_time) {
14627 							if (xa_start < xa_stop) {
14628 								ta_start = xa_start;	/* A Node first in time */
14629 								ta_stop = xa_stop;	/* A Node last in time */
14630 							}
14631 							else {
14632 								ta_start = xa_stop;	/* A Node first in time */
14633 								ta_stop = xa_start;	/* A Node last in time */
14634 							}
14635 							new_a_time = false;
14636 						}
14637 
14638 						dx_ab = xb[xb_start] - xa[ta_start];
14639 						tx_a = ta_start + fabs (dx_ab) * i_del_xa;
14640 						tx_b = tb_start + fabs ((yc - yb[tb_start]) / del_yb);
14641 						if (tx_a < ta_stop && tx_b < tb_stop) {
14642 							X->x[nx] = xb[xb_start];
14643 							X->y[nx] = yc;
14644 							X->xnode[0][nx] = tx_a;
14645 							X->xnode[1][nx] = tx_b;
14646 							nx++;
14647 						}
14648 					}
14649 				}
14650 				else if (gmt_M_is_zero (del_ya)) {	/* Horizontal A segment: Special case */
14651 
14652 					if (gmt_M_is_zero (del_yb)) {	/* Two horizontal segments with some overlap */
14653 						double x4[4];
14654 						/* Assign as crossover the middle of the overlapping segments */
14655 						X->y[nx] = ya[xa_start];
14656 						x4[0] = xa[xa_start];	x4[1] = xa[xa_stop];
14657 						x4[2] = xb[xb_start];	x4[3] = xb[xb_stop];
14658 						gmt_sort_array (GMT, x4, 4, GMT_DOUBLE);
14659 						if (!doubleAlmostEqual (x4[1], x4[2])) {
14660 							X->x[nx] = 0.5 * (x4[1] + x4[2]);
14661 							X->xnode[0][nx] = 0.5 * (xa_start + xa_stop);
14662 							X->xnode[1][nx] = 0.5 * (xb_start + xb_stop);
14663 							nx++;
14664 						}
14665 					}
14666 					else {	/* B is not horizontal so we can solve for intersection */
14667 						i_del_yb = 1.0 / del_yb;
14668 						xc = xb[xb_start] + (ya[xa_start] - yb[xb_start]) * del_xb * i_del_yb;
14669 						if (!(xc < xa[xa_start] || xc > xa[xa_stop])) {	/* Did cross within the segment extents */
14670 
14671 							/* Only accept xover if occurring before segment end (in time) */
14672 
14673 							if (xb_start < xb_stop) {
14674 								tb_start = xb_start;	/* B Node first in time */
14675 								tb_stop = xb_stop;	/* B Node last in time */
14676 							}
14677 							else {
14678 								tb_start = xb_stop;	/* B Node first in time */
14679 								tb_stop = xb_start;	/* B Node last in time */
14680 							}
14681 							if (new_a_time) {
14682 								if (xa_start < xa_stop) {
14683 									ta_start = xa_start;	/* A Node first in time */
14684 									ta_stop = xa_stop;	/* A Node last in time */
14685 								}
14686 								else {
14687 									ta_start = xa_stop;	/* A Node first in time */
14688 									ta_stop = xa_start;	/* A Node last in time */
14689 								}
14690 								new_a_time = false;
14691 							}
14692 
14693 							dx_ab = xc - xa[ta_start];
14694 							tx_a = ta_start + fabs (dx_ab) / del_xa;
14695 							tx_b = tb_start + fabs ((ya[xa_start] - yb[tb_start]) * i_del_yb);
14696 							if (tx_a < ta_stop && tx_b < tb_stop) {
14697 								X->y[nx] = ya[xa_start];
14698 								X->x[nx] = xc;
14699 								X->xnode[0][nx] = tx_a;
14700 								X->xnode[1][nx] = tx_b;
14701 								nx++;
14702 							}
14703 						}
14704 					}
14705 				}
14706 				else if (gmt_M_is_zero (del_yb)) {	/* Horizontal B segment: Special case */
14707 
14708 					i_del_ya = 1.0 / del_ya;
14709 					xc = xa[xa_start] + (yb[xb_start] - ya[xa_start]) * del_xa * i_del_ya;
14710 					if (!(xc < xb[xb_start] || xc > xb[xb_stop])) {	/* Did cross within the segment extents */
14711 
14712 						/* Only accept xover if occurring before segment end (in time) */
14713 
14714 						if (xb_start < xb_stop) {
14715 							tb_start = xb_start;	/* B Node first in time */
14716 							tb_stop = xb_stop;	/* B Node last in time */
14717 						}
14718 						else {
14719 							tb_start = xb_stop;	/* B Node first in time */
14720 							tb_stop = xb_start;	/* B Node last in time */
14721 						}
14722 						if (new_a_time) {
14723 							if (xa_start < xa_stop) {
14724 								ta_start = xa_start;	/* A Node first in time */
14725 								ta_stop = xa_stop;	/* A Node last in time */
14726 							}
14727 							else {
14728 								ta_start = xa_stop;	/* A Node first in time */
14729 								ta_stop = xa_start;	/* A Node last in time */
14730 							}
14731 							new_a_time = false;
14732 						}
14733 
14734 						dx_ab = xc - xb[tb_start];
14735 						tx_a = ta_start + fabs ((yb[xb_start] - ya[ta_start]) * i_del_ya);
14736 						tx_b = tb_start + fabs (dx_ab) / del_xb;
14737 						if (tx_a < ta_stop && tx_b < tb_stop) {
14738 							X->y[nx] = yb[xb_start];
14739 							X->x[nx] = xc;
14740 							X->xnode[0][nx] = tx_a;
14741 							X->xnode[1][nx] = tx_b;
14742 							nx++;
14743 						}
14744 					}
14745 				}
14746 				else if (gmt_M_is_dnan(del_yb)) {}	/* Just do nothing and prevent call to doubleAlmostEqual() which would assert fail with NaNs */
14747 
14748 				else {	/* General case */
14749 
14750 					i_del_xa = 1.0 / del_xa;
14751 					i_del_xb = 1.0 / del_xb;
14752 					slp_a = del_ya * i_del_xa;
14753 					slp_b = del_yb * i_del_xb;
14754 					if (doubleAlmostEqual (slp_a, slp_b)) {	/* Segments are parallel */
14755 						double x4[4];
14756 						/* Assign as possible crossover the middle of the overlapping segments */
14757 						x4[0] = xa[xa_start];	x4[1] = xa[xa_stop];
14758 						x4[2] = xb[xb_start];	x4[3] = xb[xb_stop];
14759 						gmt_sort_array (GMT, x4, 4, GMT_DOUBLE);
14760 						if (!doubleAlmostEqual (x4[1], x4[2])) {
14761 							xc = 0.5 * (x4[1] + x4[2]);
14762 							yc = slp_a * (xc - xa[xa_start]) + ya[xa_start];
14763 							if ((slp_b * (xc - xb[xb_start]) + yb[xb_start]) == yc) {
14764 								X->y[nx] = yc;
14765 								X->x[nx] = xc;
14766 								X->xnode[0][nx] = 0.5 * (xa_start + xa_stop);
14767 								X->xnode[1][nx] = 0.5 * (xb_start + xb_stop);
14768 								nx++;
14769 							}
14770 						}
14771 					}
14772 					else {	/* Segments are not parallel */
14773 						xc = (yb[xb_start] - ya[xa_start] + slp_a * xa[xa_start] - slp_b * xb[xb_start]) / (slp_a - slp_b);
14774 						if (!(xc < xa[xa_start] || xc > xa[xa_stop] || xc < xb[xb_start] || xc > xb[xb_stop])) {	/* Did cross within the segment extents */
14775 
14776 							/* Only accept xover if occurring before segment end (in time) */
14777 
14778 							if (xb_start < xb_stop) {
14779 								tb_start = xb_start;	/* B Node first in time */
14780 								tb_stop = xb_stop;	/* B Node last in time */
14781 							}
14782 							else {
14783 								tb_start = xb_stop;	/* B Node first in time */
14784 								tb_stop = xb_start;	/* B Node last in time */
14785 							}
14786 							if (new_a_time) {
14787 								if (xa_start < xa_stop) {
14788 									ta_start = xa_start;	/* A Node first in time */
14789 									ta_stop = xa_stop;	/* A Node last in time */
14790 								}
14791 								else {
14792 									ta_start = xa_stop;	/* A Node first in time */
14793 									ta_stop = xa_start;	/* A Node last in time */
14794 								}
14795 								new_a_time = false;
14796 							}
14797 
14798 							tx_a = ta_start + fabs (xc - xa[ta_start]) * i_del_xa;
14799 							tx_b = tb_start + fabs (xc - xb[tb_start]) * i_del_xb;
14800 							if (tx_a < ta_stop && tx_b < tb_stop) {
14801 								X->x[nx] = xc;
14802 								X->y[nx] = ya[xa_start] + (xc - xa[xa_start]) * slp_a;
14803 								X->xnode[0][nx] = tx_a;
14804 								X->xnode[1][nx] = tx_b;
14805 								nx++;
14806 							}
14807 						}
14808 					}
14809 				}
14810 				if (nx > 1 && fabs (X->xnode[0][nx-1] - X->xnode[0][nx-2]) < GMT_CONV8_LIMIT && fabs (X->xnode[1][nx-1] - X->xnode[1][nx-2]) < GMT_CONV8_LIMIT) {
14811 					/* Two consecutive crossing of the same node or really close, skip this repeat crossover */
14812 					nx--;
14813 					GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Warning - Two consecutive crossovers appear to be identical, the 2nd one was skipped\n");
14814 				}
14815 				if (nx == nx_alloc) {
14816 					nx_alloc <<= 1;
14817 					gmtsupport_x_alloc (GMT, X, nx_alloc, false);
14818 				}
14819 			} /* End x-overlap */
14820 
14821 			this_b++;
14822 			new_b = true;
14823 
14824 		} /* End y-overlap */
14825 
14826 		if (this_b == n_seg_b) {
14827 			this_a++;
14828 			this_b = (internal) ? this_a : 0;
14829 			new_a = new_b = true;
14830 		}
14831 
14832 	} /* End while loop */
14833 
14834 	if (!sa0) gmt_M_free (GMT, sa);
14835 	if (!sb0) gmt_M_free (GMT, sb);
14836 
14837 	if (nx == 0) gmt_x_free (GMT, X);	/* Nothing to write home about... */
14838 
14839 	return (nx);
14840 }
14841 
14842 /*! . */
gmt_x_free(struct GMT_CTRL * GMT,struct GMT_XOVER * X)14843 void gmt_x_free (struct GMT_CTRL *GMT, struct GMT_XOVER *X) {
14844 	gmt_M_free (GMT, X->x);
14845 	gmt_M_free (GMT, X->y);
14846 	gmt_M_free (GMT, X->xnode[0]);
14847 	gmt_M_free (GMT, X->xnode[1]);
14848 }
14849 
14850 /*! . */
gmtlib_linear_array(struct GMT_CTRL * GMT,double min,double max,double delta,double phase,double ** array)14851 unsigned int gmtlib_linear_array (struct GMT_CTRL *GMT, double min, double max, double delta, double phase, double **array) {
14852 	/* Create an array of values between min and max, with steps delta and given phase.
14853 	   Example: min = 0, max = 9, delta = 2, phase = 1
14854 	   Result: 1, 3, 5, 7, 9
14855 	*/
14856 
14857 	int first, last, i, n;
14858 	double *val = NULL;
14859 
14860 	if (delta <= 0.0) return (0);
14861 
14862 	/* Undo the phase and scale by 1/delta */
14863 	min = (min - phase) / delta;
14864 	max = (max - phase) / delta;
14865 
14866 	/* Look for first value */
14867 	first = irint (floor (min));
14868 	while (min - first > GMT_CONV4_LIMIT) first++;
14869 
14870 	/* Look for last value */
14871 	last = irint (ceil (max));
14872 	while (last - max > GMT_CONV4_LIMIT) last--;
14873 
14874 	n = last - first + 1;
14875 	if (n <= 0) return (0);
14876 
14877 	/* Create an array of n equally spaced elements */
14878 	val = gmt_M_memory (GMT, NULL, n, double);
14879 	for (i = 0; i < n; i++) val[i] = phase + (first + i) * delta;	/* Rescale to original values */
14880 
14881 	*array = val;
14882 
14883 	return (n);
14884 }
14885 
14886 /*! . */
gmtlib_log_array(struct GMT_CTRL * GMT,double min,double max,double delta,double ** array)14887 unsigned int gmtlib_log_array (struct GMT_CTRL *GMT, double min, double max, double delta, double **array) {
14888 	int first, last, i, n, nticks;
14889 	double *val = NULL, tvals[10];
14890 
14891 	/* Because min and max may be tiny values (e.g., 10^-20) we must do all calculations on the log10 (value) */
14892 
14893 	if (gmt_M_is_zero (delta))
14894 		return (0);
14895 	min = d_log10 (GMT, min);
14896 	max = d_log10 (GMT, max);
14897 	first = irint (floor (min));
14898 	last = irint (ceil (max));
14899 
14900 	if (delta < 0) {	/* Coarser than every magnitude */
14901 		n = gmtlib_linear_array (GMT, min, max, fabs (delta), 0.0, array);
14902 		for (i = 0; i < n; i++) (*array)[i] = pow (10.0, (*array)[i]);
14903 		return (n);
14904 	}
14905 
14906 	tvals[0] = 0.0;	/* Common to all */
14907 	switch (lrint (fabs (delta))) {
14908 		case 2:	/* Annotate 1, 2, 5, 10 */
14909 			tvals[1] = d_log10 (GMT, 2.0);
14910 			tvals[2] = d_log10 (GMT, 5.0);
14911 			tvals[3] = 1.0;
14912 			nticks = 3;
14913 			break;
14914 		case 3:	/* Annotate 1, 2, ..., 8, 9, 10 */
14915 			nticks = 9;
14916 			for (i = 1; i <= nticks; i++) tvals[i] = d_log10 (GMT, (double)(i + 1));
14917 			break;
14918 		default:	/* Annotate just 1 and 10 */
14919 			nticks = 1;
14920 			tvals[1] = 1.0;
14921 	}
14922 
14923 	/* Assign memory to array (may be a bit too much) */
14924 	n = (last - first + 1) * nticks + 1;
14925 	val = gmt_M_memory (GMT, NULL, n, double);
14926 
14927 	/* Find the first logarithm larger than min */
14928 	i = 0;
14929 	val[0] = (double) first;
14930 	while (min - val[0] > GMT_CONV4_LIMIT && i < nticks) {
14931 		i++;
14932 		val[0] = first + tvals[i];
14933 	}
14934 
14935 	/* Find the first logarithm larger than max */
14936 	n = 0;
14937 	while (val[n] - max < GMT_CONV4_LIMIT) {
14938 		if (i >= nticks) {
14939 			i -= nticks;
14940 			first++;
14941 		}
14942 		i++; n++;
14943 		val[n] = first + tvals[i];
14944 	}
14945 
14946 	/* val[n] is too large, so we use only the first n, until val[n-1] */
14947 	val = gmt_M_memory (GMT, val, n, double);
14948 
14949 	/* Convert from log10 to values */
14950 	for (i = 0; i < n; i++) val[i] = pow (10.0, val[i]);
14951 
14952 	*array = val;
14953 
14954 	return (n);
14955 }
14956 
14957 /*! . */
gmtlib_log2_array(struct GMT_CTRL * GMT,double min,double max,double delta,double ** array)14958 unsigned int gmtlib_log2_array (struct GMT_CTRL *GMT, double min, double max, double delta, double **array) {
14959 	/* Create an array of values between min and max, with steps delta.
14960 	   However, take log2 first then use the delta as increment in log2 space
14961 	*/
14962 
14963 	int first, last, i, n;
14964 	double *val = NULL;
14965 
14966 	if (delta <= 0.0) return (0);
14967 
14968 	min = d_log2 (GMT, min) / delta;
14969 	max = d_log2 (GMT, max) / delta;
14970 
14971 	/* Look for first value */
14972 	first = irint (floor (min));
14973 	while (min - first > GMT_CONV4_LIMIT) first++;
14974 
14975 	/* Look for last value */
14976 	last = irint (ceil (max));
14977 	while (last - max > GMT_CONV4_LIMIT) last--;
14978 
14979 	n = last - first + 1;
14980 	if (n <= 0) return (0);
14981 
14982 	/* Create an array of n equally spaced elements */
14983 	val = gmt_M_memory (GMT, NULL, n, double);
14984 	for (i = 0; i < n; i++) val[i] = (first + i) * delta;	/* Rescale to original values */
14985 
14986 	/* Convert from log2 to values */
14987 	for (i = 0; i < n; i++) val[i] = pow (2.0, val[i]);
14988 	*array = val;
14989 
14990 	return (n);
14991 }
14992 
14993 /*! . */
gmtlib_pow_array(struct GMT_CTRL * GMT,double min,double max,double delta,unsigned int x_or_y_or_z,double ** array)14994 unsigned int gmtlib_pow_array (struct GMT_CTRL *GMT, double min, double max, double delta, unsigned int x_or_y_or_z, double **array) {
14995 	int i, n = 0;
14996 	double *val = NULL, v0, v1;
14997 
14998 	if (delta <= 0.0) return (0);
14999 
15000 	if (GMT->current.map.frame.axis[x_or_y_or_z].type != GMT_POW) return (gmtlib_linear_array (GMT, min, max, delta, 0.0, array));
15001 
15002 	if (x_or_y_or_z == 0) { /* x-axis */
15003 		GMT->current.proj.fwd_x (GMT, min, &v0);
15004 		GMT->current.proj.fwd_x (GMT, max, &v1);
15005 		n = gmtlib_linear_array (GMT, v0, v1, delta, 0.0, &val);
15006 		for (i = 0; i < n; i++) GMT->current.proj.inv_x (GMT, &val[i], val[i]);
15007 	}
15008 	else if (x_or_y_or_z == 1) { /* y-axis */
15009 		GMT->current.proj.fwd_y (GMT, min, &v0);
15010 		GMT->current.proj.fwd_y (GMT, max, &v1);
15011 		n = gmtlib_linear_array (GMT, v0, v1, delta, 0.0, &val);
15012 		for (i = 0; i < n; i++) GMT->current.proj.inv_y (GMT, &val[i], val[i]);
15013 	}
15014 	else if (x_or_y_or_z == 2) { /* z-axis */
15015 		GMT->current.proj.fwd_z (GMT, min, &v0);
15016 		GMT->current.proj.fwd_z (GMT, max, &v1);
15017 		n = gmtlib_linear_array (GMT, v0, v1, delta, 0.0, &val);
15018 		for (i = 0; i < n; i++) GMT->current.proj.inv_z (GMT, &val[i], val[i]);
15019 	}
15020 	else
15021 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmtlib_pow_array internal error: Invalid side (%d) passed - nothing returned!\n", x_or_y_or_z);
15022 
15023 	*array = val;
15024 
15025 	return (n);
15026 }
15027 
15028 /*! . */
gmtsupport_time_array(struct GMT_CTRL * GMT,double min,double max,double inc,char unit,bool interval,double ** array)15029 GMT_LOCAL uint64_t gmtsupport_time_array (struct GMT_CTRL *GMT, double min, double max, double inc, char unit, bool interval, double **array) {
15030 	/* When T->active is true we must return interval start/stop even if outside min/max range */
15031 	uint64_t n = 0;
15032 	size_t n_alloc = GMT_SMALL_CHUNK;
15033 	struct GMT_MOMENT_INTERVAL I;
15034 	double *val = NULL;
15035 
15036 	val = gmt_M_memory (GMT, NULL, n_alloc, double);
15037 	gmt_M_memset (&I, 1, struct GMT_MOMENT_INTERVAL);
15038 	I.unit = unit;
15039 	I.step = urint (inc);
15040 	gmtlib_moment_interval (GMT, &I, min, true);	/* First time we pass true for initialization */
15041 	while (I.dt[0] <= max) {		/* As long as we are not gone way past the end time */
15042 		if (I.dt[0] >= min || interval) val[n++] = I.dt[0];		/* Was inside region */
15043 		gmtlib_moment_interval (GMT, &I, 0.0, false);			/* Advance to next interval */
15044 		if (n == n_alloc) {					/* Allocate more space */
15045 			n_alloc <<= 1;
15046 			val = gmt_M_memory (GMT, val, n_alloc, double);
15047 		}
15048 	}
15049 	if (interval) val[n++] = I.dt[0];	/* Must get end of interval too */
15050 	val = gmt_M_memory (GMT, val, n, double);
15051 
15052 	*array = val;
15053 
15054 	return (n);
15055 }
15056 
15057 /*! . */
gmtlib_time_array(struct GMT_CTRL * GMT,double min,double max,struct GMT_PLOT_AXIS_ITEM * T,double ** array)15058 unsigned int gmtlib_time_array (struct GMT_CTRL *GMT, double min, double max, struct GMT_PLOT_AXIS_ITEM *T, double **array) {
15059 	/* When T->active is true we must return interval start/stop even if outside min/max range */
15060 	unsigned int n;
15061 	bool interval;
15062 
15063 	if (!T->active) return (0);
15064 	interval = (T->type == 'i' || T->type == 'I');	/* Only true for i/I axis items */
15065 	if (T->unit == 's' && T->interval <= 1.0)
15066 		n = gmtlib_linear_array (GMT, min, max, T->interval, 0.0, array);
15067 	else
15068 		n = (unsigned int)gmtsupport_time_array (GMT, min, max, T->interval, T->unit, interval, array);
15069 
15070 	return (n);
15071 }
15072 
15073 /*! . */
gmtlib_load_custom_annot(struct GMT_CTRL * GMT,struct GMT_PLOT_AXIS * A,char item,double ** xx,char *** labels)15074 unsigned int gmtlib_load_custom_annot (struct GMT_CTRL *GMT, struct GMT_PLOT_AXIS *A, char item, double **xx, char ***labels) {
15075 	/* Reads a file with one or more records of the form
15076 	 * value	types	[label]
15077 	 * where value is the coordinate of the tickmark, types is a combination
15078 	 * of a|i (annot or interval annot), f (tick), or g (gridline).
15079 	 * The a|i will take a label string (or sentence).
15080 	 * The item argument specifies which type to consider [a|i,f,g].  We return
15081 	 * an array with coordinates and labels, and set interval to true if applicable.
15082 	 */
15083 	int nc, error;
15084 	unsigned int save_coltype, save_max_cols_to_read;
15085 	uint64_t row;
15086 	bool text, found, save_trailing;
15087  	unsigned int k = 0, n_annot = 0, n_int = 0;
15088 	double *x = NULL, limit[2] = {-DBL_MAX, +DBL_MAX};
15089 	char **L = NULL, type[GMT_LEN8] = {""}, txt[GMT_BUFSIZ] = {""};
15090 	struct GMT_DATASET *D = NULL;
15091 	struct GMT_DATASEGMENT *S = NULL;
15092 
15093 	/* Temporarily change what data type col one is */
15094 	save_coltype = gmt_get_column_type (GMT, GMT_IN, GMT_X);
15095 	save_trailing = GMT->current.io.trailing_text[GMT_IN];
15096 	save_max_cols_to_read = GMT->current.io.max_cols_to_read;
15097 	gmt_set_column_type (GMT, GMT_IN, GMT_X, gmt_M_type (GMT, GMT_IN, A->id));
15098 	GMT->current.io.trailing_text[GMT_IN] = true;	/* Since we definitively have that here */
15099 	text = ((item == 'a' || item == 'i') && labels);
15100 	if (!GMT->common.R.oblique)	/* Eliminate items outside rectangular w/e/s/n/z0/z1 bounds */
15101 		limit[0] = GMT->common.R.wesn[2*A->id], limit[1] = GMT->common.R.wesn[2*A->id+1];
15102 
15103 	gmt_disable_bghio_opts (GMT);	/* Do not want any -b -g -h -i -o to affect the reading from -F files */
15104 	GMT->current.io.max_cols_to_read = 1;
15105 	if ((error = GMT_Set_Columns (GMT->parent, GMT_IN, 1, GMT_COL_FIX)) != GMT_NOERROR) return (1);
15106 	if ((D = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, A->file_custom, NULL)) == NULL) {
15107 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to open custom annotation file %s!\n", A->file_custom);
15108 		gmt_set_column_type (GMT, GMT_IN, GMT_X, save_coltype);
15109 		return (0);
15110 	}
15111 	gmt_reenable_bghio_opts (GMT);	/* Recover settings provided by user (if -b -g -h -i were used at all) */
15112 	gmt_set_column_type (GMT, GMT_IN, GMT_X, save_coltype);
15113 	GMT->current.io.trailing_text[GMT_IN] = save_trailing;
15114 	GMT->current.io.max_cols_to_read = save_max_cols_to_read;
15115 	S = D->table[0]->segment[0];	/* All we got */
15116 
15117 	x = gmt_M_memory (GMT, NULL, S->n_rows, double);
15118 	if (text) L = gmt_M_memory (GMT, NULL, S->n_rows, char *);
15119 	for (row = 0; row < S->n_rows; row++) {
15120 		nc = sscanf (S->text[row], "%s %[^\n]", type, txt);
15121 		found = ((item == 'a' && (strchr (type, 'a') || strchr (type, 'i'))) || (strchr (type, item) != NULL));
15122 		if (!found) continue;	/* Not the type we were requesting */
15123 		if (S->data[GMT_X][row] < limit[0] || S->data[GMT_X][row] > limit[1]) continue;
15124 		if (strchr (type, 'i')) n_int++;
15125 		if (strchr (type, 'a')) n_annot++;
15126 		x[k] = S->data[GMT_X][row];
15127 		if (text && nc == 2) {
15128 			gmtlib_enforce_rgb_triplets (GMT, txt, GMT_BUFSIZ);	/* If @; is used, make sure the color information passed on to ps_text is in r/b/g format */
15129 			L[k] = strdup (txt);
15130 		}
15131 		k++;
15132 	}
15133 
15134 	if (k == 0) {	/* No such items */
15135 		gmt_M_free (GMT, x);
15136 		if (text) gmt_M_free (GMT, L);
15137 		return (0);
15138 	}
15139 	if (k < S->n_rows) {
15140 		x = gmt_M_memory (GMT, x, k, double);
15141 		if (text) L = gmt_M_memory (GMT, L, k, char *);
15142 	}
15143 	*xx = x;
15144 	if (text) *labels = L;
15145 	GMT_Destroy_Data (GMT->parent, &D);
15146 
15147 	return (k);
15148 }
15149 
15150 /*! . */
gmtlib_coordinate_array(struct GMT_CTRL * GMT,double min,double max,struct GMT_PLOT_AXIS_ITEM * T,double ** array,char *** labels)15151 unsigned int gmtlib_coordinate_array (struct GMT_CTRL *GMT, double min, double max, struct GMT_PLOT_AXIS_ITEM *T, double **array, char ***labels) {
15152 	unsigned int n = 0;
15153 
15154 	if (!T->active) return (0);	/* Nothing to do */
15155 
15156 	if (T->special && GMT->current.map.frame.axis[T->parent].file_custom) {	/* Want custom intervals */
15157 		n = gmtlib_load_custom_annot (GMT, &GMT->current.map.frame.axis[T->parent], (char)tolower((unsigned char) T->type), array, labels);
15158 		return (n);
15159 	}
15160 
15161 	switch (GMT->current.proj.xyz_projection[T->parent]) {
15162 		case GMT_LINEAR:
15163 			n = gmtlib_linear_array (GMT, min, max, gmtlib_get_map_interval (GMT, GMT_LINEAR, T), GMT->current.map.frame.axis[T->parent].phase, array);
15164 			break;
15165 		case GMT_LOG10:
15166 			n = gmtlib_log_array (GMT, min, max, gmtlib_get_map_interval (GMT, GMT_LOG10, T), array);
15167 			break;
15168 		case GMT_POW:
15169 			n = gmtlib_pow_array (GMT, min, max, gmtlib_get_map_interval (GMT, GMT_POW, T), T->parent, array);
15170 			break;
15171 		case GMT_TIME:
15172 			n = gmtlib_time_array (GMT, min, max, T, array);
15173 			break;
15174 		default:
15175 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtlib_coordinate_array internal error: Invalid projection type (%d) passed - nothing returned!\n", GMT->current.proj.xyz_projection[T->parent]);
15176 			return 0;
15177 			break;
15178 	}
15179 	return (n);
15180 }
15181 
15182 /*! . */
gmtlib_annot_pos(struct GMT_CTRL * GMT,double min,double max,struct GMT_PLOT_AXIS_ITEM * T,double coord[],double * pos)15183 bool gmtlib_annot_pos (struct GMT_CTRL *GMT, double min, double max, struct GMT_PLOT_AXIS_ITEM *T, double coord[], double *pos) {
15184 	/* Calculates the location of the next annotation in user units.  This is
15185 	 * trivial for tick annotations but can be tricky for interval annotations
15186 	 * since the annotation location is not necessarily centered on the interval.
15187 	 * For instance, if our interval is 3 months we do not want "January" centered
15188 	 * on that quarter.  If the position is outside our range we return true
15189 	 */
15190 	bool is_interval = (T->type == 'i' || T->type == 'I');
15191 	double range, start, stop;
15192 
15193 	if (is_interval && T->special) {
15194 		range = 0.5 * (coord[1] - coord[0]);	/* Half width of interval in internal representation */
15195 		start = MAX (min, coord[0]);			/* Start of interval, but not less that start of axis */
15196 		stop  = MIN (max, coord[1]);			/* Stop of interval,  but not beyond end of axis */
15197 		if ((stop - start) < (GMT->current.setting.time_interval_fraction * range)) return (true);		/* Sorry, fraction not large enough to annotate */
15198 		*pos = 0.5 * (start + stop);				/* Set half-way point */
15199 		if (((*pos) - GMT_CONV8_LIMIT) < min || ((*pos) + GMT_CONV8_LIMIT) > max) return (true);	/* Outside axis range */
15200 	}
15201 	else if (is_interval) {
15202 		if (gmt_M_uneven_interval (T->unit) || T->interval != 1.0) {	/* Must find next month to get month centered correctly */
15203 			struct GMT_MOMENT_INTERVAL Inext;
15204 			gmt_M_memset (&Inext, 1, struct GMT_MOMENT_INTERVAL);	/* Wipe it */
15205 			Inext.unit = T->unit;		/* Initialize MOMENT_INTERVAL structure members */
15206 			Inext.step = 1;
15207 			gmtlib_moment_interval (GMT, &Inext, coord[0], true);	/* Get this one interval only */
15208 			range = 0.5 * (Inext.dt[1] - Inext.dt[0]);	/* Half width of interval in internal representation */
15209 			start = MAX (min, Inext.dt[0]);			/* Start of interval, but not less that start of axis */
15210 			stop  = MIN (max, Inext.dt[1]);			/* Stop of interval,  but not beyond end of axis */
15211 		}
15212 		else {
15213 			range = 0.5 * (coord[1] - coord[0]);	/* Half width of interval in internal representation */
15214 			start = MAX (min, coord[0]);			/* Start of interval, but not less that start of axis */
15215 			stop  = MIN (max, coord[1]);			/* Stop of interval,  but not beyond end of axis */
15216 		}
15217 		if ((stop - start) < (GMT->current.setting.time_interval_fraction * range)) return (true);		/* Sorry, fraction not large enough to annotate */
15218 		*pos = 0.5 * (start + stop);				/* Set half-way point */
15219 		if (((*pos) - GMT_CONV8_LIMIT) < min || ((*pos) + GMT_CONV8_LIMIT) > max) return (true);	/* Outside axis range */
15220 	}
15221 	else if (coord[0] < (min - GMT_CONV8_LIMIT) || coord[0] > (max + GMT_CONV8_LIMIT))		/* Outside axis range */
15222 		return (true);
15223 	else if (T->parent == GMT->current.io.cycle_col && GMT->current.io.cycle_interval) {
15224 		*pos = coord[0] + 0.5;	/* Since we know this only happens for "months" (all same width) or weekdays */
15225 		if (*pos > max) return (true);
15226 	}
15227 	else
15228 		*pos = coord[0];
15229 
15230 	return (false);
15231 }
15232 
15233 /*! . */
gmtlib_get_coordinate_label(struct GMT_CTRL * GMT,char * string,struct GMT_PLOT_CALCLOCK * P,char * format,struct GMT_PLOT_AXIS_ITEM * T,double coord,double delta)15234 int gmtlib_get_coordinate_label (struct GMT_CTRL *GMT, char *string, struct GMT_PLOT_CALCLOCK *P, char *format, struct GMT_PLOT_AXIS_ITEM *T, double coord, double delta) {
15235 	/* Returns the formatted annotation string for the non-geographic axes */
15236 	bool upper = false;
15237 	unsigned int kind = 0, axis, code;
15238 	enum gmt_col_enum type;
15239 	int ival;
15240 
15241 	switch (GMT->current.map.frame.axis[T->parent].type) {
15242 		case GMT_LINEAR:
15243 #if 0
15244 			GMT_near_zero_roundoff_fixer_upper (&coord, T->parent);	/* Try to adjust those ~0 "gcc -O" values to exact 0 */
15245 #endif
15246 			code = (GMT->current.io.cycle_col == GMT->current.map.frame.axis[T->parent].id) ? GMT->current.io.cycle_operator : 0;
15247 			switch (code) {	/* Do special treatment for annual and weekly annotations since they are texts but axis is not a time-axis per se... */
15248 				case GMT_CYCLE_WEEK:		/* Must do days of the week or abbreviation */
15249 					gmtlib_set_case_and_kind (GMT, GMT->current.setting.format_time[GMT_PRIMARY], &upper, &kind);
15250 					ival = (irint (coord) + GMT->current.setting.time_week_start + GMT_WEEK2DAY_I) % GMT_WEEK2DAY_I;	/* Wrap around */
15251 					strncpy (string, GMT->current.language.day_name[kind][ival], GMT_LEN16);
15252 					if (upper) gmt_str_toupper (string);
15253 					break;
15254 				case GMT_CYCLE_ANNUAL:	/* Must write month name or abbreviation */
15255 					gmtlib_set_case_and_kind (GMT, GMT->current.setting.format_time[GMT_PRIMARY], &upper, &kind);
15256 					ival = (urint (coord) + 12) % 12;	/* Wrap around */
15257 					strncpy (string, GMT->current.language.month_name[kind][ival], GMT_LEN16);
15258 					if (upper) gmt_str_toupper (string);
15259 					break;
15260 				default:
15261 					if ((type = gmt_get_column_type (GMT, GMT_IN, T->parent)) & GMT_IS_GEO) {	/* Actually a geographic coordinate */
15262 						bool do_minutes = false, do_seconds = false;
15263 						if (!gmt_M_is_dnan (delta)) {
15264 							do_minutes = (fabs (fmod (delta, 1.0)) > GMT_CONV4_LIMIT);
15265 							do_seconds = gmtlib_set_do_seconds (GMT, delta);
15266 						}
15267 						axis = (type & GMT_IS_LON) ? GMT_X : GMT_Y;
15268 						gmtlib_get_annot_label (GMT, coord, string, do_minutes, do_seconds, true, axis, GMT->current.map.is_world);
15269 					}
15270 					else
15271 						gmt_sprintf_float (GMT, string, format, coord);
15272 					break;
15273 			}
15274 			break;
15275 		case GMT_LOG10:
15276 			sprintf (string, "%ld", lrint (d_log10 (GMT, coord)));
15277 			break;
15278 		case GMT_POW:
15279 			if (GMT->current.proj.xyz_projection[T->parent] == GMT_POW)
15280 				sprintf (string, format, coord);
15281 			else
15282 				sprintf (string, "10@+%ld@+", lrint (d_log10 (GMT, coord)));
15283 			break;
15284 		case GMT_TIME:
15285 			gmtlib_get_time_label (GMT, string, P, T, coord);
15286 			break;
15287 		default:
15288 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtlib_get_coordinate_label internal error: Wrong type (%d) passed! No label set\n", GMT->current.map.frame.axis[T->parent].type);
15289 			GMT->parent->error = GMT_NOT_A_VALID_TYPE;
15290 			string[0] = '\0';
15291 			break;
15292 	}
15293 	return (GMT_OK);
15294 }
15295 
gmtlib_polar_prepare_label(struct GMT_CTRL * GMT,double angle,unsigned int side,double * line_angle,double * text_angle,unsigned int * justify)15296 int gmtlib_polar_prepare_label (struct GMT_CTRL *GMT, double angle, unsigned int side, double *line_angle, double *text_angle, unsigned int *justify) {
15297 	/* Special function to set justification and text angle for polar projections (r/theta, i.e. GMT_POLAR) */
15298 	/* Normally y-min (south) is at r = 0 and y-max (north) is on the perimeter, but +f reverses that */
15299 	/* Normally x-min (west) is the boundary with the region on the leeft, and x-max (east) on the right, but +a reverses that */
15300 	*line_angle = angle;
15301 	if (GMT->current.proj.flip && side%2 == 0) /* Flipping means south is on the outside and north is the inside */
15302 		side = 2 - side;	/* Turns 2 to 0 and 0 to 2 */
15303 
15304 	switch (side) {
15305 		case S_SIDE:	/* We are annotating angles on the inner (donut) boundary */
15306 			if (gmt_M_is_zero (angle) || ((angle+GMT_CONV8_LIMIT) >= 180.0 && (angle-GMT_CONV8_LIMIT) < 360.0)) *justify = 10, *text_angle = angle - 270.0;
15307 			else *justify = 2, *text_angle = angle - 90;
15308 			break;
15309 		case E_SIDE:
15310 			if ((angle+GMT_CONV8_LIMIT) >= 90.0 && (angle-GMT_CONV8_LIMIT) < 270.0) *justify = 7, *text_angle = angle - 180;
15311 			else  *justify = 5, *text_angle = angle;
15312 			break;
15313 		case N_SIDE:	/* We are annotating angles on the outer boundary */
15314 			if ((angle+GMT_CONV8_LIMIT) >= 0.0 && (angle-GMT_CONV8_LIMIT) <= 180.0) *justify = 2, *text_angle = angle - 90.0;
15315 			else *justify = 10, *text_angle = angle + 90.0;
15316 			break;
15317 		case W_SIDE:
15318 			if ((angle+GMT_CONV8_LIMIT) >= 90.0 && (angle-GMT_CONV8_LIMIT) < 270.0) *justify = 7, *text_angle = angle - 180;
15319 			else  *justify = 5, *text_angle = angle;
15320 			break;
15321 	}
15322 	return (0);
15323 }
15324 
15325 /*! . */
gmtlib_prepare_label(struct GMT_CTRL * GMT,double angle,unsigned int side,double x,double y,unsigned int type,double * line_angle,double * text_angle,unsigned int * justify)15326 int gmtlib_prepare_label (struct GMT_CTRL *GMT, double angle, unsigned int side, double x, double y, unsigned int type, double *line_angle, double *text_angle, unsigned int *justify) {
15327 	bool set_angle;
15328 
15329 	if (!GMT->current.proj.edge[side]) return -1;		/* Side doesn't exist */
15330 	if (GMT->current.map.frame.side[side] < GMT_AXIS_ANNOT) return -1;	/* Don't want labels here */
15331 
15332 	if (GMT->current.map.frame.check_side == true && type != side%2) return -1;
15333 
15334 	if (GMT->current.proj.projection_GMT == GMT_POLAR) return (gmtlib_polar_prepare_label (GMT, angle, side, line_angle, text_angle, justify));
15335 
15336 	if (GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_NORMAL_TICKS && !(side%2)) angle = -90.0;	/* gmtsupport_get_label_parameters will make this 0 */
15337 
15338 	if (angle < 0.0) angle += 360.0;
15339 
15340 	set_angle = ((!GMT->common.R.oblique && !(gmt_M_is_azimuthal(GMT) || gmt_M_is_conical(GMT))) || GMT->common.R.oblique);
15341 	if (!GMT->common.R.oblique && (GMT->current.proj.projection_GMT == GMT_GENPER || GMT->current.proj.projection_GMT == GMT_GNOMONIC || GMT->current.proj.projection_GMT == GMT_POLYCONIC)) set_angle = true;
15342 	if (set_angle) {
15343 		if (side == 0 && angle < 180.0) angle -= 180.0;
15344 		if (side == 1 && (angle > 90.0 && angle < 270.0)) angle -= 180.0;
15345 		if (side == 2 && angle > 180.0) angle -= 180.0;
15346 		if (side == 3 && (angle < 90.0 || angle > 270.0)) angle -= 180.0;
15347 	}
15348 
15349 	if (!gmtsupport_get_label_parameters (GMT, side, angle, type, text_angle, justify)) return -1;
15350 	*line_angle = angle;
15351 	if (GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_NORMAL_TICKS) *line_angle = (side - 1) * 90.0;
15352 
15353 	if (!set_angle) *justify = gmtsupport_polar_adjust (GMT, side, angle, x, y);
15354 	if (set_angle && !GMT->common.R.oblique && GMT->current.proj.projection_GMT == GMT_GNOMONIC) {
15355 		/* Fix until we write something that works for everything.  This is a global gnomonic map
15356 		 * so it is easy to fix the angles.  We get correct justify and make sure
15357 		 * the line_angle points away from the boundary */
15358 
15359 		angle = fmod (2.0 * angle, 360.0) / 2.0;	/* 0-180 range */
15360 		if (angle > 90.0) angle -= 180.0;
15361 		*justify = gmtsupport_gnomonic_adjust (GMT, angle, x, y);
15362 		if (*justify == 7) *line_angle += 180.0;
15363 	}
15364 
15365 	return 0;
15366 }
15367 
15368 /*! . */
gmtlib_get_annot_label(struct GMT_CTRL * GMT,double val,char * label,bool do_minutes,bool do_seconds,bool do_hemi,unsigned int lonlat,bool worldmap)15369 void gmtlib_get_annot_label (struct GMT_CTRL *GMT, double val, char *label, bool do_minutes, bool do_seconds, bool do_hemi, unsigned int lonlat, bool worldmap) {
15370 /* val:		Degree value of annotation */
15371 /* label:	String to hold the final annotation */
15372 /* do_minutes:	true if degree and minutes are desired, false for just integer degrees */
15373 /* do_seconds:	true if degree, minutes, and seconds are desired */
15374 /* do_hemi:	true if compass headings (W, E, S, N) are desired */
15375 /* lonlat:	0 = longitudes, 1 = latitudes, 2 non-geographical data passed, 3 = latitudes in longitude positions, 4 = great-circle degrees */
15376 /* worldmap:	T/F, whatever GMT->current.map.is_world is */
15377 
15378 	int sign, d, m, s, m_sec;
15379 	unsigned int k, n_items, level, type;
15380 	bool zero_fix = false, lat_special = (lonlat == 3), deg_special = (lonlat == 4);
15381 	char hemi[GMT_LEN16] = {""};
15382 
15383 	/* Must override do_minutes and/or do_seconds if format uses decimal notation for that item */
15384 
15385 	if (GMT->current.plot.calclock.geo.order[1] == -1) do_minutes = false;
15386 	if (GMT->current.plot.calclock.geo.order[2] == -1) do_seconds = false;
15387 	for (k = n_items = 0; k < 3; k++) if (GMT->current.plot.calclock.geo.order[k] >= 0) n_items++;	/* How many of d, m, and s are requested as integers */
15388 
15389 	if (lat_special) lonlat = 1;	/* Remove the special flag */
15390 	if (deg_special) lonlat = 0;	/* Remove the special flag */
15391 	if (lonlat == 0) {	/* Fix longitudes range first */
15392 		gmt_lon_range_adjust (GMT->current.plot.calclock.geo.range, &val);
15393 	}
15394 
15395 	if (lonlat) {	/* i.e., for geographical data */
15396 		if (doubleAlmostEqual (val, 360.0) && !worldmap)
15397 			val = 0.0;
15398 		if (doubleAlmostEqual (val, 360.0) && worldmap && GMT->current.proj.projection_GMT == GMT_OBLIQUE_MERC)
15399 			val = 0.0;
15400 	}
15401 
15402 	if (GMT->current.plot.calclock.geo.wesn) {
15403 		if (GMT->current.plot.calclock.geo.wesn == 2) strcat (hemi, " ");
15404 		if (!do_hemi || gmt_M_is_zero (val)) { /* Skip adding hemisphere indication */
15405 		}
15406 		else if (lonlat == 0) {
15407 			switch (GMT->current.plot.calclock.geo.range) {
15408 				case GMT_IS_0_TO_P360_RANGE:
15409 				case GMT_IS_0_TO_P360:
15410 					strcat (hemi, GMT->current.language.cardinal_name[1][1]);
15411 					break;
15412 				case GMT_IS_M360_TO_0_RANGE:
15413 				case GMT_IS_M360_TO_0:
15414 					strcat (hemi, GMT->current.language.cardinal_name[2][1]);
15415 					break;
15416 				default:
15417 					if (!(doubleAlmostEqual (val, 180.0) || doubleAlmostEqual (val, -180.0))) strcat (hemi, (val < 0.0) ? GMT->current.language.cardinal_name[1][0] : GMT->current.language.cardinal_name[1][1]);
15418 					break;
15419 			}
15420 		}
15421 		else
15422 			strcat (hemi, (val < 0.0) ? GMT->current.language.cardinal_name[2][2] : GMT->current.language.cardinal_name[2][3]);
15423 
15424 		if (!deg_special) {
15425 			val = fabs (val);
15426 			if (hemi[0] == ' ' && hemi[1] == 0) hemi[0] = 0;	/* No space if no hemisphere indication */
15427 		}
15428 	}
15429 	if (deg_special)
15430 		hemi[0] = 0;
15431 	else if (GMT->current.plot.calclock.geo.no_sign)
15432 		val = fabs (val);
15433 	sign = (val < 0.0) ? -1 : 1;
15434 
15435 	level = do_minutes + do_seconds;		/* 0, 1, or 2 */
15436 	type = (GMT->current.plot.calclock.geo.n_sec_decimals > 0) ? 1 : 0;
15437 
15438 	if (!(lat_special || deg_special) && GMT->current.plot.r_theta_annot && lonlat)	/* Special check for the r in r-theta [set via -Jp|P +fe modifier] */
15439 		gmt_sprintf_float (GMT, label, GMT->current.setting.format_float_map, val);
15440 	else if (GMT->current.plot.calclock.geo.decimal)
15441 		sprintf (label, GMT->current.plot.calclock.geo.x_format, val, hemi);
15442 	else {
15443 		(void) gmtlib_geo_to_dms (val, n_items, GMT->current.plot.calclock.geo.f_sec_to_int, &d, &m, &s, &m_sec);	/* Break up into d, m, s, and remainder */
15444 		if (d == 0 && sign == -1) {	/* Must write out -0 degrees, do so by writing -1 and change 1 to 0 */
15445 			d = -1;
15446 			zero_fix = true;
15447 		}
15448 		switch (2*level+type) {
15449 			case 0:
15450 				sprintf (label, GMT->current.plot.format[level][type], d, hemi);
15451 				break;
15452 			case 1:
15453 				sprintf (label, GMT->current.plot.format[level][type], d, m_sec, hemi);
15454 				break;
15455 			case 2:
15456 				sprintf (label, GMT->current.plot.format[level][type], d, m, hemi);
15457 				break;
15458 			case 3:
15459 				sprintf (label, GMT->current.plot.format[level][type], d, m, m_sec, hemi);
15460 				break;
15461 			case 4:
15462 				sprintf (label, GMT->current.plot.format[level][type], d, m, s, hemi);
15463 				break;
15464 			case 5:
15465 				sprintf (label, GMT->current.plot.format[level][type], d, m, s, m_sec, hemi);
15466 				break;
15467 		}
15468 		if (zero_fix) label[1] = '0';	/* Undo the fix above */
15469 	}
15470 
15471 	return;
15472 }
15473 
15474 /*! . */
gmt_flip_justify(struct GMT_CTRL * GMT,unsigned int justify)15475 int gmt_flip_justify (struct GMT_CTRL *GMT, unsigned int justify) {
15476 	/* Return the mirror-image justification */
15477 
15478 	int j;
15479 
15480 	switch (justify) {
15481 		case 1:	 j = 11;	break;
15482 		case 2:	 j = 10;	break;
15483 		case 3:	 j = 9;		break;
15484 		case 5:	 j = 7;		break;
15485 		case 6:	 j = 6;		break;
15486 		case 7:	 j = 5;		break;
15487 		case 9:  j = 3;		break;
15488 		case 10: j = 2;		break;
15489 		case 11: j = 1;		break;
15490 		default:
15491 			j = justify;
15492 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmt_flip_justify called with incorrect argument (%d)\n", j);
15493 			break;
15494 	}
15495 
15496 	return (j);
15497 }
15498 
15499 /*! . */
gmtlib_get_custom_symbol(struct GMT_CTRL * GMT,char * name)15500 struct GMT_CUSTOM_SYMBOL * gmtlib_get_custom_symbol (struct GMT_CTRL *GMT, char *name) {
15501 	unsigned int i;
15502 	int found = -1;
15503 
15504 	/* First see if we already have loaded this symbol */
15505 
15506 	for (i = 0; found == -1 && i < GMT->init.n_custom_symbols; i++) if (!strcmp (name, GMT->init.custom_symbol[i]->name)) found = i;
15507 
15508 	if (found >= 0) return (GMT->init.custom_symbol[found]);	/* Return a previously loaded symbol */
15509 
15510 	/* Must load new symbol */
15511 
15512 	GMT->init.custom_symbol = gmt_M_memory (GMT, GMT->init.custom_symbol, GMT->init.n_custom_symbols+1, struct GMT_CUSTOM_SYMBOL *);
15513 	if (gmtsupport_init_custom_symbol (GMT, name, &(GMT->init.custom_symbol[GMT->init.n_custom_symbols]))) return NULL;
15514 
15515 	return (GMT->init.custom_symbol[GMT->init.n_custom_symbols++]);
15516 }
15517 
15518 /*! . */
gmtlib_free_custom_symbols(struct GMT_CTRL * GMT)15519 void gmtlib_free_custom_symbols (struct GMT_CTRL *GMT) {
15520 	/* Free the allocated list of custom symbols */
15521 	unsigned int i;
15522 
15523 	if (GMT->init.n_custom_symbols == 0) return;
15524 	for (i = 0; i < GMT->init.n_custom_symbols; i++)
15525 		gmtsupport_free_one_custom_symbol (GMT, GMT->init.custom_symbol[i]);
15526 	gmt_M_free (GMT, GMT->init.custom_symbol);
15527 	GMT->init.n_custom_symbols = 0;
15528 }
15529 
15530 /*! . */
gmt_polygon_is_open(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n)15531 bool gmt_polygon_is_open (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n) {
15532 	/* Returns true if the first and last point is not identical */
15533 	if (n < 3) return false;	/*	A single point or a line is by definition closed */
15534 	if (y == NULL) return true;	/*	A single vector is by definition open */
15535 	if (!doubleAlmostEqualZero (y[0], y[n-1]))
15536 		return true;	/* y difference exceeds threshold: polygon is OPEN */
15537 	if (!doubleAlmostEqualZero (x[0], x[n-1])) {	/* The x values exceeds threshold, check further if by 360 under geo */
15538 		if (gmt_M_type (GMT, GMT_IN, GMT_X) & GMT_IS_GEO) {	/* Geographical coordinates: Worry about a 360 jump */
15539 			double dlon = fabs (x[0] - x[n-1]);	/* If exactly 360 then we are OK */
15540 			if (!doubleAlmostEqualZero (dlon, 360.0))
15541 				return true;	/* x difference exceeds threshold for an exact 360 offset: polygon is OPEN */
15542 		}
15543 		else	/* Cartesian case */
15544 			return true;	/* x difference exceeds threshold: polygon is OPEN */
15545 	}
15546 	/* Here, first and last are ~identical - to be safe we enforce exact closure */
15547 	x[n-1] = x[0];	y[n-1] = y[0];	/* Note: For geo data, this step may change a 0 to 360 or vice versa */
15548 	return false;	/* Passed the tests so polygon is CLOSED */
15549 }
15550 
15551 /*! . */
gmt_polygon_centroid(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,double * Cx,double * Cy)15552 int gmt_polygon_centroid (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, double *Cx, double *Cy) {
15553 	/* Compute polygon centroid location. */
15554 	uint64_t i, last;
15555 	double A, d, xold, yold;
15556 
15557 	A = gmtsupport_polygon_area (GMT, x, y, n);
15558 	last = (n > 2 && gmt_polygon_is_open (GMT, x, y, n)) ? n : n - 1;	/* Skip repeating vertex */
15559 	*Cx = *Cy = 0.0;
15560 	xold = x[last-1];	yold = y[last-1];
15561 	for (i = 0; i < last; i++) {
15562 		d = (xold * y[i] - x[i] * yold);
15563 		*Cx += (x[i] + xold) * d;
15564 		*Cy += (y[i] + yold) * d;
15565 		xold = x[i];	yold = y[i];
15566 	}
15567 	*Cx /= (6.0 * A);
15568 	*Cy /= (6.0 * A);
15569 	return ((A < 0.0) ? -1 : +1);	/* -1 means CCW, +1 means CW */
15570 }
15571 
15572 /*! . */
gmt_prep_nodesearch(struct GMT_CTRL * GMT,struct GMT_GRID * G,double radius,unsigned int mode,unsigned int * d_row,unsigned int * actual_max_d_col)15573 unsigned int * gmt_prep_nodesearch (struct GMT_CTRL *GMT, struct GMT_GRID *G, double radius, unsigned int mode, unsigned int *d_row, unsigned int *actual_max_d_col) {
15574 	/* When we search all nodes within a radius R on a grid, we first rule out nodes that are
15575 	 * outside the circumscribing rectangle.  However, for geographic data the circle becomes
15576 	 * elliptical in lon/lat space due to the cos(lat) shrinking of the length of delta_lon.
15577 	 * Thus, while the +- width of nodes in y is fixed (d_row), the +-width of nodes in x
15578 	 * is a function of latitude.  This is the array d_col (which is constant for Cartesian data).
15579 	 * We expect gmt_init_distaz has been called so the gmt_distance function returns distances
15580 	 * in the same units as the radius.  We also return the widest value in the d_col array via
15581 	 * the actual_max_d_col value.
15582 	 */
15583 	unsigned int max_d_col, row, *d_col = gmt_M_memory (GMT, NULL, G->header->n_rows, unsigned int);
15584 	double dist_x, dist_y, lon, lat;
15585 
15586 	lon = G->header->wesn[XLO] + G->header->inc[GMT_X];
15587 
15588 	dist_y = gmt_distance (GMT, G->header->wesn[XLO], G->header->wesn[YLO], G->header->wesn[XLO], G->header->wesn[YLO] + G->header->inc[GMT_Y]);
15589 	if (mode) {	/* Input data is geographical, so circle widens with latitude due to cos(lat) effect */
15590 		max_d_col = urint (ceil (G->header->n_columns / 2.0) + 0.1);	/* Upper limit on +- halfwidth */
15591 		*actual_max_d_col = 0;
15592 		for (row = 0; row < G->header->n_rows; row++) {
15593 			lat = gmt_M_grd_row_to_y (GMT, row, G->header);
15594 			/* Determine longitudinal width of one grid ell at this latitude */
15595 			dist_x = gmt_distance (GMT, G->header->wesn[XLO], lat, lon, lat);
15596 			d_col[row] = (fabs (lat) == 90.0) ? max_d_col : urint (ceil (radius / dist_x) + 0.1);
15597 			if (d_col[row] > max_d_col) d_col[row] = max_d_col;	/* Safety valve */
15598 			if (d_col[row] > (*actual_max_d_col)) *actual_max_d_col = d_col[row];
15599 		}
15600 	}
15601 	else {	/* Plain Cartesian data with rectangular box */
15602 		dist_x = gmt_distance (GMT, G->header->wesn[XLO], G->header->wesn[YLO], lon, G->header->wesn[YLO]);
15603 		*actual_max_d_col = max_d_col = urint (ceil (radius / dist_x) + 0.1);
15604 		for (row = 0; row < G->header->n_rows; row++) d_col[row] = max_d_col;
15605 	}
15606 	*d_row = urint (ceil (radius / dist_y) + 0.1);	/* The constant half-width of nodes in y-direction */
15607 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Max node-search half-widths are: half_x = %d, half_y = %d\n", *d_row, *actual_max_d_col);
15608 	return (d_col);		/* The (possibly variable) half-width of nodes in x-direction as function of y */
15609 }
15610 
15611 /* THese three functions are used by grdmath and gmtmath only */
15612 
15613 /*! . */
gmt_load_macros(struct GMT_CTRL * GMT,char * mtype,struct GMT_MATH_MACRO ** M)15614 int gmt_load_macros (struct GMT_CTRL *GMT, char *mtype, struct GMT_MATH_MACRO **M) {
15615 	/* Load in any gmt/grdmath macros.  These records are of the format
15616 	 * MACRO = ARG1 ARG2 ... ARGN [ : comments on what they do]
15617 	 * The comments, if present, must be preceded by :<space> to distinguish
15618 	 * the flag from any dd:mm:ss or hh:mm:ss constants used in the macro. */
15619 
15620 	unsigned int n = 0, k = 0, pos = 0;
15621 	size_t n_alloc = 0;
15622 	char line[GMT_BUFSIZ] = {""}, name[GMT_LEN64] = {""}, item[GMT_LEN64] = {""}, args[GMT_BUFSIZ] = {""}, *c = NULL;
15623 	struct GMT_MATH_MACRO *macro = NULL;
15624 	FILE *fp = NULL;
15625 
15626 	if (!gmtlib_getuserpath (GMT, mtype, line)) return (0);
15627 
15628 	if ((fp = fopen (line, "r")) == NULL) {
15629 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to open %s macro file\n", line);
15630 		return -1;
15631 	}
15632 
15633 	while (fgets (line, GMT_BUFSIZ, fp)) {
15634 		if (line[0] == '#') continue;
15635 		gmt_chop (line);
15636 		if (gmt_is_a_blank_line (line)) continue;
15637 		if ((c = strstr (line, ": ")))	/* This macro has comments */
15638 			c[0] = '\0';		/* Chop off the comments */
15639 		gmt_strstrip (line, true);	/* Remove leading and trailing whitespace */
15640 		sscanf (line, "%s = %[^\n]", name, args);	/* Get name and everything else */
15641 		if (n == n_alloc) macro = gmt_M_memory (GMT, macro, n_alloc += GMT_TINY_CHUNK, struct GMT_MATH_MACRO);
15642 		macro[n].name = strdup (name);
15643 		pos = 0;
15644 		while (gmt_strtok (args, " \t", &pos, item)) macro[n].n_arg++;		/* Count the arguments */
15645 		macro[n].arg = gmt_M_memory (GMT, macro[n].arg, macro[n].n_arg, char *);	/* Allocate pointers for args */
15646 		pos = k = 0;
15647 		while (gmt_strtok (args, " \t", &pos, item)) macro[n].arg[k++] = strdup (item);	/* Assign arguments */
15648 		n++;
15649 	}
15650 	fclose (fp);
15651 	if (n < n_alloc) macro = gmt_M_memory (GMT, macro, n, struct GMT_MATH_MACRO);
15652 
15653 	*M = macro;
15654 	return (n);
15655 }
15656 
15657 /*! . */
gmt_find_macro(char * arg,unsigned int n_macros,struct GMT_MATH_MACRO * M)15658 int gmt_find_macro (char *arg, unsigned int n_macros, struct GMT_MATH_MACRO *M) {
15659 	/* See if the arg matches the name of a macro; return its ID or -1 */
15660 
15661 	unsigned int n;
15662 
15663 	if (n_macros == 0 || !M) return (GMT_NOTSET);
15664 
15665 	for (n = 0; n < n_macros; n++) if (!strcmp (arg, M[n].name)) return (n);
15666 
15667 	return (GMT_NOTSET);
15668 }
15669 
15670 /*! . */
gmt_free_macros(struct GMT_CTRL * GMT,unsigned int n_macros,struct GMT_MATH_MACRO ** M)15671 void gmt_free_macros (struct GMT_CTRL *GMT, unsigned int n_macros, struct GMT_MATH_MACRO **M) {
15672 	/* Free space allocated for macros */
15673 
15674 	unsigned int n, k;
15675 
15676 	if (n_macros == 0 || !(*M)) return;
15677 
15678 	for (n = 0; n < n_macros; n++) {
15679 		gmt_M_str_free ((*M)[n].name);
15680 		for (k = 0; k < (*M)[n].n_arg; k++) gmt_M_str_free ((*M)[n].arg[k]);	/* Free arguments */
15681 		gmt_M_free (GMT, (*M)[n].arg);	/* Free argument list */
15682 	}
15683 	gmt_M_free (GMT, (*M));
15684 }
15685 
15686 /*! . */
gmt_substitute_macros(struct GMT_CTRL * GMT,struct GMT_OPTION * options,char * mfile)15687 struct GMT_OPTION *gmt_substitute_macros (struct GMT_CTRL *GMT, struct GMT_OPTION *options, char *mfile) {
15688 	unsigned int n_macros, kk;
15689 	int k;
15690 	struct GMT_MATH_MACRO *M = NULL;
15691 	struct GMT_OPTION *opt = NULL, *ptr = NULL, *list = NULL;
15692 	struct GMTAPI_CTRL *API = GMT->parent;
15693 
15694 	n_macros = gmt_load_macros (GMT, mfile, &M);	/* Load in any macros */
15695 	if (n_macros) GMT_Report (API, GMT_MSG_INFORMATION, "Found and loaded %d user macros.\n", n_macros);
15696 
15697 	/* Expand any macro with its building blocks */
15698 
15699 	for (opt = options; opt; opt = opt->next) {
15700 		if (opt->option == GMT_OPT_INFILE && (k = gmt_find_macro (opt->arg, n_macros, M)) != GMT_NOTSET) {
15701 			/* Add in the replacement commands from the macro */
15702 			for (kk = 0; kk < M[k].n_arg; kk++) {
15703 				ptr = GMT_Make_Option (API, GMT_OPT_INFILE, M[k].arg[kk]);
15704 				if ((list = GMT_Append_Option (API, ptr, list)) == NULL) {
15705 					gmt_free_macros (GMT, n_macros, &M);
15706 					return (NULL);
15707 				}
15708 				if (ptr->arg[0] == '-' && (isalpha (ptr->arg[1]) || ptr->arg[1] == '-')) {
15709 					ptr->option = ptr->arg[1];	/* Change from "file" to an option */
15710 					gmt_strlshift (ptr->arg, 2U);	/* Remove the leading -? part */
15711 				}
15712 			}
15713 			continue;
15714 		}
15715 		else
15716 			ptr = GMT_Make_Option (API, opt->option, opt->arg);
15717 
15718 		if (ptr == NULL || (list = GMT_Append_Option (API, ptr, list)) == NULL) {
15719 			gmt_free_macros (GMT, n_macros, &M);
15720 			return (NULL);
15721 		}
15722 	}
15723 	gmt_free_macros (GMT, n_macros, &M);
15724 
15725 	return (list);
15726 }
15727 
15728 /*! . */
gmtlib_init_rot_matrix(double R[3][3],double E[])15729 void gmtlib_init_rot_matrix (double R[3][3], double E[]) {
15730 	/* This starts setting up the matrix without knowing the angle of rotation
15731 	 * Call set_rot_angle with R, and omega to complete the matrix
15732 	 * P	Cartesian 3-D vector of rotation pole
15733 	 * R	the rotation matrix without terms depending on omega
15734 	 * See Cox and Hart [1985] Box ?? for details.
15735 	 */
15736 
15737 	R[0][0] = E[0] * E[0];
15738 	R[1][1] = E[1] * E[1];
15739 	R[2][2] = E[2] * E[2];
15740 	R[0][1] = R[1][0] = E[0] * E[1];
15741 	R[0][2] = R[2][0] = E[0] * E[2];
15742 	R[1][2] = R[2][1] = E[1] * E[2];
15743 }
15744 
15745 /*! . */
gmtlib_load_rot_matrix(double w,double R[3][3],double E[])15746 void gmtlib_load_rot_matrix (double w, double R[3][3], double E[]) {
15747 	/* Sets R using R(no_omega) and the given rotation angle w in radians */
15748 	double sin_w, cos_w, c, E_x, E_y, E_z;
15749 
15750 	sincos (w, &sin_w, &cos_w);
15751 	c = 1.0 - cos_w;
15752 
15753 	E_x = E[0] * sin_w;
15754 	E_y = E[1] * sin_w;
15755 	E_z = E[2] * sin_w;
15756 
15757 	R[0][0] = R[0][0] * c + cos_w;
15758 	R[0][1] = R[0][1] * c - E_z;
15759 	R[0][2] = R[0][2] * c + E_y;
15760 
15761 	R[1][0] = R[1][0] * c + E_z;
15762 	R[1][1] = R[1][1] * c + cos_w;
15763 	R[1][2] = R[1][2] * c - E_x;
15764 
15765 	R[2][0] = R[2][0] * c - E_y;
15766 	R[2][1] = R[2][1] * c + E_x;
15767 	R[2][2] = R[2][2] * c + cos_w;
15768 }
15769 
15770 #if 0
15771 /*! . */
15772 void gmt_matrix_vect_mult (double a[3][3], double b[3], double c[3]) {
15773 	/* c = A * b */
15774 	int i, j;
15775 
15776 	for (i = 0; i < 3; i++) for (j = 0, c[i] = 0.0; j < 3; j++) c[i] += a[i][j] * b[j];
15777 }
15778 #endif
15779 
15780 /*! . */
gmt_segmentize_data(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,struct GMT_SEGMENTIZE * S)15781 struct GMT_DATASET * gmt_segmentize_data (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, struct GMT_SEGMENTIZE *S) {
15782 	/* Din is a data set with any number of tables and segments and records.
15783 	 * On output we return a pointer to new dataset in which all points in Din are used to
15784 	 * form 2-point line segments.  So output will have lots of segments, all of length 2,
15785 	 * and all these segments are placed in the first (and only) output table.
15786 	 * There are several forms of segmentization:
15787 	 * 1) -Fc: Continuous lines.  By default, lines are drawn on a segment by segment basis.
15788 	 *    Thus, -F or -Fc or -Fcs or -Fs is the same as the input and is not allowed.  However, if we use
15789 	 *    -Fcf or -Ff then we ignore segment headers WITHIN each file, except for the first header
15790 	 *   in each file.  In other words, all points in a file will be considered continuous.
15791 	 *   Finally, using -Fca or -Fa then all points in all fields are considered continuous and
15792 	 *   only the first segment header in the first file is considered.
15793 	 * 2) -Fn: Network.  For each group of points we connect each point with every other point.
15794 	 *   The modifiers a,f,s control what the "group" is.  With s, we construct a separate
15795 	 *   network for each segment, with f we group all segments in a file and construct a
15796 	 *   network for all those points, while with a with consider all points in the dataset
15797 	 *   to be one group.
15798 	 * 3) -Fr: Ref point.  Here, we construct line segments from the given reference point to
15799 	 *   each of the points in the file.  If refpoint is given as two slash-separated coordinates
15800 	 *   then the refpoint is fixed throughout this construction.  However, refpoint may also be
15801 	 *   given as a, f, s and if so we pick the first point in the dataset, or first point in each
15802 	 *   file, or the first point in each segment to update the actual reference point.
15803 	 * 4) -Fv: Vectorize.  Here, consecutive points are turned into vector segments such as used
15804 	 *   by psxy -Sv|=<size>+s or external applications.  Again, appending a|f|s controls if we should
15805 	 *   honor the segment headers [Default is -Fvs if -Fv is given].
15806 	 */
15807 	uint64_t dim[GMT_DIM_SIZE] = {1, 0, 2, 0};	/* Put everything in one table, each segment has 2 points */
15808 	uint64_t tbl, seg, row, col, new_seg;
15809 	unsigned int smode;
15810 	double *data = NULL;
15811 	struct GMT_DATASET *D = NULL;
15812 	struct GMT_DATATABLE *Tin = NULL, *Tout = NULL;
15813 
15814 	dim[GMT_COL] = Din->n_columns;	/* Same number of columns as input dataset */
15815 	dim[GMT_ROW] = 2;		/* Two per segment unless for continuous mode */
15816 	/* Determine how many total segments will be created */
15817 	switch (S->method) {
15818 		case SEGM_CONTINUOUS:
15819 			switch (S->level) {
15820 				case SEGM_DATASET:	/* Everything is just one long segment */
15821 					dim[GMT_SEG] = 1;
15822 					dim[GMT_ROW] = Din->n_records;
15823 					break;
15824 				case SEGM_TABLE:	/* Turn all segments in a table into one */
15825 					dim[GMT_SEG] = Din->n_tables;
15826 					dim[GMT_ROW] = 0;	/* Must do this per segment */
15827 					break;
15828 				case SEGM_RECORD:	/* Turn all points into individual points with segment headers */
15829 					dim[GMT_SEG] = Din->n_records;
15830 					dim[GMT_ROW] = 1;	/* Must do this per segment */
15831 					break;
15832 			}
15833 			break;
15834 		case SEGM_REFPOINT:
15835 			switch (S->level) {
15836 				case SEGM_ORIGIN:	/* External origin, so n_records points remain */
15837 					dim[GMT_SEG] = Din->n_records;
15838 					break;
15839 				case SEGM_DATASET:	/* Reset origin to first point in dataset, so n_records-1 points remains */
15840 					dim[GMT_SEG] = Din->n_records - 1;
15841 					break;
15842 				case SEGM_TABLE:	/* Reset origin to first point in table, so n_records-1 points remains */
15843 					for (tbl = 0; tbl < Din->n_tables; tbl++)
15844 						dim[GMT_SEG] += (Din->table[tbl]->n_records - 1);
15845 					break;
15846 				case SEGM_RECORD:	/* Reset origin per point */
15847 				case SEGM_SEGMENT:	/* Reset origin to first point in each segment, so n_rows-1 points remains per segment */
15848 					for (tbl = 0; tbl < Din->n_tables; tbl++) {
15849 						Tin = Din->table[tbl];
15850 						for (seg = 0; seg < Tin->n_segments; seg++)	/* For each segment to resample */
15851 							dim[GMT_SEG] += Tin->segment[seg]->n_rows - 1;
15852 					}
15853 					dim[GMT_SEG] = Din->n_records;
15854 					break;
15855 			}
15856 			data = gmt_M_memory (GMT, NULL, dim[GMT_COL], double);
15857 			break;
15858 		case SEGM_NETWORK:
15859 			switch (S->level) {
15860 				case SEGM_DATASET:	/* Make a single network */
15861 					dim[GMT_SEG] = (Din->n_records * (Din->n_records - 1)) / 2;
15862 					break;
15863 				case SEGM_TABLE:	/* Make a new network for each table */
15864 					for (tbl = 0; tbl < Din->n_tables; tbl++)
15865 						dim[GMT_SEG] += (Din->table[tbl]->n_records * (Din->table[tbl]->n_records - 1)) / 2;
15866 					break;
15867 				case SEGM_SEGMENT:	/* Make a new network for each segment */
15868 					for (tbl = 0; tbl < Din->n_tables; tbl++) {
15869 						Tin = Din->table[tbl];
15870 						for (seg = 0; seg < Tin->n_segments; seg++)	/* For each segment to resample */
15871 							dim[GMT_SEG] += (Tin->segment[seg]->n_rows *(Tin->segment[seg]->n_rows - 1)) / 2;
15872 					}
15873 			}
15874 			break;
15875 		case SEGM_VECTOR:
15876 			dim[GMT_COL] *= 2;	/* Since we are putting two records on the same line */
15877 			switch (S->level) {
15878 				case SEGM_DATASET:	/* Ignore all headers, so n_records-1 pairs remains forming one segment */
15879 					dim[GMT_SEG] = 1;
15880 					dim[GMT_ROW] = Din->n_records - 1;	/* Must do this per segment */
15881 					break;
15882 				case SEGM_TABLE:	/* Ignore all but first header in table, so n_records-1 pairs per table remains */
15883 					dim[GMT_SEG] = Din->n_tables;
15884 					dim[GMT_ROW] = 0;	/* Must do this per segment */
15885 					break;
15886 				case SEGM_SEGMENT:	/* Process per segment segment, so n_rows-1 pairs remains per segment */
15887 					dim[GMT_SEG] = 	Din->n_segments;
15888 					dim[GMT_ROW] = 0;	/* Must do this per segment */
15889 					break;
15890 			}
15891 			data = gmt_M_memory (GMT, NULL, dim[GMT_COL], double);
15892 			break;
15893 	}
15894 	if (S->method == SEGM_CONTINUOUS)
15895 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Segmentize input data into %" PRIu64 " variable-length segment lines\n", dim[GMT_SEG]);
15896 	else
15897 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Segmentize input data into %" PRIu64 " 2-point segment lines\n", dim[GMT_SEG]);
15898 
15899 	/* Allocate the dataset with one large table */
15900 	if ((D = GMT_Create_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_LINE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) {
15901 		gmt_M_free (GMT, data);
15902 		return (NULL);	/* Our new dataset */
15903 	}
15904 	Tout = D->table[0];	/* The only output table */
15905 
15906 	if (S->method == SEGM_NETWORK) {	/* Make segments that connect every point with every other point */
15907 		struct GMT_DATATABLE *Tin2 = NULL;
15908 		uint64_t tbl2, seg2, row2;
15909 		bool same[2] = {false, false};
15910 		for (tbl = new_seg = 0; tbl < Din->n_tables; tbl++) {
15911 			Tin = Din->table[tbl];	/* First input table */
15912 			for (tbl2 = tbl; tbl2 < Din->n_tables; tbl2++) {
15913 				if (S->level != SEGM_DATASET && tbl2 != tbl) continue;	/* Not working across tables */
15914 				Tin2 = Din->table[tbl2];	/* Second input table (but might be the same as Tin1) */
15915 				same[0] = (tbl == tbl2);
15916 				for (seg = 0; seg < Tin->n_segments; seg++) {	/* For each input segment to resample */
15917 					for (seg2 = (same[0]) ? seg : 0; seg2 < Tin2->n_segments; seg2++) {	/* For each input segment to resample */
15918 						same[1] = (same[0] && seg == seg2);	/* So same[1] is only true for identical segments from same table */
15919 						if (S->level == SEGM_SEGMENT && !same[1] ) continue;	/* Not working across segments */
15920 						for (row = 0; row < Tin->segment[seg]->n_rows; row++) {	/* For each end point in the new 2-point segments */
15921 							for (row2 = (same[1]) ? row+1 : 0; row2 < Tin2->segment[seg2]->n_rows; row2++, new_seg++) {	/* For each end point in the new 2-point segments */
15922 								if (Tin->segment[seg]->header) Tout->segment[new_seg]->header = strdup (Tin->segment[seg]->header);	/* Duplicate first segment header in table */
15923 								for (col = 0; col < Tin->segment[seg]->n_columns; col++) {	/* For every column */
15924 									Tout->segment[new_seg]->data[col][0] = Tin->segment[seg]->data[col][row];
15925 									Tout->segment[new_seg]->data[col][1] = Tin2->segment[seg2]->data[col][row2];
15926 								}
15927 							}
15928 						}
15929 					}
15930 				}
15931 			}
15932 		}
15933 	}
15934 	else if (S->method == SEGM_VECTOR) {	/* Turn lines into a series of x0 y0 ... x1 y1 ... records, e.g., for psxy -Sv+s */
15935 		uint64_t new_row, seg2, off = 0;
15936 		if (S->level == SEGM_DATASET) {	/* Duplicate very first segment header only */
15937 			if (Din->table[0]->segment[0]->header) Tout->segment[0]->header = strdup (Din->table[0]->segment[0]->header);
15938 		}
15939 		for (tbl = new_row = seg2 = 0; tbl < Din->n_tables; tbl++) {
15940 			Tin = Din->table[tbl];	/* Current input table */
15941 			if (S->level == SEGM_TABLE) {	/* Must allocate Tin->n_records for the output table's next segment (one per input table) */
15942 				smode = (Tin->segment[0]->text) ? GMT_WITH_STRINGS : 0;
15943 				gmt_alloc_segment (GMT, Tout->segment[seg2], Tin->n_records-1, Tout->n_columns, smode, false);
15944 				if (Tin->segment[0]->header) Tout->segment[seg2]->header = strdup (Tin->segment[0]->header);	/* Duplicate first segment header in table */
15945 			}
15946 			for (seg = 0; seg < Tin->n_segments; seg++) {	/* For each input segment to resample */
15947 				off = 0;
15948 				if (new_row == 0) {	/* Update the first row */
15949 					for (col = 0; col < Tin->segment[seg]->n_columns; col++) data[col] = Tin->segment[seg]->data[col][0];
15950 					if (Tin->segment[seg]->header) Tout->segment[seg2]->header = strdup (Tin->segment[seg]->header);
15951 					off = 1;
15952 				}
15953 				if (S->level == SEGM_SEGMENT) {	/* Must allocate Tin->n_records for the output table's next segment (one per input table) */
15954 					smode = (Tin->segment[seg]->text) ? GMT_WITH_STRINGS : 0;
15955 					gmt_alloc_segment (GMT, Tout->segment[seg2], Tin->segment[seg]->n_rows-1, Tout->n_columns, smode, false);
15956 					if (Tin->segment[seg]->header) Tout->segment[seg2]->header = strdup (Tin->segment[seg]->header);	/* Duplicate each segment header in table */
15957 				}
15958 				for (row = off; row < Tin->segment[seg]->n_rows; row++, new_row++) {	/* For each end point in the new 2-point segments */
15959 					for (col = 0; col < Tin->segment[seg]->n_columns; col++) {	/* For every column */
15960 						Tout->segment[seg2]->data[col][new_row] = data[col];
15961 						data[col] = Tout->segment[seg2]->data[Din->n_columns+col][new_row] = Tin->segment[seg]->data[col][row];		/* 2nd point */
15962 					}
15963 				}
15964 				if (S->level == SEGM_SEGMENT) {
15965 					Tout->segment[seg2]->n_rows = new_row;
15966 					seg2++;	/* Goto next output segment after each input segment */
15967 					new_row = 0;
15968 				}
15969 			}
15970 			if (S->level == SEGM_TABLE) {	/* Goto next output segment after each table */
15971 				Tout->segment[seg2]->n_rows = new_row;
15972 				seg2++;	/* Goto next output segment after each input segment */
15973 				new_row = 0;
15974 			}
15975 		}
15976 		if (S->level == SEGM_DATASET) {	/* Wrap up the lone segment */
15977 			Tout->segment[0]->n_rows = new_row;
15978 		}
15979 		gmt_M_free (GMT, data);
15980 	}
15981 	else if (S->method == SEGM_CONTINUOUS) {	/* Basically remove various segment boundaries */
15982 		uint64_t seg2, new_row = 0;
15983 		if (S->level == SEGM_DATASET) {	/* Duplicate very first segment header only */
15984 			if (Din->table[0]->segment[0]->header) Tout->segment[0]->header = strdup (Din->table[0]->segment[0]->header);
15985 		}
15986 		for (tbl = seg2 = new_row = 0; tbl < Din->n_tables; tbl++) {
15987 			Tin = Din->table[tbl];	/* Current input table */
15988 			if (S->level == SEGM_TABLE) {	/* Must allocate Tin->n_records for the output table's next segment (one per input table) */
15989 				smode = (Tin->segment[0]->text) ? GMT_WITH_STRINGS : 0;
15990 				gmt_alloc_segment (GMT, Tout->segment[tbl], Tin->n_records, Tin->n_columns, smode, false);
15991 				new_row = 0;	/* Reset output count for this output segment */
15992 				if (Tin->segment[0]->header) Tout->segment[seg2]->header = strdup (Tin->segment[0]->header);	/* Duplicate first segment header in table */
15993 			}
15994 			for (seg = 0; seg < Tin->n_segments; seg++) {	/* For each input segment to resample */
15995 				for (col = 0; col < Tin->segment[seg]->n_columns; col++) {	/* For every column */
15996 					gmt_M_memcpy (&Tout->segment[seg2]->data[col][new_row], Tin->segment[seg]->data[col], Tin->segment[seg]->n_rows, double);
15997 				}
15998 				new_row += Tin->segment[seg]->n_rows;
15999 			}
16000 			if (S->level == SEGM_TABLE) {
16001 				Tout->segment[seg2]->n_rows = new_row;
16002 				seg2++;	/* Goto next output segment */
16003 			}
16004 		}
16005 	}
16006 	else if (S->method == SEGM_REFPOINT) {	/* Make segment lines from various origins to each point */
16007 		uint64_t off = 0;
16008 		if (S->level == SEGM_ORIGIN) gmt_M_memcpy (data, S->origin, 2, double);	/* Initialize the origin of segments once */
16009 		if (S->level == SEGM_DATASET) {	/* Duplicate very first segment header only */
16010 			for (col = 0; col < Din->n_columns; col++) data[col] = Din->table[0]->segment[0]->data[col][0];
16011 			if (Din->table[0]->segment[0]->header) Tout->segment[0]->header = strdup (Din->table[0]->segment[0]->header);
16012 			off = 1;
16013 		}
16014 		for (tbl = new_seg = 0; tbl < Din->n_tables; tbl++) {
16015 			Tin = Din->table[tbl];	/* Current input table */
16016 			if (S->level == SEGM_TABLE) {	/* Initialize the origin as 1st point in this table (or for each table) */
16017 				for (col = 0; col < Din->n_columns; col++) data[col] = Tin->segment[0]->data[col][0];
16018 				if (Tin->segment[0]->header) Tout->segment[new_seg]->header = strdup (Tin->segment[0]->header);	/* Duplicate first segment header in table */
16019 				off = 1;
16020 			}
16021 			for (seg = 0; seg < Tin->n_segments; seg++) {	/* For each input segment to resample */
16022 				if (S->level == SEGM_SEGMENT || S->level == SEGM_RECORD) {	/* Initialize the origin as 1st point in segment */
16023 					for (col = 0; col < Din->n_columns; col++) data[col] = Tin->segment[seg]->data[col][0];
16024 					off = 1;
16025 				}
16026 				for (row = off; row < Tin->segment[seg]->n_rows; row++, new_seg++) {	/* For each end point in the new 2-point segments */
16027 					if (Tin->segment[seg]->header) Tout->segment[new_seg]->header = strdup (Tin->segment[seg]->header);	/* Duplicate first segment header in table */
16028 					for (col = 0; col < Tin->segment[seg]->n_columns; col++) {	/* For every column */
16029 						Tout->segment[new_seg]->data[col][0] = data[col];				/* 1st point */
16030 						Tout->segment[new_seg]->data[col][1] = Tin->segment[seg]->data[col][row];	/* 2nd point */
16031 						if (S->level == SEGM_RECORD) data[col] = Tin->segment[seg]->data[col][row];	/* Update 1st point again */
16032 					}
16033 				}
16034 				off = 0;
16035 			}
16036 		}
16037 		gmt_M_free (GMT, data);
16038 	}
16039 	gmt_set_dataset_minmax (GMT, D);	/* Determine min/max for each column */
16040 
16041 	return (D);
16042 }
16043 
16044 /*! . */
gmt_resample_data(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,double along_ds,unsigned int mode,unsigned int ex_cols,enum GMT_enum_track smode)16045 struct GMT_DATASET * gmt_resample_data (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, double along_ds,  unsigned int mode, unsigned int ex_cols, enum GMT_enum_track smode) {
16046 	/* Din is a data set with at least two columns (x,y or lon/lat);
16047 	 * it can contain any number of tables and segments with lines.
16048 	 * along_ds is the resampling interval along the traces in Din.
16049 	 * It is in the current distance units (set via gmt_init_distaz).
16050 	 * mode is either 0 (just lon,lat), 1 (lon,lat,dist) or 2 (lon,lat,dist,azim)
16051 	 * ex_cols makes space for this many extra empty data columns [0].
16052 	 * smode sets the sampling mode for the track:
16053 	 * smode = GMT_TRACK_FILL	: Keep input points; add intermediates if any gap exceeds step_out.
16054 	 * smode = GMT_TRACK_FILL_M	: Same, but traverse along meridians, then parallels between points.
16055 	 * smode = GMT_TRACK_FILL_P	: Same, but traverse along parallels, then meridians between points.
16056 	 * smode = GMT_TRACK_SAMPLE_FIX	: Resample track equidistantly; old points may be lost. Use given spacing.
16057 	 * smode = GMT_TRACK_SAMPLE_ADJ	: Resample track equidistantly; old points may be lost. Adjust spacing to fit tracklength exactly.
16058 	 *
16059 	 * Dout is the new data set with all the resampled lines;
16060 	 */
16061 	struct GMT_DATASET *D = NULL;
16062 	if (gmt_M_is_geographic (GMT, GMT_IN))
16063 		D = gmtsupport_resample_data_spherical (GMT, Din, along_ds, mode, ex_cols, smode);
16064 	else
16065 		D = gmtsupport_resample_data_cartesian (GMT, Din, along_ds, mode, ex_cols, smode);
16066 	gmt_set_dataset_minmax (GMT, D);	/* Determine min/max for each column */
16067 	return (D);
16068 }
16069 
16070 /*! . */
gmt_crosstracks(struct GMT_CTRL * GMT,struct GMT_DATASET * Din,double cross_length,double across_ds,double deviation,uint64_t n_cols,unsigned int mode,char unit)16071 struct GMT_DATASET * gmt_crosstracks (struct GMT_CTRL *GMT, struct GMT_DATASET *Din, double cross_length, double across_ds, double deviation, uint64_t n_cols, unsigned int mode, char unit) {
16072 	/* Call either the spherical or Cartesian version */
16073 	struct GMT_DATASET *D = NULL;
16074 	if (gmt_M_is_geographic (GMT, GMT_IN))
16075 		D = gmtsupport_crosstracks_spherical (GMT, Din, cross_length, across_ds, deviation, n_cols, mode, unit);
16076 	else
16077 		D = gmtsupport_crosstracks_cartesian (GMT, Din, cross_length, across_ds, deviation, n_cols, mode, unit);
16078 	gmt_set_dataset_minmax (GMT, D);	/* Determine min/max for each column */
16079 	return (D);
16080 }
16081 
16082 /*! . */
gmt_crossing_dateline(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)16083 bool gmt_crossing_dateline (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
16084 	/* Return true if this line or polygon feature contains points on either side of the Dateline */
16085 	uint64_t k;
16086 	bool east = false, west = false, cross = false;
16087 	gmt_M_unused(GMT);
16088 	for (k = 0; !cross && k < S->n_rows; k++) {
16089 		if ((S->data[GMT_X][k] > 180.0 && S->data[GMT_X][k] < 270.0) || (S->data[GMT_X][k] > -180.0 && S->data[GMT_X][k] <  -90.0)) west = true;
16090 		if ((S->data[GMT_X][k] >  90.0 && S->data[GMT_X][k] < 180.0) || (S->data[GMT_X][k] > -270.0 && S->data[GMT_X][k] < -180.0)) east = true;
16091 		if (east && west) cross = true;
16092 	}
16093 	return (cross);
16094 }
16095 
16096 /*! . */
gmtlib_split_line_at_dateline(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,struct GMT_DATASEGMENT *** Lout)16097 unsigned int gmtlib_split_line_at_dateline (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, struct GMT_DATASEGMENT ***Lout) {
16098 	/* Create two or more feature segments by splitting them across the Dateline.
16099 	 * gmtlib_split_line_at_dateline should ONLY be called when we KNOW we must split. */
16100 	unsigned int n_split;
16101 	unsigned int smode = (S->text) ? GMT_WITH_STRINGS : 0;
16102 	uint64_t k, col, seg, row, start, length, *pos = gmt_M_memory (GMT, NULL, S->n_rows, uint64_t);
16103 	char label[GMT_BUFSIZ] = {""}, *txt = NULL, *feature = "Line";
16104 	double r;
16105 	struct GMT_DATASEGMENT **L = NULL, *Sx = gmt_get_segment (GMT, S->n_columns);
16106 	struct GMT_DATASEGMENT_HIDDEN *LH = NULL, *SH = gmt_get_DS_hidden (S);
16107 
16108 	for (k = 0; k < S->n_rows; k++) gmt_lon_range_adjust (GMT_IS_0_TO_P360_RANGE, &S->data[GMT_X][k]);	/* First enforce 0 <= lon < 360 so we don't have to check again */
16109 	gmt_alloc_segment (GMT, Sx, 2*S->n_rows, S->n_columns, smode, true);	/* Temp segment with twice the number of points as we will add crossings*/
16110 
16111 	for (k = row = n_split = 0; k < S->n_rows; k++) {	/* Hunt for crossings */
16112 		if (k && gmtsupport_straddle_dateline (S->data[GMT_X][k-1], S->data[GMT_X][k])) {	/* Crossed Dateline */
16113 			r = (180.0 - S->data[GMT_X][k-1]) / (S->data[GMT_X][k] - S->data[GMT_X][k-1]);	/* Fractional distance from k-1'th point to 180 crossing */
16114 			Sx->data[GMT_X][row] = 180.0;	/* Exact longitude is known */
16115 			for (col = 1; col < S->n_columns; col++) Sx->data[col][row] = S->data[col][k-1] + r * (S->data[col][k] - S->data[col][k-1]);	/* Linear interpolation for other fields */
16116 			pos[n_split++] = row++;		/* Keep track of first point (the crossing) in new section */
16117 		}
16118 		for (col = 0; col < S->n_columns; col++) Sx->data[col][row] = S->data[col][k];	/* Append the current point */
16119 		row++;
16120 	}
16121 	Sx->n_rows = row;	/* Number of points in extended feature with explicit crossings */
16122 	if (n_split == 0) {	/* No crossings, should not have been called in the first place */
16123 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No need to insert new points at 180\n");
16124 		gmt_free_segment (GMT, &Sx);
16125 		gmt_M_free (GMT, pos);
16126 		return 0;
16127 	}
16128 	pos[n_split] = Sx->n_rows - 1;
16129 	n_split++;	/* Now means number of segments */
16130 	L = gmt_M_memory (GMT, NULL, n_split, struct GMT_DATASEGMENT *);	/* Number of output segments needed are allocated here */
16131 	txt = (S->label) ? S->label : feature;	/* What to label the features */
16132 	start = 0;
16133 	for (seg = 0; seg < n_split; seg++) {	/* Populate the output segment coordinate arrays */
16134 		length = pos[seg] - start + 1;	/* Length of new segment */
16135 		L[seg] = GMT_Alloc_Segment (GMT->parent, smode, length, S->n_columns, S->header, NULL);	/* Allocate array space for coordinates */
16136 		LH = gmt_get_DS_hidden (L[seg]);
16137 		for (col = 0; col < S->n_columns; col++) gmt_M_memcpy (L[seg]->data[col], &(Sx->data[col][start]), length, double);	/* Copy coordinates */
16138 		LH->range = (L[seg]->data[GMT_X][length/2] > 180.0) ? GMT_IS_M180_TO_P180 : GMT_IS_M180_TO_P180_RANGE;	/* Formatting ID to enable special -180 and +180 formatting on output */
16139 		/* Modify label to part number */
16140 		sprintf (label, "%s part %" PRIu64, txt, seg);
16141 		L[seg]->label = strdup (label);
16142 		if (SH->ogr) gmt_duplicate_ogr_seg (GMT, L[seg], S);
16143 		start = pos[seg];
16144 	}
16145 	gmt_free_segment (GMT, &Sx);
16146 	gmt_M_free (GMT, pos);
16147 
16148 	*Lout = L;		/* Pass pointer to the array of segments */
16149 
16150 	return (n_split);	/* Return how many segments was made */
16151 }
16152 
16153 /*! . */
gmt_putusername(struct GMT_CTRL * GMT)16154 char *gmt_putusername (struct GMT_CTRL *GMT) {
16155 	/* Calling function must free the result */
16156 	static char *unknown = "unknown";
16157 	gmt_M_unused(GMT);
16158 #ifdef HAVE_GETPWUID
16159 #include <pwd.h>
16160 	struct passwd *pw = NULL;
16161 	pw = getpwuid (getuid ());
16162 	if (pw) return (strdup (pw->pw_name));
16163 #endif
16164 #ifdef WIN32
16165 	{
16166 		char name[GMT_LEN256] = {""}, *U = NULL;
16167 		DWORD Size = (DWORD)_tcslen (name);
16168 		if (GetUserName (name, &Size)) /* Got a user name, return it */
16169 			return (strdup (name));
16170 		if (U = getenv ("USERNAME"))	/* Got a name from the environment instead */
16171 			return (strdup (U));
16172 	}
16173 #endif
16174 	return (strdup (unknown));
16175 }
16176 
16177 /* Various functions from surface that are now used elsewhere as well */
16178 
16179 /*! . */
gmt_gcd_euclid(unsigned int a,unsigned int b)16180 unsigned int gmt_gcd_euclid (unsigned int a, unsigned int b) {
16181 	/* Returns the greatest common divisor of u and v by Euclid's method.
16182 	 * I have experimented also with Stein's method, which involves only
16183 	 * subtraction and left/right shifting; Euclid is faster, both for
16184 	 * integers of size 0 - 1024 and also for random integers of a size
16185 	 * which fits in a long integer.  Stein's algorithm might be better
16186 	 * when the integers are HUGE, but for our purposes, Euclid is fine.
16187 	 *
16188 	 * Walter H. F. Smith, 25 Feb 1992, after D. E. Knuth, vol. II  */
16189 
16190 	unsigned int u, v, r;
16191 
16192 	u = MAX (a, b);
16193 	v = MIN (a, b);
16194 
16195 	while (v > 0) {
16196 		r = u % v;	/* Knuth notes that u < 2v 40% of the time;  */
16197 		u = v;		/* thus we could have tried a subtraction  */
16198 		v = r;		/* followed by an if test to do r = u%v  */
16199 	}
16200 	return (u);
16201 }
16202 
16203 /*! . */
gmt_optimal_dim_for_surface(struct GMT_CTRL * GMT,unsigned int factors[],unsigned int n_columns,unsigned int n_rows,struct GMT_SURFACE_SUGGESTION ** S)16204 unsigned int gmt_optimal_dim_for_surface (struct GMT_CTRL *GMT, unsigned int factors[], unsigned int n_columns, unsigned int n_rows, struct GMT_SURFACE_SUGGESTION **S) {
16205 	/* Calls gmtsupport_guess_surface_time for a variety of trial grid
16206 	 * sizes, where the trials are highly composite numbers
16207 	 * with lots of factors of 2, 3, and 5.  The sizes are
16208 	 * within the range (n_columns,n_rows) - (2*n_columns, 2*n_rows).  Prints to
16209 	 * GMT->session.std[GMT_ERR] the values which are an improvement over the
16210 	 * user's original n_columns,n_rows.
16211 	 * Should be called with n_columns=(x_max-x_min)/dx, and ditto
16212 	 * for n_rows; that is, one smaller than the lattice used
16213 	 * in surface.c
16214 	 *
16215 	 * W. H. F. Smith, 26 Feb 1992.  */
16216 
16217 	double users_time;	/* Time for user's n_columns, n_rows  */
16218 	double current_time;	/* Time for current nxg, nyg  */
16219 	unsigned int nxg, nyg;	/* Guessed by this routine  */
16220 	unsigned int nx2, ny2, nx3, ny3, nx5, ny5;	/* For powers  */
16221 	unsigned int xstop, ystop;	/* Set to 2*n_columns, 2*n_rows  */
16222 	unsigned int n_sug = 0;	/* N of suggestions found  */
16223 	struct GMT_SURFACE_SUGGESTION *sug = NULL;
16224 
16225 	users_time = gmtsupport_guess_surface_time (GMT, factors, n_columns, n_rows);
16226 	xstop = 2*n_columns;
16227 	ystop = 2*n_rows;
16228 
16229 	for (nx2 = 2; nx2 <= xstop; nx2 *= 2) {
16230 	  for (nx3 = 1; nx3 <= xstop; nx3 *= 3) {
16231 	    for (nx5 = 1; nx5 <= xstop; nx5 *= 5) {
16232 		nxg = nx2 * nx3 * nx5;
16233 		if (nxg < n_columns || nxg > xstop) continue;
16234 
16235 		for (ny2 = 2; ny2 <= ystop; ny2 *= 2) {
16236 		  for (ny3 = 1; ny3 <= ystop; ny3 *= 3) {
16237 		    for (ny5 = 1; ny5 <= ystop; ny5 *= 5) {
16238 			nyg = ny2 * ny3 * ny5;
16239 			if (nyg < n_rows || nyg > ystop) continue;
16240 
16241 			current_time = gmtsupport_guess_surface_time (GMT, factors, nxg, nyg);
16242 			if (current_time < users_time) {
16243 				n_sug++;
16244 				sug = gmt_M_memory (GMT, sug, n_sug, struct GMT_SURFACE_SUGGESTION);
16245 				sug[n_sug-1].n_columns = nxg;
16246 				sug[n_sug-1].n_rows = nyg;
16247 				sug[n_sug-1].factor = users_time/current_time;
16248 			}
16249 
16250 		    }
16251 		  }
16252 		}
16253 	    }
16254 	  }
16255 	}
16256 
16257 	if (n_sug) {
16258 		qsort (sug, n_sug, sizeof (struct GMT_SURFACE_SUGGESTION), gmtsupport_compare_sugs);
16259 		*S = sug;
16260 	}
16261 
16262 	return n_sug;
16263 }
16264 
16265 /*! . */
gmt_best_dim_choice(struct GMT_CTRL * GMT,unsigned int mode,unsigned int in_dim[],unsigned int out_dim[])16266 int gmt_best_dim_choice (struct GMT_CTRL *GMT, unsigned int mode, unsigned int in_dim[], unsigned int out_dim[]) {
16267 	/* Depending on mode, returns the closest out_dim >= in_dim that will speed up computations
16268 	 * in surface (mode = 1) or for FFT work (mode = 2).  We return 0 if we found a better
16269 	 * choice or 1 if the in_dim is the best choice. */
16270 	int retval = 0;
16271 	gmt_M_memcpy (out_dim, in_dim, 2U, unsigned int);	/* Default we return input if we cannot find anything better */
16272 
16273 	if (mode == 1) {
16274 		struct GMT_SURFACE_SUGGESTION *S = NULL;
16275 		unsigned int factors[32], n_sugg = gmt_optimal_dim_for_surface (GMT, factors, in_dim[GMT_X], in_dim[GMT_Y], &S);
16276 		if (n_sugg) {
16277 			out_dim[GMT_X] = S[0].n_columns;
16278 			out_dim[GMT_Y] = S[0].n_rows;
16279 			gmt_M_free (GMT, S);
16280 		}
16281 		else
16282 			retval = 1;
16283 	}
16284 	else if (mode == 2) {
16285 		struct GMT_FFT_SUGGESTION fft_sug[GMT_FFT_N_SUGGEST];
16286 		gmtlib_suggest_fft_dim (GMT, in_dim[GMT_X], in_dim[GMT_Y], fft_sug, false);
16287 		if (fft_sug[GMT_FFT_ACCURATE].totalbytes < fft_sug[GMT_FFT_FAST].totalbytes) {
16288 			/* The most accurate solution needs same or less storage
16289 			 * as the fastest solution; use the most accurate's dimensions */
16290 			out_dim[GMT_X] = fft_sug[GMT_FFT_ACCURATE].n_columns;
16291 			out_dim[GMT_Y] = fft_sug[GMT_FFT_ACCURATE].n_rows;
16292 		}
16293 		else {	/* Use the sizes of the fastest solution  */
16294 			out_dim[GMT_X] = fft_sug[GMT_FFT_FAST].n_columns;
16295 			out_dim[GMT_Y] = fft_sug[GMT_FFT_FAST].n_rows;
16296 		}
16297 	}
16298 	else {
16299 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad mode: %u Must select either 1 or 2\n", mode);
16300 		retval = -1;
16301 	}
16302 	return (retval);
16303 }
16304 
16305 /*! . */
gmt_free_int_selection(struct GMT_CTRL * GMT,struct GMT_INT_SELECTION ** S)16306 void gmt_free_int_selection (struct GMT_CTRL *GMT, struct GMT_INT_SELECTION **S) {
16307 	/* Free the selection structure */
16308 	if (*S == NULL) return;	/* Nothing to free */
16309 	gmt_M_free (GMT, (*S)->item);
16310 	gmt_M_free (GMT, *S);
16311 }
16312 
16313 /*! . */
gmt_get_int_selection(struct GMT_CTRL * GMT,struct GMT_INT_SELECTION * S,uint64_t this)16314 bool gmt_get_int_selection (struct GMT_CTRL *GMT, struct GMT_INT_SELECTION *S, uint64_t this) {
16315 	/* Return true if this item should be used */
16316 	gmt_M_unused(GMT);
16317 	if (S == NULL) return (false);	/* No selection criteria given, so can only return false */
16318 	while (S->current < S->n && S->item[S->current] < this) S->current++;	/* Advance internal counter */
16319 	if (S->current == S->n) return (S->invert);	/* Ran out, return true or false depending on initial setting */
16320 	else if (S->item[S->current] == this) return (!S->invert);	/* Found, return true or false depending on initial setting */
16321 	else return (S->invert);	/* Not found, return initial setting */
16322 }
16323 
16324 /*! . */
gmt_set_int_selection(struct GMT_CTRL * GMT,char * item)16325 struct GMT_INT_SELECTION * gmt_set_int_selection (struct GMT_CTRL *GMT, char *item) {
16326 	/* item is of the form [~]<range>[,<range>, <range>]
16327 	 * where each <range> can be
16328 	 * a) A single number [e.g., 8]
16329 	 * b) a range of numbers given as start-stop [e.g., 6-11]
16330 	 * c) A range generator start:step:stop [e.g., 13:2:19]
16331 	 * d) +f<file> a file with a list of range items.
16332 	 * If ~ is given we return the inverse selection.
16333 	 * We return a pointer to struct GMT_INT_SELECTION, which holds the info.
16334 	 */
16335 	unsigned int pos = 0;
16336 	uint64_t k = 0, n = 0, n_items;
16337 	int64_t i, start = -1, stop = -1, step, max_value = 0, value = 0;
16338 	struct GMT_INT_SELECTION *select = NULL;
16339 	char p[GMT_BUFSIZ] = {""}, **list = NULL;
16340 
16341 	if (!item || !item[0]) return (NULL);	/* Nothing to do */
16342 	if (item[0] == '~') k = 1;		/* We want the inverse selection */
16343 	if (item[k] == '+' && item[k+1] == 'f') {	/* Gave +f<file> with segment numbers */
16344 		if ((n_items = gmt_read_list (GMT, &item[k+2], &list)) == 0) {
16345 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find/open file: %s\n", &item[k+2]);
16346 			return (NULL);
16347 		}
16348 	}
16349 	else {	/* Make a list of 1 item */
16350 		list = gmt_M_memory (GMT, NULL, 1, char *);
16351 		list[0] = strdup (&item[k]);
16352 		n_items = 1;
16353 	}
16354 	/* Determine the largest item given or implied; use that for initial array allocation */
16355 	for (n = 0; n < n_items; n++) {
16356 		pos = 0;	/* Reset since gmt_strtok changed it */
16357 		while ((gmt_strtok (list[n], ",-:", &pos, p))) {	/* While it is not empty, process it */
16358 			value = atol (p);
16359 			if (value > max_value) max_value = value;
16360 		}
16361 	}
16362 	max_value++;	/* Since we start at 0, n is the n+1'th item in array */
16363 	select = gmt_M_memory (GMT, NULL, 1, struct GMT_INT_SELECTION);	/* Allocate the selection structure */
16364 	select->item = gmt_M_memory (GMT, NULL, max_value, uint64_t);	/* Allocate the sized array */
16365 	if (k) select->invert = true;		/* Save that we want the inverse selection */
16366 	/* Here we have user-supplied selection information */
16367 	for (k = n = 0; k < n_items; k++) {
16368 		pos = 0;	/* Reset since gmt_strtok changed it */
16369 		while ((gmt_strtok (list[k], ",", &pos, p))) {	/* While it is not empty or there are parsing errors, process next item */
16370 			if ((step = gmtlib_parse_index_range (GMT, p, &start, &stop)) == 0) {
16371 				gmt_free_int_selection (GMT, &select);
16372 				gmt_free_list (GMT, list, n_items);
16373 				return (NULL);
16374 			}
16375 
16376 			/* Now set the item numbers for this sub-range */
16377 			assert (stop < max_value);	/* Somehow we allocated too little */
16378 
16379 			for (i = start; i <= stop; i += step, n++) select->item[n] = i;
16380 		}
16381 	}
16382 	gmt_free_list (GMT, list, n_items);	/* Done with the list */
16383 	/* Here we got something to return */
16384 	select->n = n;							/* Total number of items */
16385 	select->item = gmt_M_memory (GMT, select->item, n, uint64_t);	/* Trim back array size */
16386 	gmt_sort_array (GMT, select->item, n, GMT_ULONG);		/* Sort the selection */
16387 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Number of integer selections returned: %" PRIu64 "\n", n);
16388 #ifdef DEBUG
16389 	if (GMT->current.setting.verbose == GMT_MSG_DEBUG) {
16390 		for (n = 0; n < select->n; n++)
16391 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Selection # %" PRIu64 ": %" PRIu64 "\n", n, select->item[n]);
16392 	}
16393 #endif
16394 
16395 	return (select);
16396 }
16397 
16398 /*! . */
gmt_free_text_selection(struct GMT_CTRL * GMT,struct GMT_TEXT_SELECTION ** S)16399 void gmt_free_text_selection (struct GMT_CTRL *GMT, struct GMT_TEXT_SELECTION **S) {
16400 	/* Free the selection structure */
16401 	if (*S == NULL) return;	/* Nothing to free */
16402 	if ((*S)->pattern) gmt_free_list (GMT, (*S)->pattern, (*S)->n);
16403 	gmt_M_free (GMT, (*S)->regexp);
16404 	gmt_M_free (GMT, (*S)->caseless);
16405 	gmt_M_free (GMT, *S);
16406 }
16407 
16408 /*! . */
gmt_get_segtext_selection(struct GMT_CTRL * GMT,struct GMT_TEXT_SELECTION * S,struct GMT_DATASEGMENT * T,bool last_match)16409 bool gmt_get_segtext_selection (struct GMT_CTRL *GMT, struct GMT_TEXT_SELECTION *S, struct GMT_DATASEGMENT *T, bool last_match) {
16410 	/* Return true if the pattern was found; see at end for what to check for in calling program */
16411 	bool match;
16412 	struct GMT_DATASEGMENT_HIDDEN *TH = gmt_get_DS_hidden (T);
16413 	if (S == NULL || S->n == 0) return (true);	/* No selection criteria given, so can only return true */
16414 	if (last_match && gmt_polygon_is_hole (GMT, T))	/* Check if current polygon is a hole */
16415 		match = true;	/* Extend a true match on a perimeter to its trailing holes */
16416 	else if (S->ogr_match)	/* Compare to single aspatial value */
16417 		match = (TH->ogr && strstr (TH->ogr->tvalue[S->ogr_item], S->pattern[0]) != NULL);		/* true if we matched */
16418 	else if (T->header) {	/* Could be one or n patterns to check */
16419 		uint64_t k = 0;
16420 		match = false;
16421 		while (!match && k < S->n) {
16422 #if !defined(WIN32) || (defined(WIN32) && defined(HAVE_PCRE)) || (defined(WIN32) && defined(HAVE_PCRE2))
16423 			if (S->regexp[k])
16424 			 	match = gmtlib_regexp_match (GMT, T->header, S->pattern[k], S->caseless[k]);	/* true if we matched */
16425 			else
16426 #endif
16427 				match = (strstr (T->header, S->pattern[k]) != NULL);
16428 			k++;
16429 		}
16430 	}
16431 	else	/* No segment header, cannot match */
16432 		match = false;
16433 	return (match);	/* Returns true if found */
16434 	/* (Ctrl->S.inverse == match); Calling function will need to perform this test to see if we are to skip it */
16435 }
16436 
16437 /*! . */
gmt_set_text_selection(struct GMT_CTRL * GMT,char * arg)16438 struct GMT_TEXT_SELECTION * gmt_set_text_selection (struct GMT_CTRL *GMT, char *arg) {
16439 	/* item is of the form [~]<pattern>]
16440 	 * where <pattern> can be
16441 	 * a) A single "string" [e.g., "my pattern"]
16442 	 * b) a name=value for OGR matches [e.g., name="Billy"]
16443 	 * c) A single regex term /regexp/[i] [/tr.t/]; append i for caseless comparison
16444 	 * d) +f<file> a file with a list of the above patterns.
16445 	 * If the leading ~ is given we return the inverse selection (segments that did not match).
16446 	 * Escape ~ or +f at start of an actual pattern with \\~ to bypass their special meanings.
16447 	 * We return a pointer to struct GMT_TEXT_SELECTION, which holds the information.
16448 	 * Programs should call gmt_get_segtext_selection on a segment to determine a match,
16449 	 * and gmt_free_text_selection to free memory when done.
16450 	 */
16451 	uint64_t k = 0, n = 0, n_items, arg_length;
16452 	bool invert = false;
16453 	struct GMT_TEXT_SELECTION *select = NULL;
16454 	char **list = NULL, *item = NULL;
16455 
16456 	if (!arg || !arg[0]) return (NULL);	/* Nothing to do */
16457 	item = strdup (arg);
16458 	if (item[0] == '~') {k = 1, invert = true;}	/* We want the inverse selection, then skip the first char */
16459 	if (item[k] == '+' && item[k+1] == 'f') {	/* Gave [~]+f<file> with list of patterns, one per record */
16460 		if ((n_items = gmt_read_list (GMT, &item[k+2], &list)) == 0) {
16461 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find/open file: %s\n", &item[k+2]);
16462 			gmt_M_str_free (item);
16463 			return (NULL);
16464 		}
16465 	}
16466 	else {	/* Make a list of one item */
16467 		list = gmt_M_memory (GMT, NULL, 1, char *);
16468 		list[0] = strdup (&arg[k]);	/* This skips any leading ~ for inverse indicator */
16469 		n_items = 1;
16470 	}
16471 	select = gmt_M_memory (GMT, NULL, 1, struct GMT_TEXT_SELECTION);	/* Allocate the selection structure */
16472 	select->regexp = gmt_M_memory (GMT, NULL, n_items, bool);		/* Allocate the regexp bool array */
16473 	select->caseless = gmt_M_memory (GMT, NULL, n_items, bool);	/* Allocate the caseless bool array */
16474 	select->invert = invert;
16475 	select->n = n_items;						/* Total number of items */
16476 	for (n = 0; n < n_items; n++) {	/* Processes all the patterns */
16477 		arg_length = strlen (list[n]);
16478 		/* Special case 1: If we start with \~ it means ~ is part of the actual search string, so we must skip the \ */
16479 		/* Special case 2: If we start with \+f it means +f is part of the actual search string and not a file option, so we must skip the \ */
16480 		k = (list[n][0] == '\\' && arg_length > 3 && (list[n][1] == '~' || (list[n][1] == '+' && list[n][2] == 'f'))) ? 1 : 0;
16481 		if (list[n][k] == '/' && list[n][arg_length-2]  == '/'  && list[n][arg_length-1]  == 'i' ) {	/* Case-less regexp string */
16482 			select->regexp[n] = select->caseless[n] = true;
16483 			list[n][arg_length-2] = '\0';	/* remove trailing '/i' from pattern string */
16484 			gmt_strlshift (list[n], 1U);	/* Shift string left to loose the starting '/' */
16485 		}
16486 		else if (list[n][0] == '/' && list[n][arg_length-1]  == '/' ) {	/* Case-honoring regexp string */
16487 			select->regexp[n] = true;
16488 			list[n][arg_length-1] = '\0';	/* remove trailing '/' */
16489 			gmt_strlshift (list[n], 1U);	/* Shift string left to loose the starting '/' */
16490 		}
16491 		/* else we have a fixed pattern string with nothing special to process */
16492 	}
16493 	/* Here we got something to return */
16494 	select->pattern = list;						/* Pass the text list */
16495 
16496 	gmt_M_str_free (item);
16497 	return (select);
16498 }
16499 
16500 /*! . */
gmt_just_to_lonlat(struct GMT_CTRL * GMT,int justify,bool geo,double * x,double * y)16501 void gmt_just_to_lonlat (struct GMT_CTRL *GMT, int justify, bool geo, double *x, double *y) {
16502 	/* See gmt_just_decode for how text code becomes the justify integer.
16503  	 * If an oblique projection is in effect OR the spacing between graticules is
16504  	 * nonlinear AND we are requesting a justification centered in y, then we must
16505 	 * use the projectioned coordinates and invert for lon,lat, else we can do our
16506 	 * calculation on the original (geographic or Cartesian) coordinates. */
16507 	int i, j;
16508 	double *box = NULL;
16509 	bool use_proj;
16510 
16511 	i = justify % 4;	/* Split the 2-D justify code into x just 1-3 */
16512 	j = justify / 4;	/* Split the 2-D justify code into y just 0-2 */
16513 	use_proj = (GMT->common.R.oblique || (j == 1 && gmt_M_is_nonlinear_graticule(GMT)));
16514 	box = (use_proj) ? GMT->current.proj.rect : GMT->common.R.wesn;
16515 	if (!geo) {	/* Check for negative Cartesian scales */
16516 		if (!GMT->current.proj.xyz_pos[GMT_X]) i = 4 - i;	/* Negative x-scale, flip left-to-right */
16517 		if (!GMT->current.proj.xyz_pos[GMT_Y]) j = 2 - j;	/* Negative y-scale, flip top-to-bottom */
16518 	}
16519 	if (i == 1)
16520 		*x = box[XLO];
16521 	else if (i == 2)
16522 		*x = (box[XLO] + box[XHI]) / 2;
16523 	else
16524 		*x = box[XHI];
16525 
16526 	if (j == 0)
16527 		*y = box[YLO];
16528 	else if (j == 1)
16529 		*y = (box[YLO] + box[YHI]) / 2;
16530 	else
16531 		*y = box[YHI];
16532 	if (use_proj) {	/* Actually computed x,y in inches for oblique box, must convert to lon lat */
16533 		double xx = *x, yy = *y;
16534 		gmt_xy_to_geo (GMT, x, y, xx, yy);
16535 	}
16536 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Converted code %d to i = %d, j = %d and finally x = %g and y = %g\n", justify, i, j, *x, *y);
16537 }
16538 
gmt_just_to_xy(struct GMT_CTRL * GMT,int justify,double * x,double * y)16539 void gmt_just_to_xy (struct GMT_CTRL *GMT, int justify,double *x, double *y) {
16540 	/* See gmt_just_decode for how text code becomes the justify integer.
16541  	 * If an oblique projection is in effect OR the spacing between graticules is
16542  	 * nonlinear AND we are requesting a justification centered in y, then we must
16543 	 * use the projected coordinates directly and return x,y in plot coordinates. */
16544 	int i, j;
16545 
16546 	i = justify % 4;	/* Split the 2-D justify code into x just 1-3 */
16547 	j = justify / 4;	/* Split the 2-D justify code into y just 0-2 */
16548 	/* Check for negative Cartesian scales */
16549 	//if (!GMT->current.proj.xyz_pos[GMT_X]) i = 4 - i;	/* Negative x-scale, flip left-to-right */
16550 	//if (!GMT->current.proj.xyz_pos[GMT_Y]) j = 2 - j;	/* Negative y-scale, flip top-to-bottom */
16551 	if (i == 1)
16552 		*x = GMT->current.proj.rect[XLO];
16553 	else if (i == 2)
16554 		*x = (GMT->current.proj.rect[XLO] + GMT->current.proj.rect[XHI]) / 2;
16555 	else
16556 		*x = GMT->current.proj.rect[XHI];
16557 
16558 	if (j == 0)
16559 		*y = GMT->current.proj.rect[YLO];
16560 	else if (j == 1)
16561 		*y = (GMT->current.proj.rect[YLO] + GMT->current.proj.rect[YHI]) / 2;
16562 	else
16563 		*y = GMT->current.proj.rect[YHI];
16564 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Converted code %d to i = %d, j = %d and finally x = %g and y = %g\n", justify, i, j, *x, *y);
16565 }
16566 
16567 /*! . */
gmtlib_refpoint_to_panel_xy(struct GMT_CTRL * GMT,int refpoint,struct GMT_SUBPLOT * P,double * x,double * y)16568 void gmtlib_refpoint_to_panel_xy (struct GMT_CTRL *GMT, int refpoint, struct GMT_SUBPLOT *P, double *x, double *y) {
16569 	/* Takes the refpoint value and converts to panel position in inches. */
16570 	int i, j;
16571 	double w = P->w + P->gap[XLO] + P->gap[XHI];
16572 	double h = P->h + P->gap[YLO] + P->gap[YHI];
16573 	gmt_M_unused (GMT);
16574 
16575 	i = refpoint % 4;	/* Split the 2-D justify code into x just 1-3 */
16576 	j = refpoint / 4;	/* Split the 2-D justify code into y just 0-2 */
16577 	if (i == 1)
16578 		*x = 0.0;
16579 	else if (i == 2)
16580 		*x = 0.5 * w;
16581 	else
16582 		*x = w;
16583 
16584 	if (j == 0)
16585 		*y = 0.0;
16586 	else if (j == 1)
16587 		*y = 0.5 * h;
16588 	else
16589 		*y = h;
16590 }
16591 
16592 /*! . */
gmt_free_refpoint(struct GMT_CTRL * GMT,struct GMT_REFPOINT ** Ap)16593 void gmt_free_refpoint (struct GMT_CTRL *GMT, struct GMT_REFPOINT **Ap) {
16594 	struct GMT_REFPOINT *A = *Ap;
16595 	if (A == NULL) return;	/* Nothing */
16596 	gmt_M_str_free (A->args);
16597 	gmt_M_free (GMT, A);
16598 	A = NULL;
16599 }
16600 
16601 /*! . */
gmt_get_refpoint(struct GMT_CTRL * GMT,char * arg_in,char option)16602 struct GMT_REFPOINT * gmt_get_refpoint (struct GMT_CTRL *GMT, char *arg_in, char option) {
16603 	/* Used to decipher option -D in psscale, pslegend, and psimage:
16604 	 * -D[g|j|n|x]<refpoint>[/<remainder]
16605 	 * where g means map coordinates, n means normalized coordinates, and x means plot coordinates.
16606 	 * For j we instead spacify a 2-char justification code and get the reference point from the corresponding
16607 	 * plot box coordinates; the <refpoint> point is the coordinate pair <x0>/<y0>.
16608 	 * All -D flavors except -Dx require -R -J.
16609 	 * Remaining arguments are returned as well via the string A->args.
16610 	 * also used to parse refpoint in scales -L and -T hence the option argument.
16611 	 */
16612 	unsigned int n_errors = 0, k = 1;	/* Assume 1st character tells us the mode */
16613 	int n, justify = 0;
16614 	enum GMT_enum_refpoint mode = GMT_REFPOINT_NOTSET;
16615 	char txt_x[GMT_LEN256] = {""}, txt_y[GMT_LEN256] = {""}, the_rest[GMT_LEN256] = {""};
16616 	static char *kind = GMT_REFPOINT_CODES;	/* The five types of refpoint specifications */
16617 	char *arg = strdup (arg_in);	/* SInce it may be a constant */
16618 	struct GMT_REFPOINT *A = NULL;
16619 
16620 	switch (arg[0]) {
16621 		case 'n':	mode = GMT_REFPOINT_NORM;		break;	/* Normalized coordinates */
16622 		case 'g':	mode = GMT_REFPOINT_MAP;		break;	/* Map coordinates */
16623 		case 'j':	mode = GMT_REFPOINT_JUST;		break;	/* Map box with justification code */
16624 		case 'J':	mode = GMT_REFPOINT_JUST_FLIP;		break;	/* Map box with mirrored justification code */
16625 		case 'x':	mode = GMT_REFPOINT_PLOT;		break;	/* Plot coordinates */
16626 		default: 	k = 0;	break;	/* None given, reset first arg to be at position 0 */
16627 	}
16628 	if (mode == GMT_REFPOINT_JUST || mode == GMT_REFPOINT_JUST_FLIP) {	/* Here we know k == 1 */
16629 		n = gmtsupport_find_mod_syntax_start (arg, k);	/* Returns position of first modifier +? */
16630 		if (arg[n]) {	/* Separated via +modifiers (or nothing follows), but here we know just is 2 chars */
16631 			strncpy (txt_x, &arg[k], 2);	txt_x[2] = 0;
16632 			strncpy (the_rest, &arg[n], GMT_LEN256-1);
16633 		}
16634 		else {	/* Old syntax with no +modifier and with things separated by slashes */
16635 			if ((n = sscanf (&arg[k], "%[^/]/%s", txt_x, the_rest)) < 1) {
16636 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Parsing of arguments (%s) failed \n", option, &arg[k]);
16637 				gmt_M_str_free (arg);
16638 				return NULL;	/* Not so good */
16639 			}
16640 		}
16641 		justify = gmt_just_decode (GMT, txt_x, PSL_MC);
16642 	}
16643 	else {	/* Must worry about leading + signs in the numbers that might confuse us w.r.t. modifiers */
16644 		/* E.g., -Dg123.3/+19+jTL we don't want to trip up on +19 as modifier! */
16645 		n = gmtsupport_find_mod_syntax_start (arg, k);
16646 		if (arg[n]) { /* Separated via +modifiers (or nothing follows) */
16647 			int n2;
16648 			strncpy (the_rest, &arg[n], GMT_LEN256-1);
16649 			arg[n] = 0;	/* Chop off modifiers temporarily */
16650 			if (mode == GMT_REFPOINT_NOTSET && strlen (arg) == 2 && strchr ("LMRBCT", toupper(arg[GMT_X])) && strchr ("LMRBCT", toupper(arg[GMT_Y]))) {	/* Apparently a 2-char justification code */
16651 				mode = GMT_REFPOINT_JUST;
16652 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your -%c option was interpreted to mean -%c%c\n", option, option, kind[mode]);
16653 			}
16654 			else if (n == 0) {	/* Got no reference point, default to x0/0 */
16655 				strcpy (txt_x, "0");	strcpy (txt_y, "0");
16656 				mode = GMT_REFPOINT_PLOT;
16657 			}
16658 			else if ((n2 = sscanf (&arg[k], "%[^/]/%s", txt_x, txt_y)) < 2) {
16659 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Parsing of reference point (%s) failed - expected slash-separated pair of coordinates\n", option, &arg[k]);
16660 				arg[n] = '+';	/* Restore modifiers */
16661 				gmt_M_str_free (arg);
16662 				return NULL;	/* Not so good */
16663 			}
16664 		}
16665 		else { /* No such modifiers given, so just slashes or nothing follows */
16666 			if ((n = sscanf (&arg[k], "%[^/]/%[^/]/%s", txt_x, txt_y, the_rest)) < 2) {
16667 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Parsing of arguments (%s )failed \n", option, &arg[k]);
16668 				gmt_M_str_free (arg);
16669 				return NULL;	/* Not so good */
16670 			}
16671 		}
16672 	}
16673 
16674 	if (mode == GMT_REFPOINT_NOTSET) {	/* Did not specify what reference point mode to use, must determine it from args */
16675 		if (strchr (GMT_DIM_UNITS, txt_x[strlen(txt_x)-1]))		/* x position included a unit */
16676 			mode = GMT_REFPOINT_PLOT;
16677 		else if (strchr (GMT_DIM_UNITS, txt_y[strlen(txt_y)-1]))	/* y position included a unit */
16678 			mode = GMT_REFPOINT_PLOT;
16679 		else if (strlen (txt_x) == 2 && strchr ("LMRBCT", toupper(txt_x[GMT_X])) && strchr ("LMRBCT", toupper(txt_x[GMT_Y])))	/* Apparently a 2-char justification code */
16680 			mode = GMT_REFPOINT_JUST;
16681 		else if (GMT->common.J.active == false && GMT->common.R.active[RSET] == false)	/* No -R, -J were given so can only mean plot coordinates */
16682 			mode = GMT_REFPOINT_PLOT;
16683 		else {	/* Must assume the user gave map coordinates */
16684 			mode = GMT_REFPOINT_MAP;
16685 		}
16686 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your -%c option was interpreted to mean -%c%c\n", option, option, kind[mode]);
16687 	}
16688 	/* Here we know or have assumed the mode and can process coordinates accordingly */
16689 
16690 	if (mode != GMT_REFPOINT_PLOT) {	/* Will require -R -J so check that these have been parsed */
16691 		gmt_set_missing_options (GMT, "JR");	/* If mode is modern, they exist in the history, and if an overlay we may add these from history automatically */
16692 		if (GMT->common.J.active == false && GMT->common.R.active[RSET] == false) {
16693 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Your -%c%c reference point coordinates require both -R -J to be specified\n", option, kind[mode]);
16694 			gmt_M_str_free (arg);
16695 			return NULL;
16696 		}
16697 	}
16698 
16699 	/* Here we have something to return */
16700 	A = gmt_M_memory (GMT, NULL, 1, struct GMT_REFPOINT);
16701 	switch (mode) {
16702 		case GMT_REFPOINT_NORM:
16703 			A->x = atof (txt_x);
16704 			A->y = atof (txt_y);
16705 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Anchor point specified via normalized coordinates: %g, %g\n", A->x, A->y);
16706 			break;
16707 		case GMT_REFPOINT_PLOT:
16708 		 	A->x = gmt_M_to_inch (GMT, txt_x);
16709 		 	A->y = gmt_M_to_inch (GMT, txt_y);
16710 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Anchor point specified via plot coordinates (in inches): %g, %g\n", A->x, A->y);
16711 			break;
16712 		case GMT_REFPOINT_JUST:
16713 		case GMT_REFPOINT_JUST_FLIP:
16714 			A->justify = justify;
16715 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Anchor point specified via justification code: %s\n", txt_x);
16716 			break;
16717 		case GMT_REFPOINT_MAP:
16718 			n_errors += gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_X), gmt_scanf (GMT, txt_x, gmt_M_type (GMT, GMT_IN, GMT_X), &A->x), txt_x);
16719 			n_errors += gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_Y), gmt_scanf (GMT, txt_y, gmt_M_type (GMT, GMT_IN, GMT_Y), &A->y), txt_y);
16720 			if (n_errors)
16721 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Could not parse geographic coordinates %s and/or %s\n", option, txt_x, txt_y);
16722 			else
16723 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Anchor point specified via map coordinates: %g, %g\n", A->x, A->y);
16724 			break;
16725 		default:		/* Here to prevent a warning */
16726 			break;
16727 	}
16728 	if (n_errors)	/* Failure; free refpoint structure */
16729 		gmt_free_refpoint (GMT, &A);
16730 	else {	/* Assign args */
16731 		A->mode = mode;
16732 		A->args = strdup (the_rest);
16733 	}
16734 	gmt_M_str_free (arg);
16735 
16736 	return (A);
16737 }
16738 
16739 /*! . */
gmt_set_refpoint(struct GMT_CTRL * GMT,struct GMT_REFPOINT * A)16740 void gmt_set_refpoint (struct GMT_CTRL *GMT, struct GMT_REFPOINT *A) {
16741 	/* Update settings after -R -J and map setup has taken place */
16742 	double x, y;
16743 	if (A->mode == GMT_REFPOINT_MAP) {	/* Convert from map coordinates to plot coordinates */
16744 		gmt_geo_to_xy (GMT, A->x, A->y, &x, &y);
16745 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Convert map reference point coordinates from %g, %g to %g, %g\n", A->x, A->y, x, y);
16746 		A->x = x;	A->y = y;
16747 	}
16748 	else if (A->mode == GMT_REFPOINT_JUST) {	/* Convert from justify code to map coordinates, then to plot coordinates */
16749 		/* Since intended for inside frame items (scales) we use the bounding box rectangle to get the plot coordinate from the code */
16750 		gmt_just_to_xy (GMT, A->justify, &x, &y);
16751 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Convert code inside reference point coordinates from justification %s to %g, %g\n", GMT_just_code[A->justify], A->x, A->y);
16752 		A->x = x;	A->y = y;
16753 	}
16754 	else if (A->mode == GMT_REFPOINT_JUST_FLIP) {	/* Convert from justify code to plot coordinates */
16755 		/* Since intended for outside frame items (scales) we use the bounding box rectangle to get the plot coordinate from the code */
16756 		gmt_just_to_xy (GMT, A->justify, &x, &y);
16757 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Convert code outside reference point coordinates from justification %s to %g, %g\n", GMT_just_code[A->justify], A->x, A->y);
16758 		A->x = x;	A->y = y;
16759 	}
16760 	else if (A->mode == GMT_REFPOINT_NORM) {	/* Convert relative to plot coordinates */
16761 		x = A->x * (2.0 * GMT->current.map.half_width);
16762 		y = A->y * GMT->current.map.height;
16763 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Convert normalized reference point coordinates from %g, %g to %g, %g\n", A->x, A->y, x, y);
16764 		A->x = x;	A->y = y;
16765 	}
16766 	/* Now the reference point is given in plot coordinates (inches) */
16767 	A->mode = GMT_REFPOINT_PLOT;
16768 }
16769 
16770 /*! . */
gmt_enable_threads(struct GMT_CTRL * GMT)16771 void gmt_enable_threads (struct GMT_CTRL *GMT) {
16772 	/* Control how many threads to use in Open MP section */
16773 #ifdef _OPENMP
16774 	if (GMT->common.x.active) {
16775 		if (GMT->common.x.n_threads < gmtlib_get_num_processors()) {
16776 			omp_set_dynamic (0);   			/* Explicitly disable dynamic teams */
16777 			omp_set_num_threads (GMT->common.x.n_threads);	/* Use requested threads for all consecutive parallel regions */
16778 		}
16779 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Enable %d threads of %d available\n", GMT->common.x.n_threads, gmtlib_get_num_processors());
16780 	}
16781 	else {	/* Default uses all cores */
16782 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Enable all available threads (up to %d)\n", gmtlib_get_num_processors());
16783 	}
16784 #else
16785 	gmt_M_unused(GMT);
16786 #endif
16787 }
16788 
16789 /*! . */
gmt_validate_modifiers(struct GMT_CTRL * GMT,const char * string,const char option,const char * valid_modifiers,unsigned int verbosity)16790 unsigned int gmt_validate_modifiers (struct GMT_CTRL *GMT, const char *string, const char option, const char *valid_modifiers, unsigned int verbosity) {
16791 	/* Looks for modifiers +<mod> in string and making sure <mod> is
16792 	 * only from the set given by valid_modifiers.  if verbosity is GMT_MSQ_QUIET
16793 	 * we file no errors but just returns the number of valid modifiers found.
16794 	 * Otherwise we return the number of invalid modifiers or 0.
16795 	*/
16796 	bool quoted = false;
16797 	unsigned int n_bad = 0, n_good = 0;
16798 	size_t k, len, start = 0;
16799 
16800 	if (!string || string[0] == 0) return 0;	/* Nothing to check */
16801 	len = strlen (string);
16802 	for (k = 0; start == 0 && k < (len-1); k++) {
16803 		if (string[k] == '\"') quoted = !quoted;	/* Initially false, becomes true at start of quote, then false when exits the quote */
16804 		if (quoted) continue;		/* Not consider +<mod> inside quoted strings */
16805 		if (string[k] == '+') {	/* Possibly a modifier */
16806 			if (k && string[k-1] == 'e' && isdigit (string[k+1])) continue;	/* Exponential notation, e.g. 1e+5 */
16807 			if (strchr (valid_modifiers, string[k+1]))	/* Found a recognized valid modifier */
16808 				n_good++;
16809 			else {	/* Found an invalid modifier or some part of a filename that has + in it */
16810 				if (option)
16811 					GMT_Report (GMT->parent, verbosity, "Option -%c option: Modifier +%c unrecognized\n", option, string[k+1]);
16812 				else
16813 					GMT_Report (GMT->parent, verbosity, "Modifier +%c unrecognized\n", string[k+1]);
16814 				n_bad++;
16815 			}
16816 		}
16817 	}
16818 	return ((verbosity == GMT_MSG_QUIET) ? n_good : n_bad);
16819 }
16820 
16821 /*! . */
gmt_pol_area(double x[],double y[],uint64_t n)16822 double gmt_pol_area (double x[], double y[], uint64_t n) {
16823 	uint64_t i;
16824 	double area, xold, yold;
16825 
16826 	/* Trapezoidal area calculation for Cartesian coordinates.
16827 	 * area will be +ve if polygon is CW, negative if CCW */
16828 
16829 	if (n < 3) return (0.0);
16830 	area = yold = 0.0;
16831 	xold = x[n-1];	yold = y[n-1];
16832 
16833 	for (i = 0; i < n; i++) {
16834 		area += (xold - x[i]) * (yold + y[i]);
16835 		xold = x[i];	yold = y[i];
16836 	}
16837 	return (0.5 * area);
16838 }
16839 
gmtsupport_cart_centroid(const double * x,const double * y,uint64_t n,double * centroid)16840 GMT_LOCAL void gmtsupport_cart_centroid (const double *x, const double *y, uint64_t n, double *centroid) {
16841 	double det = 0.0, tempDet, m;
16842 	uint64_t i, j = 1;
16843 
16844 	if (n < 4) return;	/* Triangle is smallest polygon */
16845 	n--;	/* Since last point repeats the first */
16846 	centroid[GMT_X] = centroid[GMT_Y] = 0.0;
16847 
16848 	for (i = 0; i < n; i++, j++) {
16849 		/* Compute the determinant */
16850 		tempDet = x[i] * y[j] - x[j] * y[i];
16851 		det += tempDet;
16852 		/* Update centroid sum */
16853 		centroid[GMT_X] += (x[i] + x[j]) * tempDet;
16854 		centroid[GMT_Y] += (y[i] + y[j]) * tempDet;
16855 	}
16856 
16857 	m = 3 * det;	/* Divide by the total "mass" of the polygon */
16858 	centroid[GMT_X] /= m;
16859 	centroid[GMT_Y] /= m;
16860 }
16861 
gmtsupport_cart_centroid_area(struct GMT_CTRL * GMT,const double * x,const double * y,uint64_t n,double * centroid)16862 GMT_LOCAL double gmtsupport_cart_centroid_area (struct GMT_CTRL *GMT, const double *x, const double *y, uint64_t n, double *centroid) {
16863 	double area, *xp = NULL, *yp = NULL;
16864 	uint64_t i;
16865 
16866 	if (n < 4) return 0.0;	/* Triangle is smallest polygon */
16867 	gmtsupport_cart_centroid (x, y, n, centroid);
16868 
16869 	n--;	/* Since last point repeats the first */
16870 
16871 	xp = gmt_M_memory (GMT, NULL, n, double);	yp = gmt_M_memory (GMT, NULL, n, double);
16872 	for (i = 0; i < n; i++) {	/* Just take out centroid coordinates */
16873 		xp[i] = x[i] - centroid[GMT_X];
16874 		yp[i] = y[i] - centroid[GMT_Y];
16875 	}
16876 	area = gmt_pol_area (xp, yp, n);	/* Signed area */
16877 	gmt_M_free (GMT, xp);
16878 	gmt_M_free (GMT, yp);
16879 
16880 	return (area);
16881 }
16882 
16883 /*! . */
gmt_centroid_area(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,int geo,double * pos)16884 double gmt_centroid_area (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, int geo, double *pos) {
16885 	/* Estimate centroid and area of a polygon.  geo is 1 if geographic data. Input data remains unchanged.
16886 	 * area will be +ve if polygon is CW, negative if CCW */
16887 	double area;
16888 	if (geo)	/* Spherical centroid and area */
16889 		area = gmtlib_geo_centroid_area (GMT, x, y, n, pos);
16890 	else	/* Cartesian centroid and area */
16891 		area = gmtsupport_cart_centroid_area (GMT, x, y, n, pos);
16892 	return (area);
16893 }
16894 
gmt_polygon_orientation(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,int geo)16895 unsigned int gmt_polygon_orientation (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, int geo) {
16896 	/* Estimate area of a polygon.  geo is 1 if geographic data. Input data remains unchanged.
16897 	 * Since area will be +ve if polygon is CW, negative if CCW we return */
16898 	double area, dummy[2];
16899 	if (geo)	/* Spherical centroid and area */
16900 		area = gmtlib_geo_centroid_area (GMT, x, y, n, dummy);
16901 	else	/* Cartesian centroid and area */
16902 		area = gmtsupport_cart_centroid_area (GMT, x, y, n, dummy);
16903 	return ((area < 0.0) ? GMT_POL_IS_CCW : GMT_POL_IS_CW);
16904 }
16905 
16906 /*! . */
gmt_mean_point(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,int geo,double * pos)16907 void gmt_mean_point (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, int geo, double *pos) {
16908 	/* Estimate mean position.  geo is 1 if geographic data (requiring vector mean).  Input data remains unchanged. */
16909 	uint64_t i, k;
16910 
16911 	assert (n > 0);	/* So n is >= 1 below */
16912 	if (n == 1) {	/* Single point means we return the point as is */
16913 		pos[GMT_X] = x[0];	pos[GMT_Y] = y[0];
16914 		return;
16915 	}
16916 
16917 	if (geo) {	/* Geographic data, must use vector mean */
16918 		double P[3], M[3] = {0.0, 0.0, 0.0}, yc;
16919 		for (i = 0; i < n; i++) {
16920 			yc = gmt_lat_swap (GMT, y[i], GMT_LATSWAP_G2O);	/* Convert to geocentric */
16921 			gmt_geo_to_cart (GMT, yc, x[i], P, true);
16922 			for (k = 0; k < 3; k++) M[k] += P[k];	/* Add up the three components separately */
16923 		}
16924 		gmt_normalize3v (GMT, M);
16925 		gmt_cart_to_geo (GMT, &pos[GMT_Y], &pos[GMT_X], M, true);
16926 		pos[GMT_Y] = gmt_lat_swap (GMT, pos[GMT_Y], GMT_LATSWAP_O2G);	/* Convert back to geodetic */
16927 	}
16928 	else	/* Cartesian centroid */
16929 		gmtsupport_cart_centroid (x, y, n, pos);
16930 }
16931 
16932 /*! . */
gmt_adjust_refpoint(struct GMT_CTRL * GMT,struct GMT_REFPOINT * ref,double dim[],double off[],int justify,int anchor)16933 void gmt_adjust_refpoint (struct GMT_CTRL *GMT, struct GMT_REFPOINT *ref, double dim[], double off[], int justify, int anchor) {
16934 	/* Adjust reference point based on size and justification of plotted item, towards a given anchor */
16935 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Before justify = %d, Dim x = %g y = %g, Reference x = %g y = %g\n",
16936 	            justify, dim[GMT_X], dim[GMT_Y], ref->x, ref->y);
16937 	ref->x += 0.5 * (anchor%4 - justify%4) * dim[GMT_X];
16938 	ref->y += 0.5 * (anchor/4 - justify/4) * dim[GMT_Y];
16939 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "After justify = %d, Offset x = %g y = %g, Reference x = %g y = %g\n",
16940 	            justify, off[GMT_X], off[GMT_Y], ref->x, ref->y);
16941 	/* Also deal with any justified offsets if given */
16942 	if (justify%4 == 3)	/* Right aligned */
16943 		ref->x -= off[GMT_X];
16944 	else /* Left or center aligned */
16945 		ref->x += off[GMT_X];
16946 	if (justify/4 == 2)	/* Top aligned */
16947 		ref->y -= off[GMT_Y];
16948 	else /* Bottom or middle aligned */
16949 		ref->y += off[GMT_Y];
16950 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "After shifts, Reference x = %g y = %g\n", ref->x, ref->y);
16951 }
16952 
gmt_trim_requested(struct GMT_CTRL * GMT,struct GMT_PEN * P)16953 bool gmt_trim_requested (struct GMT_CTRL *GMT, struct GMT_PEN *P) {
16954 	gmt_M_unused(GMT);
16955 	if (P == NULL) return false;	/* No settings given */
16956 	if (gmt_M_is_zero (P->end[BEG].offset) && gmt_M_is_zero (P->end[END].offset) && P->end[BEG].V == NULL && P->end[END].V == NULL)
16957 		return false;	/* No trims given */
16958 	return true;
16959 }
16960 
gmt_trim_line(struct GMT_CTRL * GMT,double ** xx,double ** yy,uint64_t * nn,struct GMT_PEN * P)16961 unsigned int gmt_trim_line (struct GMT_CTRL *GMT, double **xx, double **yy, uint64_t *nn, struct GMT_PEN *P) {
16962 	/* Recompute start and end points of line if offset trims are set */
16963 	int64_t last, next, current = 0, inc, n, new_n, start[2] = {0,0}, orig_start[2] = {0,0}, stop[2] = {0,0}, new[2] = {0,0};
16964 	int increment[2] = {1, -1};
16965 	unsigned int k, proj_type, effect;
16966 	double *x = NULL, *y = NULL, dist, ds = 0.0, f1, f2, x0, x1 = 0, y0, y1 = 0, offset;
16967 
16968 	if (!gmt_trim_requested (GMT, P)) return 0;	/* No trimming requested */
16969 
16970 	/* Here we must do some trimming */
16971 	x = *xx;	y = *yy;	n = (int64_t)*nn;	/* Input arrays and length */
16972 	new[END] = start[END] = orig_start[END] = stop[BEG] = n-1;
16973 	for (effect = 0; effect < 2; effect++) {	/* effect = 0: +o trimming, effect = 1: +v trimming */
16974 		for (k = 0; k < 2; k++) {
16975 			if (effect == 0 && gmt_M_is_zero (P->end[k].offset))	/* No trim at this end */
16976 				continue;
16977 			else if (effect == 1 && (P->end[k].V == NULL || gmt_M_is_zero (P->end[k].length)))	/* No vector of any size at this end */
16978 				continue;
16979 			proj_type = (effect == 0) ? gmt_init_distaz (GMT, P->end[k].unit, P->end[k].type, GMT_MAP_DIST) : GMT_GEO2CART;
16980 			if (proj_type == GMT_NOT_A_VALID_TYPE) return 0;	/* Failure */
16981 			next = start[k];	last = stop[k];	inc = increment[k];
16982 			dist = 0.0;
16983 			if (proj_type == GMT_GEO2CART) gmt_geo_to_xy (GMT, x[next], y[next], &x1, &y1);
16984 			offset = (effect == 0) ? P->end[k].offset : P->end[k].length;
16985 			while (dist < offset && next != last) {	/* Must trim more */
16986 				current = next;
16987 				next += inc;
16988 				if (proj_type == GMT_GEO2CART) {	/* Project and get distances in inches */
16989 					x0 = x1;	y0 = y1;
16990 					gmt_geo_to_xy (GMT, x[next], y[next], &x1, &y1);
16991 					ds = hypot (x0 - x1, y0 - y1);
16992 				}
16993 				else	/* User distances */
16994 					ds = gmt_distance (GMT, x[next], y[next], x[current], y[current]);
16995 				dist += ds;
16996 			}
16997 			if (next == last)	/* Trimmed away the entire line */
16998 				return 1;
16999 			/* Most likely dist now exceeds trim unless by fluke it is equal */
17000 			/* Must revise terminal point */
17001 			f1 = (gmt_M_is_zero (ds)) ? 1.0 : (dist - offset) / ds;
17002 			f2 = 1.0 - f1;
17003 			y[current] = y[current] * f1 + y[next] * f2;
17004 			if (gmt_M_x_is_lon (GMT, GMT_IN)) {	/* Must worry about longitude jump */
17005 				double del = x[next] - x[current];
17006 				gmt_M_set_delta_lon (x[current], x[next], del);
17007 				x[current] += del * f2;
17008 			}
17009 			else	/* Cartesian */
17010 				x[current] = x[current] * f1 + x[next] * f2;
17011 			new[k] = current;	/* First (or last) point in trimmed line */
17012 		}
17013 		start[BEG] = new[BEG];	start[END] = new[END];
17014 		stop[BEG]  = new[END];	stop[END]  = new[BEG];
17015 	}
17016 	if (new[END] <= new[BEG]) return 1;	/* Trimmed past each end */
17017 	if (new[BEG] == orig_start[BEG] && new[END] == orig_start[END]) return 0;	/* No change in segment length so no need to reallocate */
17018 	new_n = new[END] - new[BEG] + 1;	/* New length of line */
17019 	gmt_prep_tmp_arrays (GMT, GMT_NOTSET, new_n, 2);	/* Init or reallocate tmp vectors */
17020 	gmt_M_memcpy (GMT->hidden.mem_coord[GMT_X], &x[new[BEG]], new_n, double);
17021 	gmt_M_memcpy (GMT->hidden.mem_coord[GMT_Y], &y[new[BEG]], new_n, double);
17022 	gmt_M_free (GMT, x);	gmt_M_free (GMT, y);
17023 	*xx = gmtlib_assign_vector (GMT, new_n, GMT_X);
17024 	*yy = gmtlib_assign_vector (GMT, new_n, GMT_Y);
17025 	*nn = (uint64_t)new_n;
17026 	return 0;
17027 }
17028 
gmt_memory_use(size_t bytes,int width)17029 char * gmt_memory_use (size_t bytes, int width) {
17030 	/* Format the given bytes in terms of kb, Mb, or Gb, or Tb.
17031 	 * Width is the precision, e.g., 1 or 3 probably */
17032 	static char mem_report[GMT_LEN32] = {""};
17033 	static char *unit = "kMGT";	/* bytes, kilo-, Mega-, Giga-, Tera- */
17034 	unsigned int kind = 0;
17035 	if (bytes < 1000)
17036 		snprintf (mem_report, GMT_LEN32, "%d bytes", (int)bytes);
17037 	else {
17038 		double mem = bytes / 1024.0;	/* Report kb unless it is too much */
17039 		while (mem > 1024.0 && kind < strlen(unit)) { mem /= 1024.0; kind++; }	/* Goto next higher unit */
17040 		snprintf (mem_report, GMT_LEN32, "%.*f %cb", width, mem, unit[kind]);
17041 	}
17042 	return mem_report;
17043 }
17044 
gmt_new_record(struct GMT_CTRL * GMT,double * d,char * t)17045 struct GMT_RECORD *gmt_new_record (struct GMT_CTRL *GMT, double *d, char *t) {
17046 	/* Allocate a new record structure and initialize to d and t */
17047 	struct GMT_RECORD *R = gmt_M_memory (GMT, NULL, 1, struct GMT_RECORD);
17048 	R->data = d;
17049 	R->text = t;
17050 	return R;
17051 }
17052 
gmt_argv2str(struct GMT_CTRL * GMT,int argc,char * argv[])17053 char * gmt_argv2str (struct GMT_CTRL *GMT, int argc, char *argv[]) {
17054 	/* Consolidate all args into a string */
17055 	int k;
17056 	size_t len = argc;	/* Space for spaces and final 0 */
17057 	char *string = NULL;
17058 	gmt_M_unused(GMT);
17059 	if (argc == 0 || argv == NULL) return NULL;
17060 	for (k = 0; k < argc; k++) len += strlen (argv[k]);
17061 	string = calloc (len, sizeof (char));
17062 	strcat (string, argv[0]);
17063 	for (k = 1; k < argc; k++) {
17064 		strcat (string, " ");
17065 		strcat (string, argv[k]);
17066 	}
17067 	return string;
17068 }
17069 
17070 /*
17071  * Function to Convert Numbers to Roman Numerals
17072  * [http://www.sanfoundry.com/c-program-convert-numbers-roman/]
17073  */
17074 
gmtsupport_predigit(char num1,char num2,char string[],unsigned int * i)17075 GMT_LOCAL void gmtsupport_predigit(char num1, char num2, char string[], unsigned int *i) {
17076     string[(*i)++] = num1;
17077     string[(*i)++] = num2;
17078 }
17079 
gmtsupport_postdigit(char c,unsigned int n,char string[],unsigned int * i)17080 GMT_LOCAL void gmtsupport_postdigit (char c, unsigned int n, char string[], unsigned int *i) {
17081     unsigned int j;
17082     for (j = 0; j < n; j++)
17083         string[(*i)++] = c;
17084 }
17085 
gmt_arabic2roman(unsigned int number,char string[],size_t size,bool lower)17086 char *gmt_arabic2roman (unsigned int number, char string[], size_t size, bool lower) {
17087 	/* Given number, return string to roman numeral, lowercase if lower is true */
17088 	unsigned i = 0;
17089 	if (string == NULL) return NULL;
17090 	gmt_M_memset (string, size, char);
17091 	while (number != 0) {
17092 		if (number >= 1000) {
17093             gmtsupport_postdigit ('M', number / 1000, string, &i);
17094             number = number - (number / 1000) * 1000;
17095         }
17096 		else if (number >= 500) {
17097 			if (number < (500 + 4 * 100)) {
17098 				gmtsupport_postdigit('D', number / 500, string, &i);
17099 				number = number - (number / 500) * 500;
17100 			}
17101 			else {
17102 				gmtsupport_predigit('C','M', string, &i);
17103 				number = number - (1000-100);
17104             }
17105         }
17106 		else if (number >= 100) {
17107 			if (number < (100 + 3 * 100)) {
17108 				gmtsupport_postdigit('C', number / 100, string, &i);
17109 				number = number - (number / 100) * 100;
17110 			}
17111 			else {
17112 				gmtsupport_predigit('L', 'D', string, &i);
17113 				number = number - (500 - 100);
17114 			}
17115 		}
17116 		else if (number >= 50) {
17117 			if (number < (50 + 4 * 10)) {
17118 				gmtsupport_postdigit('L', number / 50, string, &i);
17119 				number = number - (number / 50) * 50;
17120 			} else {
17121 				gmtsupport_predigit('X','C', string, &i);
17122 				number = number - (100-10);
17123 			}
17124 		}
17125 		else if (number >= 10) {
17126 			if (number < (10 + 3 * 10)) {
17127 				gmtsupport_postdigit('X', number / 10, string, &i);
17128 				number = number - (number / 10) * 10;
17129 			} else {
17130 				gmtsupport_predigit('X','L', string, &i);
17131 				number = number - (50 - 10);
17132 			}
17133 		}
17134 		else if (number >= 5) {
17135 			if (number < (5 + 4 * 1)) {
17136 				gmtsupport_postdigit('V', number / 5, string, &i);
17137 				number = number - (number / 5) * 5;
17138 			} else {
17139 				gmtsupport_predigit('I', 'X', string, &i);
17140 				number = number - (10 - 1);
17141 			}
17142 		}
17143 		else if (number >= 1) {
17144 			if (number < 4) {
17145 				gmtsupport_postdigit('I', number / 1, string, &i);
17146 				number = number - (number / 1) * 1;
17147 			} else {
17148 				gmtsupport_predigit('I', 'V', string, &i);
17149 				number = number - (5 - 1);
17150 			}
17151 		}
17152 	}
17153 	if (lower) gmt_str_tolower (string);
17154 	return string;
17155 }
17156 
gmtsupport_unique_array(struct GMT_CTRL * GMT,double * array,uint64_t * n)17157 GMT_LOCAL double *gmtsupport_unique_array (struct GMT_CTRL *GMT, double *array, uint64_t *n) {
17158 	size_t k, j;
17159 	/* Sort the array in case user did not enter values that were monotonically increasing */
17160 	gmt_sort_array (GMT, array, *n, GMT_DOUBLE);
17161 	/* Skip any duplicates in the sorted array */
17162 	k = 0; j = 1;
17163 	while (j < *n) {
17164 		if (doubleAlmostEqualZero (array[j], array[k]))	/* Skip repeated point */
17165 			j++;
17166 		else	/* Copy over unique value */
17167 			array[++k] = array[j++];
17168 	}
17169 	k++;	/* (new) total number of unique values */
17170 	if (k < *n) {	/* Update array count */
17171 		*n = k;
17172 		array = gmt_M_memory (GMT, array, *n, double);	/* Reallocate exact length of array */
17173 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Eliminated %d duplicate values from the sorted array\n", (int)(j-k));
17174 	}
17175 	return array;
17176 }
17177 
gmt_list_to_array(struct GMT_CTRL * GMT,char * list,unsigned int type,bool unique,uint64_t * n)17178 double *gmt_list_to_array (struct GMT_CTRL *GMT, char *list, unsigned int type, bool unique, uint64_t *n) {
17179 	/* Given a comma-separated string of values of type, parse and return array and its length */
17180 	size_t k;
17181 	unsigned int pos = 0;
17182 	char p[GMT_LEN64] = {""};
17183 	double *array = NULL;
17184 	if (list == NULL || list[0] == '\0') {	/* Got nothing, return nothing; likely an error */
17185 		*n = 0;
17186 		return NULL;
17187 	}
17188 	/* Count the commas but not a trailing comma with nothing after it */
17189 	for (k = 0, *n = 1; k < strlen (list); k++) {
17190 		if (list[k] == ',' && list[k+1])
17191 			(*n)++;
17192 	}
17193 	array = gmt_M_memory (GMT, NULL, *n, double);	/* Allocate exact length of array */
17194 	k = 0;
17195 	while ((gmt_strtok (list, ",", &pos, p))) {	/* March along and convert each string to double */
17196 		if (gmt_verify_expectations (GMT, type, gmt_scanf_arg (GMT, p, type, false, &(array[k])), p)) {
17197 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to parse value %s from list %s\n", p, list);
17198 			gmt_M_free (GMT, array);
17199 			return NULL;
17200 		}
17201 		k++;
17202 	}
17203 	if (!unique) return array;	/* Return as is */
17204 
17205 	/* Return a possibly sorted and monotonically increasing array */
17206 	return (gmtsupport_unique_array (GMT, array, n));
17207 }
17208 
gmt_make_equidistant_array(struct GMT_CTRL * GMT,double min,double max,double inc,double ** array)17209 uint64_t gmt_make_equidistant_array (struct GMT_CTRL *GMT, double min, double max, double inc, double **array) {
17210 	/* Just makes an equidistant array given vetted input parameters */
17211 	uint64_t k, n;
17212 	double *val = NULL;
17213 
17214 	n = (doubleAlmostEqualZero (min, max) || gmt_M_is_zero (inc)) ? 1 : lrint (fabs (max - min) / fabs (inc)) + 1;
17215 	val = gmt_M_memory (GMT, NULL, n, double);
17216 	if (inc < 0.0) {	/* Reverse direction max:inc:min */
17217 		for (k = 0; k < n; k++) val[k] = max + k * inc;
17218 		val[n-1] = min;	/* To avoid round-off all the way to the end */
17219 	}
17220 	else {
17221 		for (k = 0; k < n; k++) val[k] = min + k * inc;
17222 		val[n-1] = max;	/* To avoid round-off all the way to the end */
17223 	}
17224 	*array = val;
17225 	return (n);
17226 }
17227 
gmt_duplicate_array(struct GMT_CTRL * GMT,double * array,uint64_t n)17228 double * gmt_duplicate_array (struct GMT_CTRL *GMT, double *array, uint64_t n) {
17229 	/* Simply duplicate the double array */
17230 	double *x = gmt_M_memory (GMT, NULL, n, double);
17231 	gmt_M_memcpy (x, array, n, double);
17232 	return (x);
17233 }
17234 
gmt_parse_array(struct GMT_CTRL * GMT,char option,char * argument,struct GMT_ARRAY * T,unsigned int flags,unsigned int tcol)17235 unsigned int gmt_parse_array (struct GMT_CTRL *GMT, char option, char *argument, struct GMT_ARRAY *T, unsigned int flags, unsigned int tcol) {
17236 	/* Many GMT modules need to set up an equidistant set of values to
17237 	 * serve as output times (or distances), binning boundaries, or similar.
17238 	 * We expect such 1-D arrays to be specified on the command line
17239 	 * via a syntax such as:]
17240 	 *
17241 	 *	-T<argument>
17242 	 *
17243 	 * where <argument> is one of these:
17244 	 *	[<min/max/]<inc>[<unit>|+a|e|i|n|b|l|t|u]
17245 	 *	<file>
17246 	 *
17247 	 * Parsing:
17248 	 *      0) If <argument> is a single value and flags & GMT_ARRAY_SCALAR is set
17249 	 *	   then we create an array of one item. Otherwise <argument> may be
17250 	 *	   interpreted as <inc>.
17251 	 *	1) If <argument> is a file found in our search path then
17252 	 *	   we assume it contains one column with the final values.
17253 	 *	   These are then read in via GMT_Read_Data, which means
17254 	 *	   these values could be passed via VirtualFiles.
17255 	 *	2) If +n is given it means that <inc> is an integer that
17256 	 *	   says how many points we want equidistantly distributed
17257 	 *	   between <min> and <max>.
17258 	 *	3) If +i is given it means that <inc> is the reciprocal of
17259 	 *     what is needed, e.g. use 24i instead of 0.041666...
17260 	 *	4) If <min>/<max> are missing then it means these will
17261 	 *	   be derived from the data extent.  Unless +n is also
17262 	 *	   given then the data extremes will be rounded to the
17263 	 *	   first and last multiple of <inc> that is inside the
17264 	 *	   moin-max data range.
17265 	 *	5) If <unit> is given and is any of the temporal units
17266 	 *	   o|y then we will compute a non-equidistant set of calendar
17267 	 *	   time values.
17268 	 *	6) If <unit> is given and it is any of the spatial length units
17269 	 *	   d|m|s|e|f|k|M|n|u or c (Cartesian), then we will compute
17270 	 *	   distances along a track given by the first two columns and the
17271 	 *	   resulting distances are our time-series values.
17272 	 *  7) If +l is given then we are setting up a log10 array which is
17273 	 *	   equidistant in log10(t).  Here, inc must be 1, 2, or 3 exclusively
17274 	 *	   which translates into
17275 	 *  8) If +b is given then we are setting up a log2 array which is
17276 	 *	   equidistant in log2(t).  Here, inc must be an integer and indicates
17277 	 *	   the log2 increment.
17278 	 *  9) If +a is given then we will add the output array as a new output column.
17279 	 * 10) If +e is given when only an increment is given then we must keep the
17280 	 *	   increment exact and adjust max to ensure (max-min)/inc is an integer.
17281 	 *	   The default adjusts inc instead, if flag GMT_ARRAY_ROUND is passed.
17282 	 *     10) Since -T is a command-line option we enforce ISO calendar string format
17283 	 *	   only (yyyy-mm-ddT, yyyy-jjjT, yyyy-WwwT).
17284 	 *
17285 	 * 11) If +u is given then we ensure the input array has unique and sorted entries
17286 	 *     and that duplicated are eliminated.
17287 	 * Note:   The effects in 4) and 5) are only allowed if the corresponding
17288 	 *	   flags are passed to the parser.
17289 	 */
17290 
17291 	char txt[3][GMT_LEN32] = {{""}, {""}, {""}}, *m = NULL;
17292 	bool has_inc = false;
17293 	int ns = 0;
17294 	size_t len = 0;
17295 
17296 	if (argument == NULL || argument[0] == '\0') {	/* A nothingburger */
17297 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: No arguments given\n", option);
17298 		return GMT_PARSE_ERROR;
17299 	}
17300 	gmt_M_str_free (T->file);		/* In case earlier parsing */
17301 	gmt_M_memset (T, 1, struct GMT_ARRAY);	/* Wipe clean the structure */
17302 
17303 	if (flags & GMT_ARRAY_UNIQUE)	/* This can also be set by the user via +u */
17304 		T->unique = true;	/* Resulting array must contain unique and sorted entries */
17305 
17306 	if (gmt_validate_modifiers (GMT, argument, option, GMT_ARRAY_MODIFIERS, GMT_MSG_ERROR)) return (GMT_PARSE_ERROR);
17307 
17308 	if ((m = gmt_first_modifier (GMT, argument, GMT_ARRAY_MODIFIERS))) {	/* Process optional modifiers +a, +b, +e, +i, +l, +n, +t */
17309 		unsigned int pos = 0;	/* Reset to start of new word */
17310 		unsigned int n_errors = 0;
17311 		char p[GMT_LEN32] = {""};
17312 		while (gmt_getmodopt (GMT, 'T', m, GMT_ARRAY_MODIFIERS, &pos, p, &n_errors) && n_errors == 0) {
17313 			switch (p[0]) {
17314 				case 'a':	/* Add spatial distance column to output */
17315 					T->add = true;
17316 					break;
17317 				case 'b':	/* Do a log2 grid */
17318 					T->logarithmic2 = true;
17319 					break;
17320 				case 'e':	/* Increment must be honored exactly */
17321 					T->exact_inc = true;
17322 					break;
17323 				case 'i':	/* Gave reciprocal increment; calculate inc later */
17324 					T->reciprocal = true;
17325 					break;
17326 				case 'n':	/* Gave number of points instead; calculate inc later */
17327 					T->count = true;
17328 					break;
17329 				case 'l':	/* Do a log10 grid */
17330 					T->logarithmic = true;
17331 					break;
17332 				case 't':	/* Do a time vector */
17333 					T->temporal = true;
17334 					break;
17335 				case 'u':	/* Ensure no duplicates; array must contain unique and sorted entries */
17336 					T->unique = true;
17337 					break;
17338 				default:
17339 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "-%cmin/max/inc+ modifier +%s not recognized.\n", option, p);
17340 					break;	/* These are caught in gmt_getmodopt so break is just for Coverity */
17341 			}
17342 		}
17343 		m[0] = '\0';	/* Chop off the modifiers */
17344 	}
17345 	else if (gmt_M_compat_check (GMT, 5) && argument[strlen(argument)-1] == '+') {	/* Old-style + instead of +n */
17346 		GMT_Report (GMT->parent, GMT_MSG_COMPAT, "-%cmin/max/inc+ is deprecated; use -%c[<min>/<max>/]<int>[+a|n] instead.\n", option, option);
17347 		m = strrchr (argument, '+');	/* Position of last + */
17348 		if (m) m[0] = '\0';	/* Chop off the plus */
17349 		T->count = true;
17350 	}
17351 
17352 	/* 1a. Check if argument is a remote file */
17353 	if (gmt_file_is_cache (GMT->parent, argument) || gmt_M_file_is_url (argument)) {	/* Remote file, must check */
17354 		char path[PATH_MAX] = {""};
17355 		unsigned int first = gmt_download_file_if_not_found (GMT, argument, GMT_CACHE_DIR);
17356 		if (gmt_getdatapath (GMT, &argument[first], path, R_OK) == NULL) {	/* Remote file was not found? */
17357 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot find/open file %s\n", &argument[first]);
17358 			return (GMT_PARSE_ERROR);
17359 		}
17360 		/* File successfully downloaded, just refer to it by actual name */
17361 		T->file = strdup (&argument[first]);
17362 		return (GMT_NOERROR);
17363 	}
17364 
17365 	/* 1b. Check if argument is a memory file */
17366 	if (gmt_M_file_is_memory (argument)) {	/* Yep */
17367 		T->file = strdup (argument);
17368 		return (GMT_NOERROR);
17369 	}
17370 
17371 	/* 1c. Check if argument is a local file */
17372 	if (!gmt_access (GMT, argument, F_OK)) {	/* File exists */
17373 		T->file = strdup (argument);
17374 		return (GMT_NOERROR);
17375 	}
17376 
17377 	/* 1d. Check if we are given a list t1,t2,t3,... */
17378 	if (strchr (argument, ',')) {
17379 		T->list = strdup (argument);
17380 		if (strchr (argument, 'T')) {	/* Gave list of absolute times */
17381 			gmt_set_column_type (GMT, GMT_IN,  tcol, GMT_IS_ABSTIME);	/* Set input column type as time */
17382 			/* Set output column type as time unless -fo has been set */
17383 			if (!GMT->common.f.active[GMT_OUT]) gmt_set_column_type (GMT, GMT_OUT, tcol, GMT_IS_ABSTIME);
17384 			T->temporal = true;
17385 		}
17386 		return (GMT_NOERROR);
17387 	}
17388 
17389 	if (T->count && T->reciprocal) {
17390 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Cannot give both +i and +n\n", option);
17391 		return GMT_PARSE_ERROR;
17392 	}
17393 	/* 2. Dealt with the file option, now parse [<min/max/]<inc> */
17394 	if ((ns = sscanf (argument, "%[^/]/%[^/]/%s", txt[GMT_X], txt[GMT_Y], txt[GMT_Z])) < 1) {
17395 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Must specify valid min[/max[/inc[<unit>|+n]]] option\n", option);
17396 		return GMT_PARSE_ERROR;
17397 	}
17398 	if ((flags & GMT_ARRAY_RANGE) && ns == 1 && (flags & GMT_ARRAY_SCALAR) == 0) {	/* Need to spell out all 3 items */
17399 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Must specify valid min/max/inc[<unit>|+n] option\n", option);
17400 		return GMT_PARSE_ERROR;
17401 	}
17402 	if (!(ns == 1 || ns == 3) && (flags & GMT_ARRAY_NOINC) == 0) {	/* Need to give 1 or 3 items unless inc is optional */
17403 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Must specify valid [min/max/]inc[<unit>|+n] option\n", option);
17404 		return GMT_PARSE_ERROR;
17405 	}
17406 	has_inc = (ns != 2);	/* This means we gave an increment */
17407 	if (ns == 1 && (flags & GMT_ARRAY_SCALAR)) has_inc = false;	/* Actually, just a single -T<value */
17408 	ns--;	/* ns is now the index to the txt array with the increment or count (2 or 0) */
17409 	len = strlen (txt[ns]);	if (len) len--;	/* Now txt[ns][len] holds a unit (or not) */
17410 	if (!has_inc && (T->logarithmic || T->logarithmic2)) {
17411 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Logarithmic array requires an increment argument\n", option);
17412 		return GMT_PARSE_ERROR;
17413 	}
17414 	/* 3. Check if we are working with absolute time.  This means there must be a T in both min or max arguments */
17415 	if (ns >= 1 && (strchr (txt[GMT_X], 'T') || strchr (txt[GMT_Y], 'T'))) {	/* Gave absolute time limits */
17416 		T->temporal = true;
17417 		if (!(flags & GMT_ARRAY_TIME)) {
17418 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Calendar time not allowed for this module\n", option);
17419 			return GMT_PARSE_ERROR;
17420 		}
17421 #if 0	/* Not sure why I added this, commenting it out because it seems to work fine and why shouldn't it... */
17422 		if (T->count) {
17423 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Cannot use +n modifier with calendar time data\n", option);
17424 			return GMT_PARSE_ERROR;
17425 		}
17426 #endif
17427 		/* Now worry about an increment with time units */
17428 		if (has_inc) T->unit = txt[ns][len];
17429 	}
17430 	if (ns == 0 && strchr (GMT_TIME_UNITS, txt[ns][len]) && (T->temporal || (gmt_M_type (GMT, GMT_IN, tcol) & GMT_IS_RATIME))) {	/* Giving time increments only so need to switch to temporal */
17431 		T->temporal = true;	/* May already be set but who cares */
17432 		T->unit = txt[ns][len];
17433 	}
17434 	if (ns == 0 && (flags & GMT_ARRAY_SCALAR) && strchr (txt[GMT_X], 'T')) {	/* Giving time constant only so need to switch to temporal */
17435 		T->temporal = true;	/* May already be set but who cares */
17436 	}
17437 	if (T->temporal) {	/* Must set TIME_UNIT and update time system scalings */
17438 		gmt_set_column_type (GMT, GMT_IN, tcol, GMT_IS_ABSTIME);	/* Set input column type as time */
17439 		/* Set output column type as time unless -fo has been set */
17440 		if (!GMT->common.f.active[GMT_OUT]) gmt_set_column_type (GMT, GMT_OUT, tcol, GMT_IS_ABSTIME);
17441 		if (has_inc) {	/* Gave a time increment */
17442 			if (strchr (GMT_TIME_UNITS, T->unit))	/* Gave a valid time unit */
17443 				txt[ns][len] = '\0';	/* Chop off time unit since we are done with it */
17444 			else	/* User relied on the setting of TIME_UNIT */
17445 				T->unit = GMT->current.setting.time_system.unit;	/* Set assumed time unit */
17446 			/* Check if unit is a variable increment */
17447 			T->vartime = (strchr (GMT_TIME_VAR_UNITS, T->unit) != NULL);
17448 		}
17449 		else	/* Set assumed time unit */
17450 			T->unit = GMT->current.setting.time_system.unit;
17451 	}
17452 	/* 4. Consider spatial distances */
17453 	if (has_inc && !T->temporal && strchr (GMT_LEN_UNITS "c", txt[ns][len])) {	/* Geospatial or Cartesian distances */
17454 		if (!(flags & GMT_ARRAY_DIST)) {
17455 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Distance units not allowed for this module\n", option);
17456 			return GMT_PARSE_ERROR;
17457 		}
17458 		if (txt[ns][len] == 'c') txt[ns][len] = '\0';	/* The c unit has served its purpose */
17459 		/* Gave geospatial distance increment, so initialize distance machinery */
17460 		if (txt[ns][0] == '-' && strchr ("-+", txt[ns][1])) {	/* Rare negative increment before -|+ for geospatial calculation mode */
17461 			T->distmode = gmt_get_distance (GMT, &txt[ns][1], &(T->inc), &(T->unit));
17462 			T->reverse = true;	/* Want array to be reversed */
17463 		}
17464 		else
17465 			T->distmode = gmt_get_distance (GMT, txt[ns], &(T->inc), &(T->unit));
17466 		if (gmt_init_distaz (GMT, T->unit, T->distmode, GMT_MAP_DIST) == GMT_NOT_A_VALID_TYPE)
17467 			return GMT_PARSE_ERROR;
17468 		T->spatial = (T->unit == 'X') ? 1 : 2;
17469 	}
17470 
17471 	/* 5. Get the increment (or count) */
17472 	if (has_inc && !T->spatial) {
17473 		gmt_scanf_float (GMT, txt[ns], &(T->inc));
17474 		if (gmt_M_is_zero (T->inc)) {
17475 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Increment is zero\n", option);
17476 			return GMT_PARSE_ERROR;
17477 		}
17478 		if (T->inc < 0.0 && !T->logarithmic) {	/* Flag to be reversed */
17479 			T->inc = -T->inc;
17480 			T->reverse = true;	/* Want array to be reversed */
17481 		}
17482 		if (T->logarithmic || T->logarithmic2) {
17483 			int k_inc = irint (T->inc);
17484 			if (!gmt_M_is_zero (fabs (T->inc - (double)k_inc))) {
17485 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Logarithmic increment must be an integer\n", option);
17486 				return GMT_PARSE_ERROR;
17487 			}
17488 			if (T->logarithmic && !(k_inc == 1 || k_inc == 2 || k_inc == 3 || k_inc < 0)) {
17489 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Logarithmic increment must be 1, 2, 3 (or a negative integer)\n", option);
17490 				return GMT_PARSE_ERROR;
17491 			}
17492 		}
17493 	}
17494 	/* 6. If the min/max limits were given via argument then it is OK to parse */
17495 	T->set = (has_inc) ? 1 : 0;	/* Maybe have inc so far */
17496 	if (ns >= 1) {
17497 		if (!strcmp (txt[GMT_X], "-"))	/* Must get this value later */
17498 			T->delay[GMT_X] = true;
17499 		else if (T->temporal) {
17500 			if (gmt_scanf_argtime (GMT, txt[GMT_X], &(T->min)) == GMT_IS_NAN) {
17501 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Unable to parse min temporal value from %s (ISO datetime format required)\n", option, txt[GMT_X]);
17502 				return GMT_PARSE_ERROR;
17503 			}
17504 		}
17505 		else if (gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_X), gmt_scanf_arg (GMT, txt[GMT_X], gmt_M_type (GMT, GMT_IN, GMT_X), false, &(T->min)), txt[GMT_X])) {
17506 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Unable to parse min value from %s\n", option, txt[GMT_X]);
17507 			return GMT_PARSE_ERROR;
17508 		}
17509 		if (!strcmp (txt[GMT_Y], "-"))	/* Must get this value later */
17510 			T->delay[GMT_Y] = true;
17511 		else if (T->temporal) {
17512 			if (gmt_scanf_argtime (GMT, txt[GMT_Y], &(T->max)) == GMT_IS_NAN) {
17513 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Unable to parse max temporal value from %s\n", option, txt[GMT_Y]);
17514 				return GMT_PARSE_ERROR;
17515 			}
17516 		}
17517 		else if (gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_X), gmt_scanf_arg (GMT, txt[GMT_Y], gmt_M_type (GMT, GMT_IN, GMT_X), false, &(T->max)), txt[GMT_Y])) {
17518 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Unable to parse max value from %s (ISO datetime format required)\n", option, txt[GMT_Y]);
17519 			return GMT_PARSE_ERROR;
17520 		}
17521 		if ((T->delay[GMT_X] || T->delay[GMT_Y]) && !(flags & GMT_ARRAY_NOMINMAX)) {
17522 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Cannot specify - as min or max value in this module\n", option);
17523 			return GMT_PARSE_ERROR;
17524 		}
17525 		if (!(T->delay[GMT_X] || T->delay[GMT_Y]) && T->min >= T->max) {
17526 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: min >= max\n", option);
17527 			return GMT_PARSE_ERROR;
17528 		}
17529 		T->set += 2;	/* Read both min and max */
17530 	}
17531 	else if (ns == 0 && (flags & GMT_ARRAY_SCALAR)) {
17532 		if (T->temporal) {
17533 			if (gmt_scanf_argtime (GMT, txt[GMT_X], &(T->min)) == GMT_IS_NAN) {
17534 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Unable to parse min temporal value from %s (ISO datetime format required)\n", option, txt[GMT_X]);
17535 				return GMT_PARSE_ERROR;
17536 			}
17537 			T->unit = GMT->current.setting.time_system.unit;	/* Set currently selected time unit so we don't run into issues later despite only having one time knot */
17538 		}
17539 		else if (gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_X), gmt_scanf_arg (GMT, txt[GMT_X], gmt_M_type (GMT, GMT_IN, GMT_X), false, &(T->min)), txt[GMT_X])) {
17540 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Unable to parse min value from %s\n", option, txt[GMT_X]);
17541 			return GMT_PARSE_ERROR;
17542 		}
17543 		T->max = T->min;
17544 	}
17545 	if (has_inc && ns == 0 && (flags & GMT_ARRAY_NOMINMAX)) {	/* The min/max will be set later */
17546 		T->delay[GMT_X] = T->delay[GMT_Y] = true;
17547 	}
17548 	if (flags & GMT_ARRAY_ROUND)	/* Adjust increment to fit min/max so (max-min)/inc is an integer */
17549 		T->round = true;
17550 	if (T->exact_inc && T->set == 3) {
17551 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Modifier +e only applies when increment is given without any range\n", option);
17552 		return GMT_PARSE_ERROR;
17553 
17554 	}
17555 	if (m) m[0] = '+';	/* Restore the modifiers */
17556 	T->col = tcol;
17557 
17558 	return GMT_NOERROR;
17559 }
17560 
gmtlib_var_inc(double * x,uint64_t n)17561 bool gmtlib_var_inc (double *x, uint64_t n) {
17562 	/* Determine if spacing in the array is variable or constant */
17563 	bool fixed = true;	/* Start with assumption of fixed increments */
17564 	uint64_t k;
17565 	double fix_inc, dx;
17566 	if (n <= 2) return false;	/* Strange, but a single point or pair do not imply variable increment for sure */
17567 	fix_inc = x[1] - x[0];
17568 	for (k = 2; fixed && k < n; k++) {
17569 		dx = x[k] - x[k-1];
17570 		if (fabs ((fix_inc - dx) / fix_inc) > GMT_CONV8_LIMIT)
17571 			fixed = false;	/* Not equidistant */
17572 	}
17573 	return (!fixed);
17574 }
17575 
gmt_create_array(struct GMT_CTRL * GMT,char option,struct GMT_ARRAY * T,double * min,double * max)17576 unsigned int gmt_create_array (struct GMT_CTRL *GMT, char option, struct GMT_ARRAY *T, double *min, double *max) {
17577 	/* If min and max are not NULL then will override what T->min,max says */	char unit = GMT->current.setting.time_system.unit;
17578 	double scale = GMT->current.setting.time_system.scale, inc = T->inc, t0, t1;
17579 
17580 	if (T->array) gmt_M_free (GMT, T->array);	/* Free if previously set */
17581 
17582 	if (T->file) {	/* Got a file, read first column into the array; must be one segment only */
17583 		/* Temporarily change what data type col zero is */
17584 		struct GMT_DATASET *D = NULL;
17585 		unsigned int save_coltype[2];
17586 		unsigned int save_trailing = GMT->current.io.trailing_text[GMT_IN];
17587 		unsigned int save_max_cols_to_read = GMT->current.io.max_cols_to_read;
17588 		int error;
17589 
17590 		save_coltype[GMT_IN]  = gmt_get_column_type (GMT, GMT_IN, GMT_X);
17591 		save_coltype[GMT_OUT] = gmt_get_column_type (GMT, GMT_OUT, GMT_X);
17592 		if (T->temporal) gmt_set_column_type (GMT, GMT_IN, GMT_X, GMT_IS_ABSTIME);
17593 		gmt_disable_bghio_opts (GMT);	/* Do not want any -b -g -h -i -o to affect the reading this file */
17594 		GMT->current.io.record_type[GMT_IN] = GMT_READ_NORMAL;
17595 		GMT->current.io.trailing_text[GMT_IN] = false;
17596 		if ((error = GMT_Set_Columns (GMT->parent, GMT_IN, 1, GMT_COL_FIX_NO_TEXT)) != GMT_NOERROR) return (GMT_PARSE_ERROR);
17597 		if ((D = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, T->file, NULL)) == NULL) {
17598 			return (GMT_PARSE_ERROR);
17599 		}
17600 		if (gmt_get_column_type (GMT, GMT_IN, GMT_X) == GMT_IS_ABSTIME) T->temporal = true;	/* We read absolute times from the file */
17601 		gmt_set_column_type (GMT, GMT_IN,  GMT_X, save_coltype[GMT_IN]);
17602 		gmt_set_column_type (GMT, GMT_OUT, GMT_X, save_coltype[GMT_OUT]);
17603 		GMT->current.io.trailing_text[GMT_IN] = save_trailing;
17604 		GMT->current.io.max_cols_to_read = save_max_cols_to_read;
17605 		gmt_reenable_bghio_opts (GMT);	/* Recover settings provided by user (if -b -g -h -i were used at all) */
17606 		if (D->n_segments > 1) {
17607 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: File %s has more than one segment\n", option, T->file);
17608 			GMT_Destroy_Data (GMT->parent, &D);
17609 			return GMT_PARSE_ERROR;
17610 		}
17611 		T->n = D->n_records;
17612 		T->array = gmt_M_memory (GMT, NULL, T->n, double);
17613 		gmt_M_memcpy (T->array, D->table[0]->segment[0]->data[GMT_X], T->n, double);
17614 		GMT_Destroy_Data (GMT->parent, &D);
17615 		if (T->unique)	/* Must sort and eliminate duplicates */
17616 			T->array = gmtsupport_unique_array (GMT, T->array, &(T->n));
17617 		T->var_inc = gmtlib_var_inc (T->array, T->n);
17618 		return GMT_NOERROR;
17619 	}
17620 
17621 	if (T->list) {	/* Got a list, parse and make array */
17622 		if ((T->array = gmt_list_to_array (GMT, T->list, gmt_M_type (GMT, GMT_IN, T->col), T->unique, &(T->n))) == NULL)
17623 			return GMT_PARSE_ERROR;
17624 		T->var_inc = gmtlib_var_inc (T->array, T->n);
17625 		T->min = T->array[0];	T->max = T->array[T->n-1];
17626 		if (T->n > 1) {	/* Got at least min/max */
17627 			if (!T->var_inc) {	/* Just did an alternate way to set an equidistant array */
17628 				T->inc = T->array[1] - T->array[0];
17629 				T->set = 3;
17630 			}
17631 			else
17632 				T->set = 2;
17633 		}
17634 		else	/* Got a single point only */
17635 			T->set = 1;
17636 		return GMT_NOERROR;
17637 	}
17638 
17639 	if (min != NULL)	/* Update min now */
17640 		T->min = *min;
17641 	if (max != NULL)	/* Update max now */
17642 		T->max = *max;
17643 	if (T->count)	/* This means we gave a count instead of increment  */
17644 		inc = (T->max - T->min) / (T->inc - 1.0);
17645 	else if (T->reciprocal)	/* This means we gave the reciprocal increment  */
17646 		inc = 1.0 / T->inc;
17647 
17648 	t0 = T->min;	t1 = T->max;
17649 	if (T->temporal && GMT->current.setting.time_system.unit != T->unit) {	/* Dealing with calendar time and must update time unit */
17650 		GMT->current.setting.time_system.unit = T->unit;
17651 		(void) gmt_init_time_system_structure (GMT, &GMT->current.setting.time_system);
17652 		scale = GMT->current.setting.time_system.scale / scale;
17653 		t0 /= scale;	t1 /= scale;
17654 	}
17655 	if (T->set == 2) return (GMT_NOERROR);	/* Probably makecpt giving just a range */
17656 
17657 	if (doubleAlmostEqualZero (t0, t1) && gmt_M_is_zero (T->inc)) {	/* Got a single item for our "array" */
17658 		T->array = gmt_M_memory (GMT, NULL, 1, double);
17659 		T->array[0] = t0;
17660 		T->n = 1;
17661 	}
17662 	else if (T->vartime)	/* Must call special function that knows about variable months and years */
17663 		T->n = gmtsupport_time_array (GMT, t0, t1, inc, GMT->current.setting.time_system.unit, false, &(T->array));
17664 	else if (T->logarithmic)	/* Must call special function that deals with logarithmic arrays */
17665 		T->n = gmtlib_log_array (GMT, t0, t1, inc, &(T->array));
17666 	else if (T->logarithmic2)	/* Must call special function that deals with logarithmic arrays */
17667 		T->n = gmtlib_log2_array (GMT, t0, t1, inc, &(T->array));
17668 	else {	/* Equidistant intervals are straightforward - make sure the min/max/inc values harmonize and watch out for decreasing time */
17669 		unsigned int nt;
17670 		double range = t1 - t0, tmp_t0, tmp_t1;
17671 		if (T->exact_inc) {	/* Must enforce that max-min is a multiple of inc and adjust max if it is not */
17672 			double new = rint (range / inc) * inc;
17673 			if (!doubleAlmostEqualZero (new, range)) {	/* Must adjust t1 to match proper range */
17674 				t1 = t0 + new;
17675 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Range (max - min) is not a whole multiple of inc. Adjusted max to %g\n", option, t1);
17676 			}
17677 		}
17678 		else if (T->round) {	/* Must enforce an increment that fits the given range */
17679 			double new;
17680 			nt = urint (range / inc);
17681 			new = nt * inc;
17682 			if (nt && !doubleAlmostEqualZero (new, range)) {	/* Must adjust inc to match proper range */
17683 				inc = range / nt;
17684 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Range (max - min) is not a whole multiple of inc. Adjusted inc to %g\n", option, inc);
17685 			}
17686 		}
17687 		tmp_t0 = t0;	tmp_t1 = t1;
17688 		if (T->reverse) gmt_M_double_swap (tmp_t0, tmp_t1);	/* Needed to trick gmt_minmaxinc_verify which was developed for w/e/s/n checks where we cannot go reverse */
17689 		switch (gmt_minmaxinc_verify (GMT, tmp_t0, tmp_t1, inc, GMT_CONV8_LIMIT)) {
17690 			case 1:	/* Must trim max to give a range in multiple of inc */
17691 				nt = floor ((tmp_t1 - tmp_t0) / inc);
17692 				tmp_t1 = tmp_t0 + inc * nt;
17693 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Information from -%c option: (max - min) is not a whole multiple of inc. Adjusted max to %g\n", option, tmp_t1);
17694 				break;
17695 			case 2:
17696 				if (inc != 1.0 && gmt_M_is_zero (t0) && gmt_M_is_zero (t1)) {	/* Allow for somebody explicitly saying -T0/0/1, otherwise an error */
17697 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: (max - min) is <= 0\n", option);
17698 					return (GMT_PARSE_ERROR);
17699 				}
17700 				break;
17701 			case 3:
17702 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: inc is <= 0\n", option);
17703 				return (GMT_PARSE_ERROR);
17704 				break;
17705 			default:	/* OK as is */
17706 				break;
17707 		}
17708 		T->n = gmt_make_equidistant_array (GMT, tmp_t0, tmp_t1, T->reverse ? -inc : inc, &(T->array));
17709 	}
17710 	if (T->vartime && GMT->current.setting.time_system.unit != unit) {
17711 		uint64_t k;
17712 		/* Restore to original TIME_UNIT unit and update val array to have same units */
17713 		//scale = GMT->current.setting.time_system.scale / scale;
17714 		GMT->current.setting.time_system.unit = unit;
17715 		(void) gmt_init_time_system_structure (GMT, &GMT->current.setting.time_system);
17716 		for (k = 0; k < T->n; k++) T->array[k] *= scale;
17717 	}
17718 	if (T->n == 0) {
17719 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Your min/max/inc arguments resulted in no items\n", option);
17720 		return (GMT_PARSE_ERROR);
17721 	}
17722 	T->var_inc = gmtlib_var_inc (T->array, T->n);
17723 	return (GMT_NOERROR);
17724 }
17725 
gmt_free_array(struct GMT_CTRL * GMT,struct GMT_ARRAY * T)17726 void gmt_free_array (struct GMT_CTRL *GMT, struct GMT_ARRAY *T) {
17727 	/* Free anything that was allocated during parsing and creating a 1D array */
17728 	gmt_M_str_free (T->file);
17729 	gmt_M_str_free (T->list);
17730 	if (T->array) gmt_M_free (GMT, T->array);
17731 }
17732 
gmt_no_pstext_input(struct GMTAPI_CTRL * API,char * arg)17733 bool gmt_no_pstext_input (struct GMTAPI_CTRL *API, char *arg) {
17734 	char *c = NULL;
17735 	gmt_M_unused (API);
17736 	/* Determine if -F is such that there is nothing to read */
17737 	if (strstr (arg, "+c") == NULL) return false;	/* Without +c there will be input */
17738 	if (strstr (arg, "+t") == NULL) return false;	/* Without +t there will be input */
17739 	if ((c = strstr (arg, "+A")) && (c[2] == '+' || c[2] == '\0')) return false;	/* With +a and no arg there must be input */
17740 	if ((c = strstr (arg, "+a")) && (c[2] == '+' || c[2] == '\0')) return false;	/* With +a and no arg there must be input */
17741 	if ((c = strstr (arg, "+j")) && (c[2] == '+' || c[2] == '\0')) return false;	/* With +j and no arg there must be input */
17742 	if ((c = strstr (arg, "+f")) && (c[2] == '+' || c[2] == '\0')) return false;	/* With +f and no arg there must be input */
17743 	return true;
17744 }
17745 
gmt_filename_set(char * name)17746 void gmt_filename_set (char *name) {
17747 	/* Replace spaces with GMT_ASCII_RS */
17748 	gmt_strrepc (name, ' ', GMT_ASCII_RS);
17749 }
17750 
gmt_filename_get(char * name)17751 void gmt_filename_get (char *name) {
17752 	/* Replace GMT_ASCII_RS with spaces */
17753 	gmt_strrepc (name, GMT_ASCII_RS, ' ');
17754 }
17755 
gmt_check_executable(struct GMT_CTRL * GMT,char * program,char * arg,char * pattern,char * text)17756 bool gmt_check_executable (struct GMT_CTRL *GMT, char *program, char *arg, char *pattern, char *text) {
17757 	/* Determine if a program exists by calling program with arg via popen.  If popen is successful
17758 	 * and pattern != NULL we check that the first line read from popen contains the pattern.
17759 	 * If text != NULL then we return what popen read as first line. If successful test then
17760 	 * we return true, else false */
17761 	char cmd[PATH_MAX] = {""}, line[GMT_LEN256] = {""};
17762 	FILE *fp = NULL;
17763 	bool answer = false;
17764 
17765 	/* Turn off any stderr messages coming to the terminal */
17766 	if (strchr (program, ' ')) {	/* Command has spaces [most likely under Windows] */
17767 		if (!(program[0] == '\'' || program[0] == '\"'))	/* Not in quotes, place double quotes */
17768 			snprintf (cmd, PATH_MAX, "\"%s\"", program);
17769 		else	/* Already has quotes, but these might be double or single */
17770 			strncpy (cmd, program, PATH_MAX-1);
17771 		if (program[0] == '\'')	/* Replace single quotes with double quotes*/
17772 			gmt_strrepc (cmd, '\'', '\"');
17773 	}
17774 	else	/* No spaces, just copy */
17775 		strncpy (cmd, program, PATH_MAX-1);
17776 	if (arg) {	/* Append the command argument */
17777 		strcat (cmd, " ");
17778 		strncat (cmd, arg, PATH_MAX-1);
17779 	}
17780 	/* Finally, append redirection of errors */
17781 #ifdef WIN32
17782 	strcat (cmd, " 2> NUL");
17783 #else
17784 	strcat (cmd, " 2> /dev/null");
17785 #endif
17786 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_check_executable: Pass to popen: [%s]\n", cmd);
17787 
17788 	if ((fp = popen (cmd, "r")))	/* There was such a command */
17789 		gmt_fgets (GMT, line, GMT_LEN256, fp);	/* Read first line */
17790 	if (fp == NULL || line[0] == '\0' || (pattern && strstr (line, pattern) == NULL)) {
17791 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s failed\n", cmd);
17792 	}
17793 	else {	/* Get here if we passed the test */
17794 		if (text) strcpy (text, line);	/* Want to return the first line */
17795 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s was successful\n", cmd);
17796 		answer = true;
17797 	}
17798 	if (fp) pclose (fp);
17799 	if (text) gmt_chop (text);	/* Get rid of newline */
17800 	return (answer);
17801 }
17802 
gmt_extend_region(struct GMT_CTRL * GMT,double wesn[],unsigned int mode,double inc[])17803 void gmt_extend_region (struct GMT_CTRL *GMT, double wesn[], unsigned int mode, double inc[]) {
17804 	/* Extend the region outward according to mode */
17805 	gmt_M_unused (GMT);
17806 	if (mode == 0) return;	/* Do nothing */
17807 	if (mode == GMT_REGION_ADD) {	/* Extend the region by increments */
17808 		wesn[XLO] -= inc[XLO];
17809 		wesn[YLO] -= inc[YLO];
17810 		wesn[XHI] += inc[XHI];
17811 		wesn[YHI] += inc[YHI];
17812 	}
17813 	else {	/* Make region be in multiples of increments, possibly with adjustment */
17814 		double adjust = (mode == GMT_REGION_ROUND_EXTEND) ? GMT_REGION_INCFACTOR : 0.0;
17815 		wesn[XLO] = floor ((wesn[XLO] - adjust * inc[XLO]) / inc[XLO]) * inc[XLO];
17816 		wesn[YLO] = floor ((wesn[YLO] - adjust * inc[YLO]) / inc[YLO]) * inc[YLO];
17817 		wesn[XHI] = ceil  ((wesn[XHI] + adjust * inc[XHI]) / inc[XHI]) * inc[XHI];
17818 		wesn[YHI] = ceil  ((wesn[YHI] + adjust * inc[YHI]) / inc[YHI]) * inc[YHI];
17819 	}
17820 }
17821 
gmt_get_contours_from_table(struct GMT_CTRL * GMT,char * file,bool inner_tics,unsigned int * type,unsigned int * n_contours)17822 struct GMT_CONTOUR_INFO * gmt_get_contours_from_table (struct GMT_CTRL *GMT, char *file, bool inner_tics, unsigned int *type, unsigned int *n_contours) {
17823 	/* Read contour info from file with cval [angle] C|A|c|a [pen]] records.
17824 	 * The deprecated format was cval C|A|c|a [angle [pen]].
17825 	 * If fix-angle annotations are specified we must pass out the type flag as well.
17826 	 */
17827 	bool got_angle;
17828 	char pen[GMT_LEN64] = {""}, txt[GMT_LEN64] = {""};
17829 	unsigned int seg, row, c, save_coltype = gmt_get_column_type (GMT, GMT_IN, GMT_X);
17830 	int nc;
17831 	struct GMT_DATASET *C = NULL;
17832 	struct GMT_DATASEGMENT *S = NULL;
17833 	struct GMT_CONTOUR_INFO * cont = NULL;
17834 
17835 	gmt_set_column_type (GMT, GMT_IN, GMT_X, GMT_IS_FLOAT);	/* Since x is likely longitude we must avoid 360 wrapping here */
17836 
17837 	if ((C = GMT_Read_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_IO_ASCII, NULL, file, NULL)) == NULL) {
17838 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to read contour information file %s - aborting\n", file);
17839 		return (NULL);
17840 	}
17841 	if (C->n_records == 0) {
17842 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "No records found in contour information file %s - aborting\n", file);
17843 		return (NULL);
17844 	}
17845 
17846 	got_angle = (C->n_columns == 2);	/* We either know now (because we got two numerical columns), or we may find out later */
17847 	cont = gmt_M_memory (GMT, NULL, C->n_records, struct GMT_CONTOUR_INFO);
17848 	for (seg = c = 0; seg < C->n_segments; seg++) {
17849 		S = C->table[0]->segment[seg];
17850 		for (row = 0; row < S->n_rows; row++, c++) {
17851 			cont[c].val = S->data[GMT_X][row];
17852 			cont[c].angle = GMT->session.d_NaN;	/* May be overridden below */
17853 			pen[0] = txt[0] = '\0';
17854 			nc = sscanf (S->text[row], "%c %s %s", &cont[c].type, txt, pen);
17855 			if (strchr ("AaCc", cont[c].type) == NULL) {
17856 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Not a recognized contour type: %c\n", cont[c].type);
17857 				gmt_M_free (GMT, cont);
17858 				return (NULL);
17859 			}
17860 			if (S->n_columns == 2) {	/* Both value and angle given as part of new syntax */
17861 				cont[c].angle = S->data[GMT_Y][row];
17862 				if (nc == 2) strcpy (pen, txt);	/* Since trailing text here was <type> <pen> */
17863 			}
17864 			else if (nc == 3) {	/* Deprecated format with <type> <angle> <pen> */
17865 				cont[c].angle = atof (txt);
17866 				got_angle = true;
17867 			}
17868 			else if (nc == 2) {	/* Trailing text is either <type> <angle> or <type> <pen> */
17869 				if (gmtsupport_is_pen (GMT, txt))	/* Definitively a pen */
17870 					strcpy (pen, txt);
17871 				else {	/* Could still be a pen if no unit appended */
17872 					cont[c].angle = atof (txt);
17873 					got_angle = true;
17874 					if (cont[c].angle > 0.0 && cont[c].angle < 5.0)
17875 						GMT_Report (GMT->parent, GMT_MSG_WARNING, "Cannot tell if %s is a pen or angle; chose angle. Please use trailing c|i|p units for all pens\n", txt);
17876 				}
17877 			}
17878 			/* else nc == 1 for record with only type information (no angle nor pen) */
17879 			if (pen[0]) {	/* Got a pen */
17880 				if (gmt_getpen (GMT, pen, &cont[c].pen)) {	/* Bad pen syntax */
17881 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to parse %s as a proper pen specification - aborting\n", pen);
17882 					gmt_pen_syntax (GMT, 'C', NULL, " ", NULL, 0);
17883 					gmt_M_free (GMT, cont);
17884 					return (NULL);
17885 				}
17886 				cont[c].penset = true;
17887 			}
17888 			cont[c].do_tick = (inner_tics && (cont[c].type == 'C' || cont[c].type == 'A')) ? 1 : 0;
17889 			if (got_angle) *type = 2;	/* Must set this directly if angles are provided */
17890 		}
17891 	}
17892 	gmt_set_column_type (GMT, GMT_IN, GMT_X, save_coltype);
17893 
17894 	/* Return information structure array back to the calling environment */
17895 
17896 	*n_contours = C->n_records;
17897 	return (cont);
17898 }
17899 
gmt_is_barcolumn(struct GMT_CTRL * GMT,struct GMT_SYMBOL * S)17900 bool gmt_is_barcolumn (struct GMT_CTRL *GMT, struct GMT_SYMBOL *S) {
17901 	/* Return true if this is a vertical, horizontal bar or a column */
17902 	gmt_M_unused (GMT);
17903 	if (S->symbol == GMT_SYMBOL_BARX) return true;
17904 	if (S->symbol == GMT_SYMBOL_BARY) return true;
17905 	if (S->symbol == GMT_SYMBOL_COLUMN) return true;
17906 	return false;
17907 }
17908 
gmt_get_columbar_bands(struct GMT_CTRL * GMT,struct GMT_SYMBOL * S)17909 unsigned int gmt_get_columbar_bands (struct GMT_CTRL *GMT, struct GMT_SYMBOL *S) {
17910 	/* Report how many bands in the 3-D column */
17911 	unsigned int n_z = S->n_required;	/* z normally not counted unless +z|Z was used, so this could be 0 */
17912 	gmt_M_unused (GMT);
17913 	if ((S->base_set & GMT_BASE_READ) && n_z) n_z--;	/* Remove the base column item */
17914 	if (n_z == 0) n_z = 1;	/* 1 means single band column */
17915 	return (n_z);
17916 }
17917 
gmt_init_next_color(struct GMT_CTRL * GMT)17918 void gmt_init_next_color (struct GMT_CTRL *GMT) {
17919 	/*  Reset the sequential color IDs if starting a new plot or we detect overlay shift via -X -Y */
17920 	bool reset = false;
17921 	if (!GMT->common.O.active)	/* Start of a new plot means reset counters read from history to 0 0 */
17922 		reset = true;
17923 	else if (fabs (GMT->common.X.off) > 0.0 || fabs (GMT->common.Y.off) > 0.0)	/* Overlay but we are moving focus */
17924 		reset = true;
17925 	if (reset) {
17926 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reset sequential color pick IDs to 0,0\n");
17927 		GMT->current.plot.color_seq_id[0] = GMT->current.plot.color_seq_id[1] = 0;
17928 	}
17929 }
17930 
gmt_set_next_color(struct GMT_CTRL * GMT,struct GMT_PALETTE * P,unsigned int type,double rgb[])17931 void gmt_set_next_color (struct GMT_CTRL *GMT, struct GMT_PALETTE *P, unsigned int type, double rgb[]) {
17932 	/* Cycle through the colors in P and increment sequential ID and only update r,g,b but not alpha */
17933 	static char *kind[2] = {"table", "segment"};
17934 	type--;	/* So 1 and 2 becomes 0 and 1 for array indices */
17935 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Current %s sequential color pick ID = %u.\n", kind[type], GMT->current.plot.color_seq_id[type]);
17936 	gmt_M_rgb_only_copy (rgb, P->data[GMT->current.plot.color_seq_id[type]].rgb_low);
17937 	GMT->current.plot.color_seq_id[type] = (GMT->current.plot.color_seq_id[type] + 1) % P->n_colors;
17938 }
17939 
17940 #if 0	/* Probably not needed after all */
17941 char * gmt_add_options (struct GMT_CTRL *GMT, const char *list) {
17942 	/* Build option string that needs to be passed to GMT_Call_Module */
17943 	static char opts[GMT_BUFSIZ] = {""}, string[4] = {" - "};
17944 	size_t k;
17945 	opts[0] = '\0';
17946 	if (list == NULL) return (opts);
17947 	for (k = 0; k < strlen (list); k++) {
17948 		string[2] = list[k];
17949 		strncat (opts, string, 3U);
17950 		switch (list[k]) {
17951 			case 'a': strcat (opts, GMT->common.a.string); break;
17952 			case 'b': strcat (opts, GMT->common.b.string); break;
17953 			case 'd': strcat (opts, GMT->common.d.string); break;
17954 			case 'f': strcat (opts, GMT->common.f.string); break;
17955 			case 'g': strcat (opts, GMT->common.g.string); break;
17956 			case 'h': strcat (opts, GMT->common.h.string); break;
17957 			case 'i': strcat (opts, GMT->common.i.string); break;
17958 			case 'n': strcat (opts, GMT->common.n.string); break;
17959 			case 's': strcat (opts, GMT->common.s.string); break;
17960 			case 'V': string[0] = gmt_set_V (GMT->current.setting.verbose);
17961 				strncat (opts, string, 1U); break;
17962 			default:
17963 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unrecognized option %c\n", list[k]);
17964 				return NULL;
17965 		}
17966 	}
17967 	return (opts);
17968 }
17969 #endif
17970 
17971 /*! . */
gmtsupport_sort_moduleinfo(const void * p_1,const void * p_2)17972 GMT_LOCAL int gmtsupport_sort_moduleinfo (const void *p_1, const void *p_2) {
17973 	const struct GMT_MODULEINFO *point_1 = (const struct GMT_MODULEINFO *)p_1, *point_2 = (const struct GMT_MODULEINFO *)p_2;
17974 	int res = strcmp (point_1->mname, point_2->mname);
17975 	if (res < 0) return -1;
17976 	if (res > 0) return +1;
17977 	return 0;
17978 }
17979 
gmt_write_glue_function(struct GMTAPI_CTRL * API,char * library)17980 int gmt_write_glue_function (struct GMTAPI_CTRL *API, char* library) {
17981 	/* Called when we get gmt --new-glue=library is run, e.g.,
17982 	 * 	gmt --new-glue=mbsystem > gmt_mbsystem_glue.c
17983 	 */
17984 
17985 	char **C = NULL, *lib_purpose = NULL;
17986 	char line[BUFSIZ] = {""}, argument[GMT_LEN256] = {""};
17987 	bool first, first_purpose = true;
17988 	int error = GMT_NOERROR, k = 0, n_alloc = 0, n = -1;	/* Advance to 0 for first item */
17989 	FILE *fp = NULL;
17990 	struct GMT_MODULEINFO *M = NULL;
17991 
17992 	if ((C = gmtlib_get_dir_list (API->GMT, ".", ".c")) == NULL) {
17993 		GMT_Report (API, GMT_MSG_ERROR, "No C files found in current directory\n");
17994 		return GMT_RUNTIME_ERROR;
17995 	}
17996 	while (C[k]) {	/* A NULL marks the end of files */
17997 		if ((fp = fopen (C[k], "r")) == NULL) {
17998 			GMT_Report (API, GMT_MSG_ERROR, "Unable to open file %s for reading - permission problem?\n", C[k]);
17999 			error = GMT_RUNTIME_ERROR;
18000 			goto CROAK;
18001 		}
18002 		first = true;	/* Reset for each new C file */
18003 		while (fgets (line, BUFSIZ, fp)) {	/* This leaves the trailing linefeed intact */
18004 			if (strncmp (line, "#define THIS_MODULE_", 20U)) continue;	/* Not found our lines yet */
18005 			if (first) {	/* First time we passed the above if-test */
18006 				n++, first = false;
18007 			}
18008 			if (n >= n_alloc) {	/* Need to allocate more memory */
18009 				n_alloc += 50;
18010 				M = gmt_M_memory (API->GMT, M, n_alloc, struct GMT_MODULEINFO);
18011 			}
18012 			/* Here we know we are looking at one of the preprocessor directives of interest */
18013 			sscanf (line, "%*s %*s %[^\n]\n", argument);	/* Extract the argument */
18014 			if (!strncmp (line, "#define THIS_MODULE_MODERN_NAME", 31U))
18015 				M[n].mname = strdup (argument);
18016 			else if (!strncmp (line, "#define THIS_MODULE_CLASSIC_NAME", 32U))
18017 				M[n].cname = strdup (argument);
18018 			else if (!strncmp (line, "#define THIS_MODULE_NAME", 24U)) {
18019 				M[n].mname = strdup (argument);
18020 				M[n].cname = strdup (argument);
18021 			}
18022 			else if (!strncmp (line, "#define THIS_MODULE_LIB", 23U))
18023 				M[n].component = strdup (argument);
18024 			else if (!strncmp (line, "#define THIS_MODULE_PURPOSE", 27U))
18025 				M[n].purpose = strdup (argument);
18026 			else if (!strncmp (line, "#define THIS_MODULE_KEYS", 24U))
18027 				M[n].keys = strdup (argument);
18028 			else if (!strncmp (line, "#define THIS_MODULE_LIB_PURPOSE", 31U) && first_purpose) {
18029 				lib_purpose = strdup (argument);
18030 				first_purpose = false;
18031 			}
18032 		}
18033 		if (M[n].mname == NULL && M[n].cname == NULL && M[n].component == NULL && M[n].purpose == NULL && M[n].keys == NULL) { /* Not a module file */
18034 			n--;	/* Counteract the n++ that will happen in the next file */
18035 			GMT_Report (API, GMT_MSG_WARNING, "File %s had incomplete set of #define THIS_MODULE_* parameters; file skipped.\n", C[k]);
18036 		}
18037 		fclose (fp);
18038 		k++;	/* Go to next file */
18039 	}
18040 
18041 	if (n == -1) {
18042 		GMT_Report (API, GMT_MSG_ERROR, "No module files found in current directory\n");
18043 		error = GMT_RUNTIME_ERROR;
18044 		goto CROAK;
18045 	}
18046 
18047 	n++;
18048 	GMT_Report (API, GMT_MSG_INFORMATION, "%d %s module files found in current directory\n", n, library);
18049 
18050 	if (first_purpose) {
18051 		GMT_Report (API, GMT_MSG_WARNING, "No #define THIS_MODULE_LIB_PURPOSE setting found in any module.  Please edit argument in gmtlib_%s_show_all\n", library);
18052 		sprintf (line, "GMT %s: The third-party supplements to the Generic Mapping Tools", library);
18053 		lib_purpose = strdup (line);
18054 		GMT_Report (API, GMT_MSG_WARNING, "Default purpose assigned: %s\n", lib_purpose);
18055 	}
18056 
18057 	qsort (M, n, sizeof (struct GMT_MODULEINFO), gmtsupport_sort_moduleinfo);
18058 
18059 	printf ("/*\n * Copyright (c) 2012-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)\n");
18060 	printf (" * See LICENSE.TXT file for copying and redistribution conditions.\n */\n");
18061 	printf ("/* gmt_%s_glue.c populates the external array of this shared lib with\n", library);
18062 	printf (" * module parameters such as name, group, purpose and keys strings.\n");
18063 	printf (" * This file also contains the following convenience functions to\n");
18064 	printf (" * display all module purposes, list their names, or return keys or group:\n *\n");
18065 	printf (" *   int %s_module_show_all    (void *API);\n", library);
18066 	printf (" *   int %s_module_list_all    (void *API);\n", library);
18067 	printf (" *   int %s_module_classic_all (void *API);\n *\n", library);
18068 	printf (" * These functions may be called by gmt --help and gmt --show-modules\n *\n");
18069 	printf (" * Developers of external APIs for accessing GMT modules will use this\n");
18070 	printf (" * function indirectly via GMT_Encode_Options to retrieve option keys\n");
18071 	printf (" * needed for module arg processing:\n *\n");
18072 	printf (" *   const char * %s_module_keys  (void *API, char *candidate);\n", library);
18073 	printf (" *   const char * %s_module_group (void *API, char *candidate);\n *\n", library);
18074 	printf (" * All functions are exported by the shared %s library so that gmt can call these\n", library);
18075 	printf (" * functions by name to learn about the contents of the library.\n */\n\n");
18076 	printf ("#include \"gmt_dev.h\"\n\n");
18077 	printf ("/* Sorted array with information for all GMT %s modules */\n", library);
18078 	printf ("static struct GMT_MODULEINFO modules[] = {\n");
18079 	for (k = 0; k < n; k++)
18080 		printf ("\t{%s, %s, %s, %s, %s},\n", M[k].mname, M[k].cname, M[k].component, M[k].purpose, M[k].keys);
18081 	printf ("\t{NULL, NULL, NULL, NULL, NULL} /* last element == NULL detects end of array */\n");
18082 	printf ("};\n\n");
18083 	printf ("/* Pretty print all shared module names and their purposes for gmt --help */\n");
18084 	printf ("EXTERN_MSC int %s_module_show_all (void *API) {\n", library);
18085 	printf ("\treturn (GMT_Show_ModuleInfo (API, modules, \"%s\", GMT_MODULE_HELP));\n}\n\n", lib_purpose);
18086 	printf ("/* Produce single list on stdout of all shared module names for gmt --show-modules */\n");
18087 	printf ("EXTERN_MSC int %s_module_list_all (void *API) {\n", library);
18088 	printf ("\treturn (GMT_Show_ModuleInfo (API, modules, NULL, GMT_MODULE_SHOW_MODERN));\n}\n\n");
18089 	printf ("/* Produce single list on stdout of all shared module names for gmt --show-classic [i.e., classic mode names] */\n");
18090 	printf ("EXTERN_MSC int %s_module_classic_all (void *API) {\n", library);
18091 	printf ("\treturn (GMT_Show_ModuleInfo (API, modules, NULL, GMT_MODULE_SHOW_CLASSIC));\n}\n\n");
18092 	printf ("/* Lookup module id by name, return option keys pointer (for external API developers) */\n");
18093 	printf ("EXTERN_MSC const char *%s_module_keys (void *API, char *candidate) {\n", library);
18094 	printf ("\treturn (GMT_Get_ModuleInfo (API, modules, candidate, GMT_MODULE_KEYS));\n}\n\n");
18095 	printf ("/* Lookup module id by name, return group char name (for external API developers) */\n");
18096 	printf ("EXTERN_MSC const char *%s_module_group (void *API, char *candidate) {\n", library);
18097 	printf ("\treturn (GMT_Get_ModuleInfo (API, modules, candidate, GMT_MODULE_GROUP));\n}\n");
18098 
18099 CROAK:	/* We are done or premature return due to error */
18100 
18101 	gmtlib_free_dir_list (API->GMT, &C);
18102 	for (k = 0; k < n; k++) {
18103 		gmt_M_str_free (M[k].mname);
18104 		gmt_M_str_free (M[k].cname);
18105 		gmt_M_str_free (M[k].component);
18106 		gmt_M_str_free (M[k].purpose);
18107 		gmt_M_str_free (M[k].keys);
18108 	}
18109 	gmt_M_free (API->GMT, M);
18110 	gmt_M_str_free (lib_purpose);
18111 
18112 	return (error);
18113 }
18114 
gmt_cpt_interval_modifier(struct GMT_CTRL * GMT,char ** arg,double * interval)18115 void gmt_cpt_interval_modifier (struct GMT_CTRL *GMT, char **arg, double *interval) {
18116 	/* CPT files in some programs (grd2kml, grdimage, grdvector, grdview) may have a +i<dz> modifier,
18117 	 * but it may be just one of several valid CPT modifiers.  Here, we wish to remove this
18118 	 * modifier, set the corresponding interval, and update *file to only have the
18119 	 * remaining text items. */
18120 	char *file = NULL, *c = NULL, *f = NULL, new_arg[PATH_MAX] = {""};
18121 	gmt_M_unused (GMT);
18122 	if (arg == NULL || (file = *arg) == NULL || file[0] == '\0') return;	/* NULL argument */
18123 	if ((f = gmt_strrstr (file, GMT_CPT_EXTENSION)))	/* Filename has .cpt extension, look behind it */
18124 		c = gmtlib_last_valid_file_modifier (GMT->parent, f, GMT_CPTFILE_MODIFIERS);
18125 	else	/* Must search the entire filename from the back */
18126 		c = gmtlib_last_valid_file_modifier (GMT->parent, file, GMT_CPTFILE_MODIFIERS);
18127 	if (c == NULL) return;	/* No modifiers present in the filename */
18128 	if ((f = strstr (c, "+i")) == NULL) return;	/* Modifiers are present, but not the +i setting */
18129 	/* Here we have a +i<dz> string in section pointed to by f */
18130 	if (f[2] == '\0' || !(f[2] == '.' || isdigit (f[2]))) {	/* Failed basic sanity checking */
18131 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "CPT filename has +i appended [%s] but sets no valid interval\n", f);
18132 		return;	/* Simply not acting on the bad argument */
18133 	}
18134 	*interval = atof (&f[2]);
18135 	f[0] = '\0';	f++;	/* Chop off and move one char to the right */
18136 	strncpy (new_arg, file, PATH_MAX-1);        /* Everything up to start of +i */
18137 	while (*f && *f != '+') f++;                /* Wind to next modifier or reach end of string */
18138 	if (*f) strncat (new_arg, f, PATH_MAX-1);   /* Append other modifiers given after +i */
18139 	gmt_M_str_free (*arg);
18140 	*arg = strdup (new_arg);
18141 }
18142 
18143 /* This set of 14 functions are used by both movie.c and batch.c to deal with understanding
18144  * input script types and to write shell commands in various syntax variants.
18145  */
18146 
gmt_sleep(unsigned int microsec)18147 int gmt_sleep (unsigned int microsec) {
18148 	/* Waiting before checking if the completion file has been generated */
18149 #ifdef WIN32
18150 	Sleep ((uint32_t)microsec/1000);	/* msec are microseconds but Sleep wants milliseconds */
18151 	return 0;
18152 #else
18153 	return (usleep ((useconds_t)microsec));
18154 #endif
18155 }
18156 
gmt_set_value(struct GMT_CTRL * GMT,FILE * fp,int mode,int col,char * name,double value)18157 void gmt_set_value (struct GMT_CTRL *GMT, FILE *fp, int mode, int col, char *name, double value) {
18158 	/* Assigns a single named data floating point variable given the script mode
18159 	 * Here, col indicates which input column in case special formatting is implied via -f */
18160 	char string[GMT_LEN64] = {""};
18161 	gmt_ascii_format_one (GMT, string, value, gmt_M_type (GMT, GMT_IN, col));
18162 	switch (mode) {
18163 		case GMT_BASH_MODE: fprintf (fp, "%s=%s", name, string);       break;
18164 		case GMT_CSH_MODE:  fprintf (fp, "set %s = %s", name, string); break;
18165 		case GMT_DOS_MODE:  fprintf (fp, "set %s=%s", name, string);   break;
18166 	}
18167 	fprintf (fp, "\n");
18168 }
18169 
gmt_set_ivalue(FILE * fp,int mode,bool env,char * name,int value)18170 void gmt_set_ivalue (FILE *fp, int mode, bool env, char *name, int value) {
18171 	/* Assigns a single named integer variable given the script mode */
18172 	switch (mode) {
18173 		case GMT_BASH_MODE: fprintf (fp, "%s=%d\n", name, value);       break;
18174 		case GMT_CSH_MODE:  if (env)
18175 					fprintf (fp, "%s %d\n", name, value);
18176 				else
18177 					fprintf (fp, "set %s = %d\n", name, value);
18178 				break;
18179 		case GMT_DOS_MODE:  fprintf (fp, "set %s=%d\n", name, value);   break;
18180 	}
18181 }
18182 
gmt_set_dvalue(FILE * fp,int mode,char * name,double value,char unit)18183 void gmt_set_dvalue (FILE *fp, int mode, char *name, double value, char unit) {
18184 	/* Assigns a single named Cartesian floating point variable given the script mode */
18185 	switch (mode) {
18186 		case GMT_BASH_MODE: fprintf (fp, "%s=%.12g", name, value);       break;
18187 		case GMT_CSH_MODE:  fprintf (fp, "set %s = %.12g", name, value); break;
18188 		case GMT_DOS_MODE:  fprintf (fp, "set %s=%.12g", name, value);   break;
18189 	}
18190 	if (unit) fprintf (fp, "%c", unit);	/* Append the unit [c|i|p] unless 0 */
18191 	fprintf (fp, "\n");
18192 }
18193 
gmt_set_tvalue(FILE * fp,int mode,bool env,char * name,char * value)18194 void gmt_set_tvalue (FILE *fp, int mode, bool env, char *name, char *value) {
18195 	/* Assigns a single named text variable given the script mode */
18196 	if (strchr (value, ' ') || strchr (value, '\t') || strchr (value, '|')) {	/* String has spaces, tabs, or bar */
18197 		switch (mode) {
18198 			case GMT_BASH_MODE: fprintf (fp, "%s=\"%s\"\n", name, value);       break;
18199 			case GMT_CSH_MODE:  if (env)
18200 						fprintf (fp, "%s \"%s\"\n", name, value);
18201 					else
18202 						fprintf (fp, "set %s = \"%s\"\n", name, value);
18203 					break;
18204 			case GMT_DOS_MODE:  fprintf (fp, "set %s=\"%s\"\n", name, value);   break;
18205 		}
18206 	}
18207 	else {	/* Single word */
18208 		switch (mode) {
18209 			case GMT_BASH_MODE: fprintf (fp, "%s=%s\n", name, value);       break;
18210 			case GMT_CSH_MODE:  if (env)
18211 						fprintf (fp, "%s %s\n", name, value);
18212 					else
18213 						fprintf (fp, "set %s = %s\n", name, value);
18214 					break;
18215 			case GMT_DOS_MODE:  fprintf (fp, "set %s=%s\n", name, value);   break;
18216 		}
18217 	}
18218 }
18219 
gmt_set_script(FILE * fp,int mode)18220 void gmt_set_script (FILE *fp, int mode) {
18221 	/* Writes the script's incantation line (or a comment for DOS, turning off default echo) */
18222 	switch (mode) {
18223 		case GMT_BASH_MODE: fprintf (fp, "#!/usr/bin/env bash\n"); break;
18224 		case GMT_CSH_MODE:  fprintf (fp, "#!/usr/bin/env csh\n"); break;
18225 		case GMT_DOS_MODE:  fprintf (fp, "@echo off\nREM Start of script\n"); break;
18226 	}
18227 }
18228 
gmt_set_comment(FILE * fp,int mode,char * comment)18229 void gmt_set_comment (FILE *fp, int mode, char *comment) {
18230 	/* Write a comment line given the script mode */
18231 	switch (mode) {
18232 		case GMT_BASH_MODE: case GMT_CSH_MODE:  fprintf (fp, "# %s\n", comment); break;
18233 		case GMT_DOS_MODE:  fprintf (fp, "REM %s\n", comment); break;
18234 	}
18235 }
18236 
gmt_get_strwithtab(const char * txt)18237 char *gmt_get_strwithtab (const char *txt) {
18238 	/* Replace any literal "\t" strings with the tab character */
18239 	char dummy[GMT_LEN128] = {""};
18240 	if (!strcmp (txt, "\\t")) {
18241 		char new[2] = {'\t', 0};	/* The tab character in a string */
18242 		strncpy (dummy, gmt_strrep (txt, "\\t", new), GMT_LEN128-1);
18243 	}
18244 	else
18245 		strncpy (dummy, txt, GMT_LEN128-1);
18246 	return strdup (dummy);
18247 }
18248 
gmt_place_var(int mode,char * name)18249 char *gmt_place_var (int mode, char *name) {
18250 	/* Prints a single variable to stdout where needed in the script via the string static variable.
18251 	 * PS!  Only one call per printf statement since static string cannot hold more than one item at the time */
18252 	static char string[GMT_LEN128] = {""};	/* So max length of variable name is 127 */
18253 	if (mode == GMT_DOS_MODE)
18254 		sprintf (string, "%%%s%%", name);
18255 	else
18256 		sprintf (string, "${%s}", name);
18257 	return (string);
18258 }
18259 
gmt_dry_run_only(const char * cmd)18260 int gmt_dry_run_only (const char *cmd) {
18261 	/* Dummy function to not actually run the loop script when -Q is used */
18262 	gmt_M_unused (cmd);
18263 	return 0;
18264 }
18265 
gmt_check_language(struct GMT_CTRL * GMT,unsigned int mode,char * file,unsigned int type,bool * PS)18266 unsigned int gmt_check_language (struct GMT_CTRL *GMT, unsigned int mode, char *file, unsigned int type, bool *PS) {
18267 	unsigned int n_errors = 0;
18268 	/* Examines file extension and compares to known mode from mainscript.
18269 	 * Here type is 0-2 for background, foreground, or title script/plot which may be either a script of PS/EPS
18270 	 * while type = 0 is script only and will be compared to the mode. */
18271 
18272 	if (PS) {	/* Only used in movie.c so far */
18273 		size_t L = strlen (file);
18274 		*PS = false;
18275 		if (type < 3 && L > 4 && (!strncmp (&file[L-3], ".ps", 3U) || !strncmp (&file[L-4], ".eps", 3U))) {
18276 			static char *layer[3] = {"background", "foreground", "title"};
18277 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "PostScript/EPS %s layer %s detected\n", layer[type], file);
18278 			*PS = true;	/* Got a PostScript or EPS file */
18279 			return GMT_NOERROR;
18280 		}
18281 	}
18282 
18283 	switch (mode) {
18284 		case GMT_BASH_MODE:
18285 			if (!(strstr (file, ".bash") || strstr (file, ".sh"))) {
18286 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Main script is bash/sh but %s is not!\n", file);
18287 				n_errors++;
18288 			}
18289 			break;
18290 		case GMT_CSH_MODE:
18291 			if (!strstr (file, ".csh")) {
18292 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Main script is csh but %s is not!\n", file);
18293 				n_errors++;
18294 			}
18295 			break;
18296 		case GMT_DOS_MODE:
18297 			if (!strstr (file, ".bat")) {
18298 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Main script is bat but %s is not!\n", file);
18299 				n_errors++;
18300 			}
18301 			break;
18302 	}
18303 	return (n_errors);
18304 }
18305 
gmt_script_is_classic(struct GMT_CTRL * GMT,FILE * fp)18306 bool gmt_script_is_classic (struct GMT_CTRL *GMT, FILE *fp) {
18307 	/* Read script to determine if it is in GMT classic mode or not, then rewind */
18308 	bool modern = false;
18309 	char line[PATH_MAX] = {""};
18310 	while (!modern && gmt_fgets (GMT, line, PATH_MAX, fp)) {
18311 		if (strstr (line, "gmt ") == NULL) continue;	/* Does not start with gmt */
18312 		if (strstr (line, " begin"))		/* A modern mode script */
18313 			modern = true;
18314 		else if (strstr (line, " figure"))	/* A modern mode script */
18315 			modern = true;
18316 		else if (strstr (line, " subplot"))	/* A modern mode script */
18317 			modern = true;
18318 		else if (strstr (line, " inset"))	/* A modern mode script */
18319 			modern = true;
18320 		else if (strstr (line, " end"))		/* A modern mode script */
18321 			modern = true;
18322 	}
18323 	rewind (fp);	/* Go back to beginning of file */
18324 	return (!modern);
18325 }
18326 
gmt_token_check(struct GMT_CTRL * GMT,FILE * fp,char * prefix,unsigned int mode)18327 int gmt_token_check (struct GMT_CTRL *GMT, FILE *fp, char *prefix, unsigned int mode) {
18328 	/* Read main script to determine any *prefix*_*** variables
18329 	 * are found with a missing %,$ token, then rewind */
18330 	int n_errors = 0, e;
18331 	char line[GMT_LEN256] = {""}, record[GMT_LEN256] = {""}, *p = NULL, *prev = NULL, *start = NULL;
18332 	static char var_token[4] = "$$%";
18333 	while (gmt_fgets (GMT, line, GMT_LEN256, fp)) {
18334 		start = line;
18335 		while (strchr (" \t", start[0])) start++;	/* Skip any leading whitespace */
18336 		if (start[0] == '\n' || start[0] == '\r')  continue;	/* Blank line */
18337 		if (mode == GMT_DOS_MODE) {	/* Some DOS-specific checks */
18338 			if (!strcmp (start, "REM") || !strcmp (start, "rem")) continue;	/* DOS comment */
18339 		}
18340 		else {	/* UNIX shells */
18341 			if (start[0] == '#') continue;	/* Shell comment */
18342 			if (mode == GMT_BASH_MODE) {	/* Some bash-specific checks */
18343 				if (strchr (line, '`'))	/* sub-shell call using back-ticks */
18344 					GMT_Report (GMT->parent, GMT_MSG_WARNING, "Main script appears to have a deprecated sub-shell call `...`, please use $(...) instead: %s", start);
18345 				else if (strchr (line, ')') && (p = strchr (line, '('))) {	/* sub-shell call without leading $ */
18346 					prev = p - 1;	/* Get previous character */
18347 					if (prev < start || prev[0] != '$')
18348 						GMT_Report (GMT->parent, GMT_MSG_WARNING, "Main script appears to have a sub-shell call $(...) without the leading $: %s", start);
18349 				}
18350 			}
18351 		}
18352 		if ((p = strstr (line, prefix))) {
18353 			/* Got a MOVIE_ or BATCH_ variable here. Check if it is missing the token */
18354 			strncpy (record, start, GMT_LEN256-1);
18355 			prev = p - 1;	/* Get previous character */
18356 			if (prev >= start && prev[0] == '{') prev--, p--;	/* Found {prefix} */
18357 			if (prev < start || prev[0] != var_token[mode]) {	/* Start of line or the previous char is not the token */
18358 				start = line;	/* Reset pointer */
18359 				e = (int)(p - start);	/* Try to find the end of the variable name to make message below clearer */
18360 				while (line[e] && strchr (" \t,/:", line[e]) == NULL) e++;
18361 				line[e] = '\0';
18362 				while (strchr (" \t", start[0])) start++;	/* Skip any leading whitespace */
18363 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Main script uses %s but missing the required leading %c: %s", p, var_token[mode], record);
18364 				n_errors++;
18365 			}
18366 			else if (mode != GMT_DOS_MODE) {
18367 				if (strchr (start, '{') && !strchr (start, '}'))
18368 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Main script missing  in variable name: %s", record), n_errors++;
18369 				else if (!strchr (start, '{') && strchr (start, '}'))
18370 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Main script missing { in variable name: %s", record), n_errors++;
18371 			}
18372 		}
18373 	}
18374 	rewind (fp);	/* Go back to beginning of file */
18375 	return (n_errors);
18376 }
18377 
gmtsupport_line_is_a_comment(char * line)18378 GMT_LOCAL bool gmtsupport_line_is_a_comment (char *line) {
18379 	unsigned int k = 0;
18380 	while (line[k] && isspace (line[k])) k++;	/* Wind past leading whitespace */
18381 	return (line[k] == '#' || !strncasecmp (&line[k], "rem", 3U)) ? true : false;	/* Will return true for lines starting with some tabs, then comment */
18382 }
18383 
gmt_is_gmtmodule(char * line,char * module)18384 bool gmt_is_gmtmodule (char *line, char *module) {
18385 	/* Robustly identify the command "gmt begin" */
18386 	char word[GMT_LEN128] = {""};
18387 	unsigned int pos = 0;
18388 	size_t L;
18389 	if (strlen (line) >= GMT_LEN128) return false;	/* Cannot be gmt begin */
18390 	/* To handle cases where there may be more than one space between gmt and module */
18391 	if (gmtsupport_line_is_a_comment (line)) return false;	/* Skip commented lines like "  # anything" */
18392 	if (gmt_strtok (line, " \t\n", &pos, word) == 0) return false;	/* Get first word in the command or fail */
18393 	if (strcmp (word, "gmt")) return false;		/* Not starting with gmt so we are done */
18394 	if (gmt_strtok (line, " \t\n", &pos, word) == 0) return false;	/* Get second word or fail */
18395 	L = strlen (module);				/* How many characters to compare against */
18396 	if (!strncmp (word, module, L)) return true;	/* Command starting with gmt <module> found */
18397 	return false;	/* Not gmt <module> */
18398 }
18399 
gmt_is_gmt_end_show(char * line)18400 bool gmt_is_gmt_end_show (char *line) {
18401 	char word[GMT_LEN128] = {""};
18402 	unsigned int pos = 0;
18403 	if (strlen (line) >= GMT_LEN128) return false;	/* Cannot be gmt end show */
18404 	/* Robustly identify the command "gmt end show" */
18405 	/* To handle cases where there may be more than one space between gmt and module */
18406 	if (gmtsupport_line_is_a_comment (line)) return false;	/* Skip commented lines like "  # anything" */
18407 	if (gmt_strtok (line, " \t\n", &pos, word) == 0) return false;	/* Get first word in the command or fail */
18408 	if (strcmp (word, "gmt")) return false;		/* Not starting with gmt so we are done */
18409 	if (gmt_strtok (line, " \t\n", &pos, word) == 0) return false;	/* Get second word or fail */
18410 	if (strcmp (word, "end")) return false;		/* Not continuing with end so we are done */
18411 	if (gmt_strtok (line, " \t\n", &pos, word) == 0) return false;	/* Get third word or fail */
18412 	if (!strcmp (word, "show")) return true;	/* Yes, found gmt end show */
18413 	return false;	/* Not gmt end show */
18414 }
18415 
gmt_found_modifier(struct GMT_CTRL * GMT,char * string,char * mods)18416 bool gmt_found_modifier (struct GMT_CTRL *GMT, char *string, char *mods) {
18417 	/* Return true if any of the modifiers listed are found in the string */
18418 	char this_modifier[3] = {'+', ' ', '\0'};
18419 	gmt_M_unused (GMT);
18420 	for (unsigned int k = 0; k < strlen (mods); k++) {
18421 		this_modifier[1] = mods[k];
18422 		if (strstr (string, this_modifier)) return (true);	/* Found it */
18423 	}
18424 	return (false);
18425 }
18426 
gmt_unpack_rgbcolors(struct GMT_CTRL * GMT,struct GMT_IMAGE * I,unsigned char rgbmap[])18427 unsigned int gmt_unpack_rgbcolors (struct GMT_CTRL *GMT, struct GMT_IMAGE *I, unsigned char rgbmap[]) {
18428 	unsigned int n, k = 0;
18429 	int red;
18430 	gmt_M_unused (GMT);
18431 	/* Repack column-stored RGBA integer values into a row-stored RGBA byte array */
18432 	for (n = 0; n < (unsigned int)I->n_indexed_colors && (red = gmt_M_get_rgba (I->colormap, n, 0, I->n_indexed_colors)) >= 0; n++) {
18433 		rgbmap[k++] = red;	/* Got this already */
18434 		rgbmap[k++] = gmt_M_get_rgba (I->colormap, n, 1, I->n_indexed_colors);
18435 		rgbmap[k++] = gmt_M_get_rgba (I->colormap, n, 2, I->n_indexed_colors);
18436 		rgbmap[k++] = gmt_M_get_rgba (I->colormap, n, 3, I->n_indexed_colors);
18437 	}
18438 	return (k/4);	/* Number of entries */
18439 }
18440 
gmt_format_region(struct GMT_CTRL * GMT,char * record,double * wesn)18441 void gmt_format_region (struct GMT_CTRL *GMT, char *record, double *wesn) {
18442 	/* Fancy ddd:mm:ssF typeset of -Rw/e/s/n if possible, and being aware of 360-range */
18443 	bool wrap = false;
18444 	enum gmt_col_enum type = gmt_get_column_type (GMT, GMT_IN, GMT_X);
18445 	char text[GMT_LEN64] = {""}, save[GMT_LEN64];
18446 	if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* Try formal reporting */
18447 		wrap = gmt_M_360_range (wesn[XLO], wesn[XHI]);
18448 		strcpy (save, GMT->current.setting.format_geo_out);
18449 		strcpy (GMT->current.setting.format_geo_out, "ddd:mm:ssF");
18450 		gmtlib_geo_C_format (GMT);
18451 	}
18452 	if (wrap)	/* Do it directly */
18453 		sprintf (record, "-R180:00:00W/180:00:00E/");
18454 	else {
18455 		gmt_ascii_format_one (GMT, text, wesn[XLO], type);
18456 		sprintf (record, "-R%s/", text);	/* west or xmin */
18457 		gmt_ascii_format_one (GMT, text, wesn[XHI], type);
18458 		strcat (record, text);	strcat (record, "/");/* east or xmax */
18459 	}
18460 	type = gmt_get_column_type (GMT, GMT_IN, GMT_Y);
18461 	gmt_ascii_format_one (GMT, text, wesn[YLO], type);
18462 	strcat (record, text);	strcat (record, "/");/* south or ymin */
18463 	gmt_ascii_format_one (GMT, text, wesn[YHI], type);
18464 	strcat (record, text);	/* north or ymax */
18465 	if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* Reset */
18466 		strcpy (GMT->current.setting.format_geo_out, save);
18467 		gmtlib_geo_C_format (GMT);
18468 	}
18469 }
18470 
gmt_get_limits(struct GMT_CTRL * GMT,char option,char * text,unsigned int mode,double * min,double * max)18471 unsigned int gmt_get_limits (struct GMT_CTRL *GMT, char option, char *text, unsigned int mode, double *min, double *max) {
18472 	/* Parse strings like low/high, NaN/high, low/NaN, /high, low/ and return min/max
18473 	 * with either set to NaN if not given.  However, if mode - 1 then unset min/max becomes
18474 	 * -DBL_MAX or +DBL_MAX instead of NaNs */
18475 	size_t L;
18476 	int n;
18477 	char txt_a[GMT_LEN512] = {""}, txt_b[GMT_LEN32] = {""};
18478 	if (!text || text[0] == '\0') {	/* Gave no argument */
18479 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Argument is required\n", option);
18480 		return GMT_PARSE_ERROR;
18481 	}
18482 	if (strchr (text, '/') == NULL) {	/* Gave no valid argument */
18483 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Dividing slash is required\n", option);
18484 		return GMT_PARSE_ERROR;
18485 	}
18486 	L = strlen (text) - 1;	/* Will be 0 or larger */
18487 	if (text[0] == '/') {	/* Gave /max with min implicitly set to NaN */
18488 		strcpy (txt_a, "NaN");
18489 		strcpy (txt_b, &text[1]);
18490 	}
18491 	else if (text[L] == '/') {	/* Gave min/ with max implicitly set to NaN */
18492 		strcpy (txt_a, text);	txt_a[L] = '\0';	/* Chop off the trailing / */
18493 		strcpy (txt_b, "NaN");
18494 	}
18495 	else if ((n = sscanf (text, "%[^/]/%s", txt_a, txt_b)) < 2) {	/* Got min/max (either could be NaN) */
18496 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Must specify min/max\n", option);
18497 		return GMT_PARSE_ERROR;
18498 	}
18499 	/* Here we got txt_a and txt_b filled */
18500 	if (strcasecmp (txt_a, "NAN") == 0)
18501 		*min = GMT->session.d_NaN;
18502 	else if (gmt_is_float (GMT, txt_a))
18503 		*min = atof (txt_a);
18504 	else {
18505 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unable to parse %s\n", option, txt_a);
18506 		return GMT_PARSE_ERROR;
18507 	}
18508 	if (strcasecmp (txt_b, "NAN") == 0)
18509 		*max = GMT->session.d_NaN;
18510 	else if (gmt_is_float (GMT, txt_b))
18511 		*max = atof (txt_b);
18512 	else {
18513 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Unable to parse %s\n", option, txt_b);
18514 		return GMT_PARSE_ERROR;
18515 	}
18516 	if (mode) {	/* Replace any NaNs with min and max doubles */
18517 		if (gmt_M_is_dnan (*min))
18518 			*min = -DBL_MAX;
18519 		if (gmt_M_is_dnan (*max))
18520 			*max = +DBL_MAX;
18521 	}
18522 	else if (gmt_M_is_dnan (*min) && gmt_M_is_dnan (*max)) {
18523 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: Both limits cannot be NaN\n", option);
18524 		return GMT_PARSE_ERROR;
18525 	}
18526 	if (!(gmt_M_is_dnan (*min) || gmt_M_is_dnan (*max)) && *max <= *min) {
18527 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -%c: min must be less than max\n", option);
18528 		return GMT_PARSE_ERROR;
18529 	}
18530 	return GMT_NOERROR;
18531 }
18532