1 /*--------------------------------------------------------------------
2  *
3  *   Copyright (c) 1999-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU Lesser General Public License as published by
7  *   the Free Software Foundation; version 3 or any later version.
8  *
9  *   This program is distributed in the hope that it will be useful,
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *   GNU Lesser General Public License for more details.
13  *
14  *   Contact info: www.soest.hawaii.edu/wessel
15  *--------------------------------------------------------------------*/
16 /*
17  * SPOTTER: functions for moving points along small circles on a sphere.
18  *
19  * Paul Wessel, University of Hawaii
20  * July 20, 2010
21  * Version 1.3 for GMT 6
22  *
23  * The user-callable functions in this library are:
24  *
25  * spotter_init             : Load stage poles from file
26  * spotter_backtrack        : Trace track from seamount to hotspot
27  * spotter_forthtrack       : Trace track from hotspot to seamount
28  * spotter_total_to_stages  : Convert finite rotations to stage poles
29  * spotter_stages_to_total  : Convert stage poles to finite rotations
30  * spotter_add_rotations    : Add to plate motion models together.
31  * spotter_conf_ellipse     : Calculate confidence ellipse for rotated point
32  *
33  * Programs must first call spotter_init() which reads a file of
34  * backward stage poles.  Given the right flag it can convert these
35  * to forward stage poles.
36  *
37  * Then to draw a hotspot track the program can:
38  *	1. Draw FROM the hotspot TO a seamount: Use spotter_forthtrack
39  *	2. Draw FROM a seamount BACK TO a hotspot: Use spotter_backtrack
40  *
41  * To draw crustal flowlines (seamounts motion over mantle) do select
42  * flowline = true when calling spotter_init and then:
43  *	1. Draw FROM a hotspot TO a seamount: Use spotter_backtrack
44  *	2. Draw FROM a seamount TO a hotspot (and beyond): Use spotter_forthtrack
45  *
46  * All coordinates herein are assumed to be GEOCENTRIC.  The main programs are
47  * respondible for converting to/from geodetic data coordinates.  Rotation pole
48  * latitudes are usually implied to be geocentric.
49  */
50 
51 #include "gmt_dev.h"
52 #include "spotter.h"
53 
54 EXTERN_MSC void gmtlib_get_point_from_r_az (struct GMT_CTRL *GMT, double lon0, double lat0, double r, double azim, double *lon1, double *lat1);
55 
56 #define SPOTTER_N_STEPS		360
57 #define SPOTTER_DEL_STEP	(TWO_PI/SPOTTER_N_STEPS)
58 #define SPOTTER_N_FINE_STEPS	36000
59 #define SPOTTER_N_GRID		100
60 #define SPOTTER_D_CUT		1.0e-6
61 
spotter_rot_usage(struct GMTAPI_CTRL * API)62 void spotter_rot_usage (struct GMTAPI_CTRL *API) {
63 	GMT_Usage (API, 1, "\n%s", SPOTTER_E_OPT);
64 	GMT_Usage (API, -2, "Specify rotation(s) to use in one of three ways:");
65 	GMT_Usage (API, 3, "%s Give a file with a series of rotations to be used (see documentation for format).", GMT_LINE_BULLET);
66 	GMT_Usage (API, 3, "%s Give a single rotation as <plon>/<plat>/<prot> (in degrees).", GMT_LINE_BULLET);
67 	GMT_Usage (API, 3, "%s Give two plate IDs separated by a hyphen (e.g., PAC-MBL) "
68 		"to extract that rotation from the GPlates rotation database.", GMT_LINE_BULLET);
69 	GMT_Usage (API, -2, "Append +i if you want to invert the finite rotation(s) prior to use.");
70 
71 }
72 
73 /* Sort functions used to order the rotations */
74 
spotter_comp_stage(const void * p_1,const void * p_2)75 int spotter_comp_stage (const void *p_1, const void *p_2) {
76 	/* Returns -1 if rotation pointed to by p_1 is older that point_2,
77 	   +1 if the reverse it true, and 0 if they are equal
78 	*/
79 	const struct EULER *point_1 = p_1, *point_2 = p_2;
80 
81 	if (point_1->t_start > point_2->t_start) return (-1);
82 	if (point_1->t_start < point_2->t_start) return (+1);
83 	return (0);
84 }
85 
spotter_comp_total(const void * p_1,const void * p_2)86 int spotter_comp_total (const void *p_1, const void *p_2) {
87 	/* Returns -1 if rotation pointed to by p_1 is older that point_2,
88 	   +1 if the reverse it true, and 0 if they are equal
89 	*/
90 	const struct EULER *point_1 = p_1, *point_2 = p_2;
91 
92 	if (point_1->t_start < point_2->t_start) return (-1);
93 	if (point_1->t_start > point_2->t_start) return (1);
94 	return (0);
95 }
96 
spotter_matrix_to_pole(struct GMT_CTRL * GMT,double T[3][3],double * plon,double * plat,double * w)97 void spotter_matrix_to_pole (struct GMT_CTRL *GMT, double T[3][3], double *plon, double *plat, double *w) {
98 	/* Based on Cox and Hart, 1986 */
99 	double T13_m_T31, T32_m_T23, T21_m_T12, L, H, tr;
100 	gmt_M_unused(GMT);
101 
102 	T13_m_T31 = T[0][2] - T[2][0];
103 	T32_m_T23 = T[2][1] - T[1][2];
104 	T21_m_T12 = T[1][0] - T[0][1];
105 	H = T32_m_T23 * T32_m_T23 + T13_m_T31 * T13_m_T31;
106 	L = sqrt (H + T21_m_T12 * T21_m_T12);
107 	H = sqrt (H);
108 	tr = T[0][0] + T[1][1] + T[2][2];
109 
110 	*plon = atan2d (T13_m_T31, T32_m_T23);
111 	if (*plon < 0.0) (*plon) += 360.0;
112 	*plat = atan2d (T21_m_T12, H);
113 	*w = atan2d (L, tr - 1.0);
114 	if (*plat < 0.0) {	/* Make N hemisphere pole */
115 		*plat = -(*plat);
116 		*(plon) += 180.0;
117 		if (*plon > 360.0) *plon -=-360.0;
118 		*w = -(*w);
119 	}
120 }
121 
122 #if 0	/* Currently unused */
123 GMT_LOCAL void spotter_make_rot0_matrix (struct GMT_CTRL *GMT, double lonp, double latp, double R[3][3], double E[]) {
124 	/* Based on Cox and Hart, 1986 */
125 	/* This starts setting up the matrix without knowing the angle of rotation
126 	 * Call spotter_set_rot_angle with R, E, and omega to complete the matrix
127 	 * lonp, latp	Euler pole in degrees
128 	 *
129 	 *	R		the 3x3 rotation matrix without terms depending on omega
130 	 */
131 
132         gmt_geo_to_cart (GMT, latp, lonp, E, true);
133 
134 	R[0][0] = E[0] * E[0];
135 	R[0][1] = E[0] * E[1];
136 	R[0][2] = E[0] * E[2];
137 
138 	R[1][0] = E[0] * E[1];
139 	R[1][1] = E[1] * E[1];
140 	R[1][2] = E[1] * E[2];
141 
142 	R[2][0] = E[0] * E[2];
143 	R[2][1] = E[1] * E[2];
144 	R[2][2] = E[2] * E[2];
145 }
146 #endif
147 
spotter_reverse_rotation_order(struct EULER * p,unsigned int n)148 GMT_LOCAL void spotter_reverse_rotation_order (struct EULER *p, unsigned int n) {
149 	/* Simply reverses the array from 1:n to n:1 */
150 	unsigned int i, j;
151 	struct EULER p_tmp;
152 
153 	for (i = 0; i < n/2; i++) {
154 		j = n - i - 1;
155 		if (i != j) {
156 			p_tmp = p[i];
157 			p[i] = p[j];
158 			p[j] = p_tmp;
159 		}
160 	}
161 }
162 
spotter_xyw_to_struct_euler(struct EULER * p,double lon[],double lat[],double w[],unsigned int n,unsigned int stages,bool convert)163 GMT_LOCAL void spotter_xyw_to_struct_euler (struct EULER *p, double lon[], double lat[], double w[], unsigned int n, unsigned int stages, bool convert) {
164 	/* Reload the EULER structure from the lon, lat, w arrays.
165 	 * stages is true if we are loading stage rotations (false is finite poles).
166 	 * convert is true if we must change angles to rates or vice versa */
167 	unsigned int i;
168 
169 	for (i = 0; i < n; i++) {
170 		p[i].lon = lon[i];
171 		p[i].lat = lat[i];
172 		p[i].duration = (stages) ? p[i].t_start - p[i].t_stop : p[i].t_start;
173 		p[i].omega = w[i];
174 		if (convert) p[i].omega /= p[i].duration;	/* Convert opening angle to opening rate */
175 		p[i].omega_r = p[i].omega * D2R;
176 		p[i].sin_lat = sind (p[i].lat);
177 		p[i].cos_lat = cosd (p[i].lat);
178 		p[i].lon_r = p[i].lon * D2R;
179 		p[i].lat_r = p[i].lat * D2R;
180 	}
181 }
182 
spotter_set_I_matrix(double R[3][3])183 GMT_LOCAL void spotter_set_I_matrix (double R[3][3]) {
184 	/* Simply sets R to I, the identity matrix */
185 
186 	gmt_M_memset (R, 9, double);
187 	R[0][0] = R[1][1] = R[2][2] = 1.0;
188 }
189 
spotter_must_do_track(int sideA[],int sideB[])190 GMT_LOCAL bool spotter_must_do_track (int sideA[], int sideB[]) {
191 	int dx, dy;
192 	/* First check if any of the two points are inside the box */
193 	if (sideA[0] == 0 && sideA[1] == 0) return (true);
194 	if (sideB[0] == 0 && sideB[1] == 0) return (true);
195 	/* Now check if the two points may cut a corner */
196 	dx = abs (sideA[0] - sideB[0]);
197 	dy = abs (sideA[1] - sideB[1]);
198 	if (dx && dy) return (true);
199 	if (dx == 2 || dy == 2) return (true);	/* Could cut across the box */
200 	return (false);
201 }
202 
spotter_set_inout_sides(double x,double y,double wesn[],int sideXY[2])203 GMT_LOCAL void spotter_set_inout_sides (double x, double y, double wesn[], int sideXY[2]) {
204 	/* Given the rectangular region in wesn, return -1, 0, +1 for
205 	 * x and y if the point is left/below (-1) in (0), or right/above (+1).
206 	 *
207 	 */
208 
209 	if (y < wesn[YLO])
210 		sideXY[1] = -1;
211 	else if (y > wesn[YHI])
212 		sideXY[1] = +1;
213 	else
214 		sideXY[1] = 0;
215 	while ((x + TWO_PI) < wesn[XHI]) x += TWO_PI;
216 	while ((x - TWO_PI) > wesn[XLO]) x -= TWO_PI;
217 	if (x < wesn[XLO])
218 		sideXY[0] = -1;
219 	else if (x > wesn[XHI])
220 		sideXY[0] = +1;
221 	else
222 		sideXY[0] = 0;
223 }
224 
spotter_covar_to_record(struct GMT_CTRL * GMT,struct EULER * e,double K[])225 void spotter_covar_to_record (struct GMT_CTRL *GMT, struct EULER *e, double K[]) {
226 	/* Translates an Euler covariance matrix to the 9 values needed for printout
227 	 * covariance matrix is stored as [k_hat a b c d e f g df] */
228 
229 	unsigned int k;
230 	gmt_M_unused(GMT);
231 	K[0] = e->k_hat;
232 	K[7] = e->g;
233 	K[8] = e->df;
234 	K[1] = e->C[0][0];
235 	K[2] = e->C[0][1];
236 	K[4] = e->C[0][2];
237 	K[3] = e->C[1][1];
238 	K[5] = e->C[1][2];
239 	K[6] = e->C[2][2];
240 	for (k = 1; k < 7; k++) K[k] *= (e->k_hat / e->g);
241 }
242 
spotter_record_to_covar(struct EULER * e,double K[])243 GMT_LOCAL void spotter_record_to_covar (struct EULER *e, double K[]) {
244 	/* Translates the 9 values read from plate motion file [k_hat a b c d e f g df]
245 	 * into the Euler covariance matrix */
246 
247 	unsigned int k, j;
248 	e->has_cov = true;
249 	e->k_hat   = K[0];
250 	e->g       = K[7];
251 	e->df	   = K[8];
252 	e->C[0][0] = K[1];
253 	e->C[0][1] = e->C[1][0] = K[2];
254 	e->C[0][2] = e->C[2][0] = K[4];
255 	e->C[1][1] = K[3];
256 	e->C[1][2] = e->C[2][1] = K[5];
257 	e->C[2][2] = K[6];
258 	for (k = 0; k < 3; k++) for (j = 0; j < 3; j++) e->C[k][j] *= (e->g / e->k_hat);
259 }
260 
spotter_set_M(struct GMT_CTRL * GMT,double lon,double lat,double M[3][3])261 void spotter_set_M (struct GMT_CTRL *GMT, double lon, double lat, double M[3][3]) {
262 	/* Just initializes the M(x), the skew-symmetric matrix needed to compute cov of rotated point */
263 	double x[3];
264         gmt_geo_to_cart (GMT, lat, lon, x, true);	/* Get Cartesian vector for this point */
265 	M[0][0] = M[1][1] = M[2][2] = 0.0;
266 	M[0][1] = -x[2];
267 	M[0][2] = x[1];
268 	M[1][0] = x[2];
269 	M[1][2] = -x[0];
270 	M[2][0] = -x[1];
271 	M[2][1] = x[0];
272 }
273 
spotter_cov_of_inverse(struct GMT_CTRL * GMT,struct EULER * e,double Ct[3][3])274 void spotter_cov_of_inverse (struct GMT_CTRL *GMT, struct EULER *e, double Ct[3][3]) {
275 	/* If A and cov(u) is a rotation and its covariance matrix and
276 	 * let A' and cov(v) be the inverse rotation and its covariacne matrix,
277 	 * then cov(v) = A*cov(u)*A' */
278 
279 	double A[3][3], At[3][3], tmp[3][3];
280 
281 	gmt_make_rot_matrix (GMT, e->lon, e->lat, e->omega, A);
282 	spotter_matrix_transpose (GMT, At, A);	/* Get A' */
283 	spotter_matrix_mult (GMT, e->C, At, tmp);	/* Calculate the cov(u)*A' product */
284 	spotter_matrix_mult (GMT, A, tmp, Ct);	/* Calculate the cov(v) = A*cov(u)*A' product */
285 }
286 
287 /* Converts a set of total reconstruction poles to forward stage poles for flowlines
288  *
289  * Based partly on Cox and Hart, 1986
290  */
291 
spotter_total_to_fwstages(struct GMT_CTRL * GMT,struct EULER p[],unsigned int n,bool finite_rates,bool stage_rates)292 void spotter_total_to_fwstages (struct GMT_CTRL *GMT, struct EULER p[], unsigned int n, bool finite_rates, bool stage_rates) {
293 	/* Convert finite rotations to forward stage rotations for flowlines */
294 	/* p[]		: Array of structure elements with rotation parameters
295 	 * n		: Number of rotations
296 	 * finite_rates	: true if finite rotations given in degree/my [else we have opening angle]
297 	 * stage_rates	: true if stage rotations should be returned in degree/my [else we return opening angle]
298 	 */
299 
300 	unsigned int i;
301 	double *elon = NULL, *elat = NULL, *ew = NULL, t_old;
302 	double R_young[3][3], R_old[3][3], R_stage[3][3];
303 
304 	/* Expects total reconstruction models to have youngest poles first */
305 
306 	elon = gmt_M_memory (GMT, NULL, n, double);
307 	elat = gmt_M_memory (GMT, NULL, n, double);
308 	ew   = gmt_M_memory (GMT, NULL, n, double);
309 
310 	spotter_set_I_matrix (R_young);		/* The first time, R_young is simply I */
311 
312 	/* First forward stage pole is the youngest total reconstruction pole */
313 
314 	t_old = 0.0;
315 	for (i = 0; i < n; i++) {
316 		if (finite_rates) p[i].omega *= p[i].duration;			/* Convert opening rate to opening angle */
317 		gmt_make_rot_matrix (GMT, p[i].lon, p[i].lat, -p[i].omega, R_old);	/* Make rotation matrix from rotation parameters, take transpose by passing -omega */
318 		spotter_matrix_mult (GMT, R_young, R_old, R_stage);			/* This is R_stage = R_young * R_old^t */
319 		spotter_matrix_to_pole (GMT, R_stage, &elon[i], &elat[i], &ew[i]);	/* Get rotation parameters from matrix */
320 		if (elon[i] > 180.0) elon[i] -= 360.0;				/* Adjust lon */
321 		spotter_matrix_transpose (GMT, R_young, R_old);			/* Set R_young = (R_old^t)^t = R_old */
322 		p[i].t_stop = t_old;
323 		t_old = p[i].t_start;
324 	}
325 
326 	/* Repopulate the EULER structure given the rotation parameters */
327 
328 	spotter_xyw_to_struct_euler (p, elon, elat, ew, n, true, stage_rates);
329 
330 	gmt_M_free (GMT, elon);
331 	gmt_M_free (GMT, elat);
332 	gmt_M_free (GMT, ew);
333 
334 	/* Flip order since stages go from oldest to youngest */
335 
336 	spotter_reverse_rotation_order (p, n);	/* Flip order since stages go from oldest to youngest */
337 }
338 
spotter_GPlates_pair(char * file)339 bool spotter_GPlates_pair (char *file) {
340 	/* Check if given file is actually a GPlates plate pair */
341 	unsigned int i;
342 	char A[GMT_LEN64] = {""}, B[GMT_LEN64] = {""};
343 	if (strlen (file) > GMT_LEN64) return (false);	/* Cannot be two pairs of tags */
344 	if (sscanf (file, "%[^-]-%s", A, B) != 2) return (false);
345 	i = 0;	while (A[i]) if (!isupper ((int)A[i++])) return (false);	/* Not all upper case tag */
346 	i = 0;	while (B[i]) if (!isupper ((int)B[i++])) return (false);	/* Not all upper case tag */
347 	return (true);	/* Got PLATE_A-PLATE_B specification for GPlates lookup, e.g., IND-CIB */
348 }
349 
spotter_parse(struct GMT_CTRL * GMT,char option,char * arg,struct SPOTTER_ROT * R)350 unsigned int spotter_parse (struct GMT_CTRL *GMT, char option, char *arg, struct SPOTTER_ROT *R) {
351 	unsigned int n_errors = 0, k = (arg[0] == '+') ? 1 : 0;
352 	char txt_a[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, *c = NULL;
353 	gmt_M_unused(GMT);
354 	if ((c = strstr (arg, "+i"))) c[0] = '\0';	/* Chop off modifier */
355 	if (c) R->invert = true;	/* Gave +i */
356 	if (k == 0 && spotter_GPlates_pair (arg)) {	/* A GPlates plate pair to look up in the rotation table */
357 		R->file = strdup (arg);
358 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Received GPlates pair: %s\n", arg);
359 	}
360 	else if (!gmt_access (GMT, &arg[k], F_OK)) {	/* Was given a file (with possible leading + flag) */
361 		R->file = strdup (&arg[k]);
362 		if (GMT_Get_FilePath (GMT->parent, GMT_IS_DATASET, GMT_IN, GMT_FILE_REMOTE, &(R->file))) n_errors++;
363 		if (k == 1) R->invert = true;
364 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Received rotation file: %s\n", R->file);
365 	}
366 	else if (gmt_file_is_cache (GMT->parent, arg)) {	/* Was given a remote file */
367 		R->file = strdup (&arg[k]);
368 		if (k == 1) R->invert = true;
369 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Received rotation file: %s\n", R->file);
370 	}
371 	else {	/* Apply a fixed total reconstruction rotation to all input points  */
372 		unsigned int ns = 0;
373 		size_t kk;
374 		for (kk = 0; kk < strlen (arg); kk++) if (arg[kk] == '/') ns++;
375 		if (ns == 2 || ns == 3) {	/* Looks like we got lon/lat/omega[/age] */
376 			R->single  = true;
377 			sscanf (arg, "%[^/]/%[^/]/%[^/]/%lg", txt_a, txt_b, txt_c, &(R->age));
378 			n_errors += gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_X), gmt_scanf_arg (GMT, txt_a, gmt_M_type (GMT, GMT_IN, GMT_X), false, &(R->lon)), txt_a);
379 			n_errors += gmt_verify_expectations (GMT, gmt_M_type (GMT, GMT_IN, GMT_Y), gmt_scanf_arg (GMT, txt_b, gmt_M_type (GMT, GMT_IN, GMT_Y), false, &(R->lat)), txt_b);
380 			R->w = atof (txt_c);
381 			if (ns == 2)	/* No age given */
382 				R->age = GMT->session.d_NaN;
383 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Received rotation parameters: %s\n", arg);
384 		}
385 		else {	/* Junk of some sort */
386 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Received rotation junk: %s\n", arg);
387 			n_errors++;
388 		}
389 	}
390 	if (n_errors) GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option %c: Rotation argument is neither GPlates pair, rotation file, or rotation parameters: %s\n", option, arg);
391 	if (c) c[0] = '+';	/* Restore modifier */
392 	return (n_errors);
393 }
394 
spotter_setrot(struct GMT_CTRL * GMT,struct EULER * e)395 void spotter_setrot (struct GMT_CTRL *GMT, struct EULER *e) {
396 	/* Update this rotation's derived settings */
397 	gmt_M_unused(GMT);
398 	e->duration = e->t_start - e->t_stop;
399 	e->omega /= e->duration;	/* Convert to opening rate */
400 	e->omega_r = e->omega * D2R;
401 	sincosd (e->lat, &e->sin_lat, &e->cos_lat);
402 	e->lon_r = e->lon * D2R;
403 	e->lat_r = e->lat * D2R;
404 }
405 
spotter_init(struct GMT_CTRL * GMT,char * infile,struct EULER ** p,unsigned int flowline,bool total_out,bool invert,double * t_max)406 int spotter_init (struct GMT_CTRL *GMT, char *infile, struct EULER **p, unsigned int flowline, bool total_out, bool invert, double *t_max) {
407 	/* file;	Name of file with backward stage poles, always GEOCENTRIC */
408 	/* p;		Pointer to stage pole array */
409 	/* flowline;	1 if flowlines rather than hotspot-tracks are needed */
410 	/* total_out;	true if we want to return finite (total construction poles) [alternative is stage poles] */
411 	/* invert;	true if we want to invert all the rotations */
412 	/* t_max;	Extend earliest stage pole back to this age */
413 	bool GPlates = false, total_in = false;
414 	int nf;
415 	unsigned int n, i = 0, k, id, A_id = 0, B_id = 0, p1, p2, V1 = 0, V2 = 0;
416 	size_t n_alloc = GMT_SMALL_CHUNK;
417 	double lon, lat, rot, t, last_t = -DBL_MAX;
418 	FILE *fp = NULL;
419 	struct EULER *e = NULL;
420 	char buffer[GMT_BUFSIZ] = {""}, A[GMT_LEN64] = {""}, B[GMT_LEN64] = {""}, txt[GMT_LEN64] = {""}, comment[GMT_BUFSIZ] = {""};
421 	char file[PATH_MAX] = {""}, Plates[GMT_BUFSIZ] = {""}, Rotations[GMT_BUFSIZ] = {""}, *this_c = NULL;
422 	double K[9];
423 
424 	strncpy (file, infile, PATH_MAX);
425 	gmt_filename_get (file);	/* Replace any ASCII 29 with spaces */
426 	if (gmt_file_is_cache (GMT->parent, file)) {	/* Must be a cache file */
427 		gmt_download_file_if_not_found (GMT, file, 0);
428 	}
429 	if (spotter_GPlates_pair (file)) {	/* Got PLATE_A-PLATE_B specification for GPlates lookup, e.g., IND-CIB */
430 		sscanf (file, "%[^-]-%s", A, B);
431 		if ((this_c = getenv ("GPLATES_PLATES"))) {
432 			strncpy (Plates, this_c, GMT_BUFSIZ-1);
433 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Use environmental value of GPLATES_PLATES = %s\n", Plates);
434 		}
435 		else {
436 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Use default plate ID file = %s\n", GPLATES_PLATES);
437 			if (!gmt_getsharepath (GMT, "spotter", GPLATES_PLATES, ".txt", Plates, R_OK)) {	/* Decode GPlates ID file */
438 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to find GPLATES_PLATES file : %s\n", Plates);
439 				return -GMT_FILE_NOT_FOUND;
440 			}
441 		}
442 #ifdef WIN32
443 		gmt_dos_path_fix (Plates);
444 #endif
445 		if ((fp = gmt_fopen (GMT, Plates, "r")) == NULL) {
446 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open GPlates plate id file %s\n", Plates);
447 			return -GMT_ERROR_ON_FOPEN;
448 		}
449 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Using GPlates plate id file %s\n", Plates);
450 		A_id = B_id = 0;
451 		while ((A_id == 0 || B_id == 0) && gmt_fgets (GMT, buffer, GMT_BUFSIZ, fp) != NULL) { /* Expects lon lat t0 t1 ccw-angle */
452 			if (buffer[0] == '#' || buffer[0] == '\n') continue;
453 			if ((nf = sscanf (buffer, "%d\t%s\t%[^\n]", &id, txt, comment)) < 3) continue;
454 			if (A_id == 0 && !strcmp (txt, A)) A_id = id;
455 			if (B_id == 0 && !strcmp (txt, B)) B_id = id;
456 		}
457 		gmt_fclose (GMT, fp);
458 		if (A_id == 0) {
459 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find an entry for plate %s in the GPlates plate id file\n", A);
460 			return -GMT_NOT_A_VALID_ARG;
461 		}
462 		if (B_id == 0) {
463 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find an entry for plate %s in the GPlates plate id file\n", B);
464 			return -GMT_NOT_A_VALID_ARG;
465 		}
466 		/* OK, here we have the two IDs */
467 		if ((this_c = getenv ("GPLATES_ROTATIONS"))) {
468 			strncpy (Rotations, this_c, GMT_BUFSIZ-1);
469 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Use environmental value of GPLATES_ROTATIONS = %s\n", Rotations);
470 		}
471 		else {
472 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Use default rotation file = %s\n", GPLATES_ROTATIONS);
473 			if (!gmt_getsharepath (GMT, "spotter", GPLATES_ROTATIONS, ".rot", Rotations, R_OK)) {	/* Decode GPlates rotations file */
474 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to find GPLATES_ROTATIONS file : %s\n", Rotations);
475 				return -GMT_FILE_NOT_FOUND;
476 			}
477 		}
478 #ifdef WIN32
479 		gmt_dos_path_fix (Rotations);
480 #endif
481 		if ((fp = gmt_fopen (GMT, Rotations, "r")) == NULL) {
482 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open GPlates rotation file %s\n", Rotations);
483 			return -GMT_ERROR_ON_FOPEN;
484 		}
485 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Using GPlates rotation file %s\n", Rotations);
486 		GPlates = total_in = true;
487 	}
488 	else if ((fp = gmt_fopen (GMT, file, "r")) == NULL) {
489 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open stage pole file: %s\n", file);
490 		return -GMT_ERROR_ON_FOPEN;
491 	}
492 
493 	e = gmt_M_memory (GMT, NULL, n_alloc, struct EULER);
494 	if (flowline) total_out = true;	/* Override so we get finite poles for conversion to forward stage poles at the end */
495 
496 	while (gmt_fgets (GMT, buffer, GMT_BUFSIZ, fp) != NULL) { /* Expects lon lat t0 t1 ccw-angle */
497 		if (buffer[0] == '#') continue;
498 		gmt_chop (buffer);
499 		if (gmt_is_a_blank_line (buffer)) continue;
500 
501 		if (GPlates) {
502 			if ((nf = sscanf (buffer, "%d %lf %lf %lf %lf %d %[^\n]", &p1, &t, &lat, &lon, &rot, &p2, comment)) != 7) continue;
503 			if (gmt_M_is_zero (t)) continue;	/* Not a rotation */
504 			if (strstr (comment, "cross-over") || strstr (comment, "cross over") || strstr (comment, "crossover")) continue;	/* Skip GPlates cross-over rotations */
505 			if (A_id == p1 && B_id == p2 && !V2) {	/* Exactly what we wanted */
506 				e[i].lon = lon;	e[i].lat = lat;	e[i].omega = rot;	e[i].t_start = t;
507 				V1 = true;	/* So we don't later find inverse rotations */
508 			}
509 			else if (A_id == p2 && B_id == p1 && !V1) {	/* Got the inverse rotation, so change angle sign */
510 				e[i].lon = lon;	e[i].lat = lat;	e[i].omega = -rot;	e[i].t_start = t;
511 				V2 = true;
512 			}
513 			else
514 				continue;	/* Not the plate pair we are looking for */
515 		}
516 		else {	/* The minimalist record formats is: lon lat t0 [t1] omega [covar] */
517 			nf = sscanf (buffer, "%lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf %lf",
518 				&e[i].lon, &e[i].lat, &e[i].t_start, &e[i].t_stop, &e[i].omega, &K[0], &K[1], &K[2], &K[3], &K[4], &K[5], &K[6], &K[7], &K[8]);
519 			if (! (nf == 4 || nf == 5 || nf == 13 || nf == 14)) {
520 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Rotation file format must be lon lat t0 [t1] omega [k_hat a b c d e f g df]\n");
521 				gmt_fclose (GMT, fp);
522 				gmt_M_free(GMT, e);
523 				return -GMT_PARSE_ERROR;
524 			}
525 			if (nf == 4 || nf == 13) {	/* total reconstruction format: Got lon lat t0 omega [covars], must shift the K's by one */
526 				if (i && !total_in) {
527 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Rotation file mixes total reconstruction and stage rotation format\n");
528 					gmt_M_free(GMT, e);
529 					return -GMT_PARSE_ERROR;
530 				}
531 				total_in = true;
532 				for (k = 8; k > 0; k--) K[k] = K[k-1];
533 				K[0] = e[i].omega;
534 				e[i].omega = e[i].t_stop;
535 				e[i].t_stop = 0.0;
536 			}
537 			if (nf > 5) { /* [K = covars] is stored as [k_hat a b c d e f g df] */
538 				if (K[8] == 0.0) K[8] = 10000.0;	/* No d.f. given */
539 				spotter_record_to_covar (&e[i], K);
540 			}
541 			if (total_in && invert) e[i].omega = -e[i].omega;	/* Want the inverse rotation; easy to do if total reconstruction rotations */
542 		}
543 
544 		if (total_in && e[i].t_start < last_t) {
545 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Rotation %d has time reversal\n", i);
546 			gmt_fclose (GMT, fp);
547 			gmt_M_free(GMT, e);
548 			return -GMT_RUNTIME_ERROR;
549 		}
550 		last_t = e[i].t_start;
551 		//if (e[i].omega == 0.0) continue;	/* skip null rotations [2016/10/18 PW: No, since GPlates sometimes contain null rotations between stable plates] */
552 		if (e[i].t_stop >= e[i].t_start) {
553 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Stage rotation %d has start time younger than stop time\n", i);
554 			gmt_fclose (GMT, fp);
555 			gmt_M_free(GMT, e);
556 			return -GMT_RUNTIME_ERROR;
557 		}
558 		spotter_setrot (GMT, &(e[i]));
559 		if (GPlates) {
560 			e[i].id[0] = A_id;
561 			e[i].id[1] = B_id;
562 		}
563 		i++;
564 		if (i == n_alloc) {
565 			n_alloc <<= 1;
566 			e = gmt_M_memory (GMT, e, n_alloc, struct EULER);
567 		}
568 	}
569 	gmt_fclose (GMT, fp);
570 
571 	if (GPlates && i == 0) {
572 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find rotations for the plate pair %s - %s\n", A, B);
573 		gmt_M_free(GMT, e);
574 		return -GMT_RUNTIME_ERROR;
575 	}
576 
577 	/* Sort the rotations to make sure they are in the expected order */
578 
579 	n = i;
580 	if (total_in) {
581 		qsort (e, n, sizeof (struct EULER), spotter_comp_total);
582 		invert = false;	/* Since we have taken care of this already */
583 	}
584 	else
585 		qsort (e, n, sizeof (struct EULER), spotter_comp_stage);
586 
587 	if (total_in && !total_out) spotter_total_to_stages (GMT, e, n, true, true);	/* Convert total reconstruction poles to forward stage poles */
588 	if (!total_in && total_out) {
589 		spotter_stages_to_total (GMT, e, n, true, true);	/* Convert forward stage poles to total reconstruction poles */
590 		if (invert)
591 			for (i = 0; i < n; i++) {e[i].omega = -e[i].omega; e[i].omega_r = - e[i].omega_r;}
592 		invert = false;	/* Since we have taken care of this now */
593 	}
594 	if (n < n_alloc) e = gmt_M_memory (GMT, e, n, struct EULER);
595 
596 	if (invert) {	/* If true this means we read stage rotations and want stage rotations out.  We must take a detour */
597 		spotter_stages_to_total (GMT, e, n, true, true);	/* Convert forward stage poles to total reconstruction poles */
598 		for (i = 0; i < n; i++) {e[i].omega = -e[i].omega; e[i].omega_r = - e[i].omega_r;}
599 		spotter_total_to_stages (GMT, e, n, true, true);	/* Convert total reconstruction poles to forward stage poles */
600 	}
601 
602 	if (flowline) {	/* Get the forward stage poles from the total reconstruction poles */
603 		spotter_total_to_fwstages (GMT, e, n, true, true);
604 	}
605 
606 	/* Extend oldest stage pole back to t_max Ma */
607 
608 	if ((*t_max) > 0.0 && e[0].t_start < (*t_max)) {
609 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "spotter: Extending oldest stage pole back to %g Ma\n", (*t_max));
610 
611 		e[0].t_start = (*t_max);
612 		e[0].duration = e[0].t_start - e[0].t_stop;
613 	}
614 	else
615 		(*t_max) = MAX (e[0].t_start, e[n-1].t_start);
616 	*p = e;
617 
618 	return (n);
619 }
620 
621 /* hotspot_init: Reads a file with hotspot information and returns pointer to
622  * array of structures.  Hotspot locations are stored as geodetic coordinates
623  * but are converted to GEOCENTRIC by this function if geocentric == true */
624 
spotter_hotspot_init(struct GMT_CTRL * GMT,char * file,bool geocentric,struct HOTSPOT ** p)625 int spotter_hotspot_init (struct GMT_CTRL *GMT, char *file, bool geocentric, struct HOTSPOT **p) {
626 	unsigned int i = 0, n;
627 	int ival;
628 	size_t n_alloc = GMT_CHUNK;
629 	FILE *fp = NULL;
630 	struct HOTSPOT *e = NULL;
631 	char buffer[GMT_BUFSIZ] = {""}, create, fit, plot;
632 	double P[3];
633 
634 	if ((fp = gmt_fopen (GMT, file, "r")) == NULL) {
635 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open file %s - aborts\n", file);
636 		return -1;
637 	}
638 
639 	e = gmt_M_memory (GMT, NULL, n_alloc, struct HOTSPOT);
640 
641 	while (gmt_fgets (GMT, buffer, GMT_BUFSIZ, fp) != NULL) {
642 		if (buffer[0] == '#') continue;
643 		gmt_chop (buffer);
644 		if (gmt_is_a_blank_line (buffer)) continue;
645 		n = sscanf (buffer, "%lf %lf %s %d %lf %lf %lf %c %c %c %s",
646 		            &e[i].lon, &e[i].lat, e[i].abbrev, &ival, &e[i].radius, &e[i].t_off, &e[i].t_on, &create, &fit, &plot, e[i].name);
647 		if (n == 3) ival = i + 1;	/* Minimal lon, lat, abbrev */
648 		if (ival <= 0) {
649 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Hotspot ID numbers must be > 0\n");
650 			gmt_fclose (GMT, fp);
651 			gmt_M_free (GMT, e);
652 			return -1;
653 		}
654 		e[i].id = ival;
655 		if (n >= 10) {		/* Long-form hotspot table used for rotator suite */
656 			e[i].create = (create == 'Y');
657 			e[i].fit = (fit == 'Y');
658 			e[i].plot = (plot == 'Y');
659 		}
660 		if (geocentric) e[i].lat = gmt_lat_swap (GMT, e[i].lat, GMT_LATSWAP_G2O);	/* Convert to geocentric */
661 		gmt_geo_to_cart (GMT, e[i].lat, e[i].lon, P, true);
662 		e[i].x = P[0];
663 		e[i].y = P[1];
664 		e[i].z = P[2];
665 		i++;
666 		if (i == n_alloc) {
667 			n_alloc <<= 1;
668 			e = gmt_M_memory (GMT, e, n_alloc, struct HOTSPOT);
669 		}
670 	}
671 	gmt_fclose (GMT, fp);
672 	if (i < n_alloc) e = gmt_M_memory (GMT, e, i, struct HOTSPOT);
673 	*p = e;
674 
675 	return (i);
676 }
677 
spotter_stage(struct GMT_CTRL * GMT,double t,struct EULER p[],unsigned int ns)678 int spotter_stage (struct GMT_CTRL *GMT, double t, struct EULER p[], unsigned int ns) {
679 	/* Return the stage ID for given t, or -1 if not within time range */
680 	unsigned int j = 0;
681 	gmt_M_unused(GMT);
682 	while (j < ns && t < p[j].t_stop) j++;	/* Find first applicable stage pole */
683 	if (j == ns) return (-1);	/* Outside in time */
684 	return (j);
685 }
686 
687 /* spotter_backtrack: Given a seamount location and age, trace the
688  *	hotspot-track between this seamount and a seamount of
689  *	age t_zero.  For t_zero = 0 this means the hotspot
690  */
691 
spotter_backtrack(struct GMT_CTRL * GMT,double xp[],double yp[],double tp[],unsigned int np,struct EULER p[],unsigned int ns,double d_km,double t_zero,unsigned int time_flag,double wesn[],double ** c)692 int spotter_backtrack (struct GMT_CTRL *GMT, double xp[], double yp[], double tp[], unsigned int np, struct EULER p[], unsigned int ns, double d_km, double t_zero, unsigned int time_flag, double wesn[], double **c) {
693 	/* xp, yp;	Points, in RADIANS */
694 	/* tp;		Age of feature in m.y. */
695 	/* np;		# of points */
696 	/* p;		Stage poles */
697 	/* ns;		# of stage poles */
698 	/* d_km;	Create track point every d_km km.  If == -1.0, return bend points only */
699 	/* t_zero;	Backtrack up to this age */
700 	/* time_flag;	1 if we want to interpolate and return time along track, 2 if we just want stage # */
701 	/* wesn:	if time_flag >= 10, only to track within the given box */
702 	/* **c;		Pointer to return track vector */
703 	unsigned int i, stage = 0, k, kk = 0, start_k = 0, nd = 1, nn;
704 	bool path, bend, go = false, box_check;
705 	int sideA[2] = {0, 0}, sideB[2] = {0, 0};
706 	size_t n_alloc = 2 * GMT_CHUNK;
707 	double t, tt = 0.0, dt, d_lon, tlon, dd = 0.0, i_km = 0.0, xnew, xx, yy, next_x, next_y;
708 	double s_lat, c_lat, s_lon, c_lon, cc, ss, cs, i_nd, *track = NULL;
709 
710 	bend = (d_km <= (GMT_CONV4_LIMIT - 1.0));
711 	path = (bend || d_km > GMT_CONV4_LIMIT);
712 	if (time_flag >= 10) {	/* Restrict track sampling to given wesn box */
713 		time_flag -= 10;
714 		box_check = true;
715 	}
716 	else {
717 		box_check = false;
718 		go = true;
719 	}
720 
721 	if (path) {
722 		track = gmt_M_memory (GMT, NULL, n_alloc, double);
723 		if (d_km != 0.0) i_km = EQ_RAD / d_km;
724 	}
725 
726 	if (p[ns-1].t_stop > t_zero) t_zero = p[ns-1].t_stop;	/* In case we don't go all the way to zero */
727 
728 	for (i = 0; i < np; i++) {
729 
730 		if (path) {
731 			start_k = kk++;
732 			if (kk == n_alloc) {
733 				n_alloc <<= 1;
734 				track = gmt_M_memory (GMT, track, n_alloc, double);
735 			}
736 		}
737 		nn = 0;
738 
739 		t = tp[i];
740 
741 		if (box_check) spotter_set_inout_sides (xp[i], yp[i], wesn, sideB);
742 		while (t > t_zero) {	/* As long as we're not back at zero age */
743 			if (box_check) sideA[0] = sideB[0], sideA[1] = sideB[1];
744 
745 			stage = 0;
746 			while (stage < ns && t <= p[stage].t_stop) stage++;	/* Find first applicable stage pole */
747 			if (stage == ns) {
748 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "(spotter_backtrack) Ran out of stage poles for t = %g\n", t);
749 				gmt_M_free(GMT, track);
750 				return -GMT_RUNTIME_ERROR;
751 			}
752 			dt = MIN (p[stage].duration, t - MAX(p[stage].t_stop, t_zero));
753 			d_lon = p[stage].omega_r * dt;
754 
755 			xnew = xp[i] - p[stage].lon_r;
756 			sincos (yp[i], &s_lat, &c_lat);
757 			sincos (xnew, &s_lon, &c_lon);
758 			cc = c_lat * c_lon;
759 			tlon = d_atan2 (c_lat * s_lon, p[stage].sin_lat * cc - p[stage].cos_lat * s_lat);
760 			s_lat = p[stage].sin_lat * s_lat + p[stage].cos_lat * cc;
761 			c_lat = sqrt (1.0 - s_lat * s_lat);
762 			ss = p[stage].sin_lat * s_lat;
763 			cs = p[stage].cos_lat * s_lat;
764 
765 			/* Get the next bend point first */
766 
767 			xnew = tlon + d_lon;
768 			sincos (xnew, &s_lon, &c_lon);
769 			cc = c_lat * c_lon;
770 			next_y = d_asin (ss - p[stage].cos_lat * cc);
771 			next_x = p[stage].lon_r + d_atan2 (c_lat * s_lon, p[stage].sin_lat * cc + cs);
772 
773 			if (next_x < 0.0) next_x += TWO_PI;
774 			if (next_x >= TWO_PI) next_x -= TWO_PI;
775 
776 			if (box_check) {	/* See if this segment _might_ involve the box in any way; if so do the track sampling */
777 				spotter_set_inout_sides (next_x, next_y, wesn, sideB);
778 				go = spotter_must_do_track (sideA, sideB);
779 			}
780 			if (path) {
781 				if (!bend) {
782 					nd = urint (ceil ((fabs (d_lon) * c_lat) * i_km));
783 					i_nd = 1.0 / nd;
784 					dd = d_lon * i_nd;
785 					tt = dt * i_nd;
786 				}
787 				track[kk++] = xp[i];
788 				if (kk == n_alloc) {
789 					n_alloc <<= 1;
790 					track = gmt_M_memory (GMT, track, n_alloc, double);
791 				}
792 				track[kk++] = yp[i];
793 				if (kk == n_alloc) {
794 					n_alloc <<= 1;
795 					track = gmt_M_memory (GMT, track, n_alloc, double);
796 				}
797 				if (time_flag) {
798 					track[kk++] = (time_flag == 2) ? (double)(ns - stage) : t;
799 					if (kk == n_alloc) {
800 						n_alloc <<= 1;
801 						track = gmt_M_memory (GMT, track, n_alloc, double);
802 					}
803 				}
804 				if (!go) nd = 1;
805 				for (k = 1; go && k < nd; k++) {
806 
807 					xnew = tlon + k * dd;
808 					sincos (xnew, &s_lon, &c_lon);
809 					cc = c_lat * c_lon;
810 					yy = d_asin (ss - p[stage].cos_lat * cc);
811 					xx = p[stage].lon_r + d_atan2 (c_lat * s_lon, p[stage].sin_lat * cc + cs);
812 
813 					if (xx < 0.0) xx += TWO_PI;
814 					if (xx >= TWO_PI) xx -= TWO_PI;
815 					track[kk++] = xx;
816 					if (kk == n_alloc) {
817 						n_alloc <<= 1;
818 						track = gmt_M_memory (GMT, track, n_alloc, double);
819 					}
820 					track[kk++] = yy;
821 					if (kk == n_alloc) {
822 						n_alloc <<= 1;
823 						track = gmt_M_memory (GMT, track, n_alloc, double);
824 					}
825 					if (time_flag) {
826 						track[kk++] = (time_flag == 2) ? (double)(ns - stage) : t - k * tt;
827 						if (kk == n_alloc) {
828 							n_alloc <<= 1;
829 							track = gmt_M_memory (GMT, track, n_alloc, double);
830 						}
831 					}
832 				}
833 				nn += nd;
834 			}
835 			xp[i] = next_x;	yp[i] = next_y;
836 			t -= dt;
837 		}
838 		if (path) {
839 			track[kk++] = xp[i];
840 			if (kk == n_alloc) {
841 				n_alloc <<= 1;
842 				track = gmt_M_memory (GMT, track, n_alloc, double);
843 			}
844 			track[kk++] = yp[i];
845 			if (kk == n_alloc) {
846 				n_alloc <<= 1;
847 				track = gmt_M_memory (GMT, track, n_alloc, double);
848 			}
849 			if (time_flag) {
850 				track[kk++] = (time_flag == 2) ? (double)(ns - stage) : t;
851 				if (kk == n_alloc) {
852 					n_alloc <<= 1;
853 					track = gmt_M_memory (GMT, track, n_alloc, double);
854 				}
855 			}
856 			track[start_k] = (double)(nn+1);
857 		}
858 	}
859 	if (path) {
860 		track = gmt_M_memory (GMT, track, kk, double);
861 		*c = track;
862 		return (kk);
863 	}
864 
865 	return (np);
866 }
867 
868 /* spotter_forthtrack: Given a hotspot location and final age, trace the
869  *	hotspot-track between the seamount created at t_zero and a
870  *	seamount of age tp.  For t_zero = 0 this means from the hotspot.
871  */
872 
spotter_forthtrack(struct GMT_CTRL * GMT,double xp[],double yp[],double tp[],unsigned int np,struct EULER p[],unsigned int ns,double d_km,double t_zero,unsigned int time_flag,double wesn[],double ** c)873 int spotter_forthtrack (struct GMT_CTRL *GMT, double xp[], double yp[], double tp[], unsigned int np, struct EULER p[], unsigned int ns, double d_km, double t_zero, unsigned int time_flag, double wesn[], double **c) {
874 	/* xp, yp;	Points, in RADIANS */
875 	/* tp;		Age of feature in m.y. */
876 	/* np;		# of points */
877 	/* p;		Stage poles */
878 	/* ns;		# of stage poles */
879 	/* d_km;	Create track point every d_km km.  If == -1.0, return bend points only */
880 	/* t_zero;	Foretrack from this age forward */
881 	/* time_flag;	1 if we want to interpolate and return time along track */
882 	/* wesn:	if time_flag >= 10, only to track within the given box */
883 	/* c;		Pointer to return track vector */
884 	unsigned int i, stage = 0, k, kk = 0, start_k = 0, nd = 1, nn;
885 	bool path, bend, go = false, box_check;
886 	int sideA[2] = {0, 0}, sideB[2] = {0, 0};
887 	size_t n_alloc = BIG_CHUNK;
888 	double t, tt = 0.0, dt, d_lon, tlon, dd = 0.0, i_km = 0.0, xnew, xx, yy, *track = NULL;
889 	double s_lat, c_lat, s_lon, c_lon, cc, ss, cs, i_nd, next_x, next_y;
890 
891 	bend = (d_km <= (GMT_CONV4_LIMIT - 1.0));
892 	path = (bend || d_km > GMT_CONV4_LIMIT);
893 	if (time_flag >= 10) {	/* Restrict track sampling to given wesn box */
894 		time_flag -= 10;
895 		box_check = true;
896 	}
897 	else {
898 		box_check = false;
899 		go = true;
900 	}
901 
902 	if (path) {
903 		track = gmt_M_memory (GMT, NULL, n_alloc, double);
904 		if (d_km != 0.0) i_km = EQ_RAD / d_km;
905 	}
906 
907 	if (p[ns-1].t_stop > t_zero) t_zero = p[ns-1].t_stop;	/* In case we don't go all the way to zero */
908 
909 	for (i = 0; i < np; i++) {
910 
911 		if (path) {
912 			start_k = kk++;
913 			if (kk == n_alloc) {
914 				n_alloc <<= 1;
915 				track = gmt_M_memory (GMT, track, n_alloc, double);
916 			}
917 		}
918 		nn = 0;
919 
920 		t = t_zero;
921 
922 		if (box_check) spotter_set_inout_sides (xp[i], yp[i], wesn, sideB);
923 		while (t < tp[i]) {	/* As long as we're not back at zero age */
924 			if (box_check) sideA[0] = sideB[0], sideA[1] = sideB[1];
925 			stage = ns - 1;
926 			while (stage && (t + GMT_CONV8_LIMIT) > p[stage].t_start) stage--;
927 			/* while (stage < ns && (t + GMT_CONV8_LIMIT) < p[stage].t_stop) stage++; */	/* Find first applicable stage pole */
928 			if (stage == ns) {
929 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "(spotter_forthtrack) Ran out of stage poles for t = %g\n", t);
930 				gmt_M_free(GMT, track);
931 				return -GMT_RUNTIME_ERROR;
932 			}
933 			dt = MIN (tp[i], p[stage].t_start) - t;	/* Time interval to rotate */
934 			d_lon = p[stage].omega_r * dt;		/* Rotation angle (radians) */
935 
936 			xnew = xp[i] - p[stage].lon_r;
937 			sincos (yp[i], &s_lat, &c_lat);
938 			sincos (xnew, &s_lon, &c_lon);
939 			cc = c_lat * c_lon;
940 			tlon = d_atan2 (c_lat * s_lon, p[stage].sin_lat * cc - p[stage].cos_lat * s_lat);
941 			s_lat = p[stage].sin_lat * s_lat + p[stage].cos_lat * cc;
942 			c_lat = sqrt (1.0 - s_lat * s_lat);
943 			ss = p[stage].sin_lat * s_lat;
944 			cs = p[stage].cos_lat * s_lat;
945 
946 			/* Get the next bend point first */
947 
948 			xnew = tlon - d_lon;
949 			sincos (xnew, &s_lon, &c_lon);
950 			cc = c_lat * c_lon;
951 			next_y = d_asin (ss - p[stage].cos_lat * cc);
952 			next_x = p[stage].lon_r + d_atan2 (c_lat * s_lon, p[stage].sin_lat * cc + cs);
953 
954 			if (next_x < 0.0) next_x += TWO_PI;
955 			if (next_x >= TWO_PI) next_x -= TWO_PI;
956 
957 			if (box_check) {	/* See if this segment _might_ involve the box in any way; if so do the track sampling */
958 				spotter_set_inout_sides (next_x, next_y, wesn, sideB);
959 				go = spotter_must_do_track (sideA, sideB);
960 			}
961 			if (path) {
962 				if (!bend) {
963 					nd = urint (ceil ((fabs (d_lon) * c_lat) * i_km));
964 					i_nd = 1.0 / nd;
965 					dd = d_lon * i_nd;
966 					tt = dt * i_nd;
967 				}
968 				track[kk++] = xp[i];
969 				if (kk == n_alloc) {
970 					n_alloc <<= 1;
971 					track = gmt_M_memory (GMT, track, n_alloc, double);
972 				}
973 				track[kk++] = yp[i];
974 				if (kk == n_alloc) {
975 					n_alloc <<= 1;
976 					track = gmt_M_memory (GMT, track, n_alloc, double);
977 				}
978 				if (time_flag) {
979 					track[kk++] = (time_flag == 2) ? (double)(ns - stage) : t;
980 					if (kk == n_alloc) {
981 						n_alloc <<= 1;
982 						track = gmt_M_memory (GMT, track, n_alloc, double);
983 					}
984 				}
985 				if (!go) nd = 1;
986 				for (k = 1; go && k < nd; k++) {
987 					xnew = tlon - k * dd;
988 					sincos (xnew, &s_lon, &c_lon);
989 					cc = c_lat * c_lon;
990 					yy = d_asin (ss - p[stage].cos_lat * cc);
991 					xx = p[stage].lon_r + d_atan2 (c_lat * s_lon, p[stage].sin_lat * cc + cs);
992 
993 					if (xx < 0.0) xx += TWO_PI;
994 					if (xx >= TWO_PI) xx -= TWO_PI;
995 					track[kk++] = xx;
996 					if (kk == n_alloc) {
997 						n_alloc <<= 1;
998 						track = gmt_M_memory (GMT, track, n_alloc, double);
999 					}
1000 					track[kk++] = yy;
1001 					if (kk == n_alloc) {
1002 						n_alloc <<= 1;
1003 						track = gmt_M_memory (GMT, track, n_alloc, double);
1004 					}
1005 					if (time_flag) {
1006 						track[kk++] = (time_flag == 2) ? (double)(ns - stage) : t + k * tt;
1007 						if (kk == n_alloc) {
1008 							n_alloc <<= 1;
1009 							track = gmt_M_memory (GMT, track, n_alloc, double);
1010 						}
1011 					}
1012 				}
1013 				nn += nd;
1014 			}
1015 
1016 			xp[i] = next_x;	yp[i] = next_y;
1017 			t += dt;
1018 		}
1019 		if (path) {
1020 			track[kk++] = xp[i];
1021 			if (kk == n_alloc) {
1022 				n_alloc <<= 1;
1023 				track = gmt_M_memory (GMT, track, n_alloc, double);
1024 			}
1025 			track[kk++] = yp[i];
1026 			if (kk == n_alloc) {
1027 				n_alloc <<= 1;
1028 				track = gmt_M_memory (GMT, track, n_alloc, double);
1029 			}
1030 			if (time_flag) {
1031 				track[kk++] = (time_flag == 2) ? (double)(ns - stage) : t;
1032 				if (kk == n_alloc) {
1033 					n_alloc <<= 1;
1034 					track = gmt_M_memory (GMT, track, n_alloc, double);
1035 				}
1036 			}
1037 			track[start_k] = (double)(nn+1);
1038 		}
1039 	}
1040 	if (path) {
1041 		track = gmt_M_memory (GMT, track, kk, double);
1042 		*c = track;
1043 		return (kk);
1044 	}
1045 
1046 	return (np);
1047 }
1048 
spotter_total_to_stages(struct GMT_CTRL * GMT,struct EULER p[],unsigned int n,bool finite_rates,bool stage_rates)1049 void spotter_total_to_stages (struct GMT_CTRL *GMT, struct EULER p[], unsigned int n, bool finite_rates, bool stage_rates) {
1050 	/* Convert finite rotations to backwards stage rotations for backtracking */
1051 	/* p[]		: Array of structure elements with rotation parameters
1052 	 * n		: Number of rotations
1053 	 * finite_rates	: true if finite rotations given in degree/my [else we have opening angle]
1054 	 * stage_rates	: true if stage rotations should be returned in degree/my [else we return opening angle]
1055 	 */
1056 
1057 	unsigned int i;
1058 	double *elon = NULL, *elat = NULL, *ew = NULL, t_old;
1059 	double R_young[3][3], R_old[3][3], R_stage[3][3];
1060 
1061 	/* Expects total reconstruction models to have youngest poles first */
1062 
1063 	elon = gmt_M_memory (GMT, NULL, n, double);
1064 	elat = gmt_M_memory (GMT, NULL, n, double);
1065 	ew   = gmt_M_memory (GMT, NULL, n, double);
1066 
1067 	spotter_set_I_matrix (R_young);		/* The first time, R_young is simply I */
1068 
1069 	t_old = 0.0;
1070 	for (i = 0; i < n; i++) {
1071 		if (finite_rates) p[i].omega *= p[i].duration;				/* Convert opening rate to opening angle */
1072 		gmt_make_rot_matrix (GMT, p[i].lon, p[i].lat, p[i].omega, R_old);	/* Get rotation matrix from pole and angle */
1073 		spotter_matrix_mult (GMT, R_young, R_old, R_stage);			/* This is R_stage = R_young^t * R_old */
1074 		spotter_matrix_to_pole (GMT, R_stage, &elon[i], &elat[i], &ew[i]);	/* Get rotation parameters from matrix */
1075 		if (elon[i] > 180.0) elon[i] -= 360.0;					/* Adjust lon */
1076 		spotter_matrix_transpose (GMT, R_young, R_old);				/* Sets R_young = transpose (R_old) for next round */
1077 		p[i].t_stop = t_old;
1078 		t_old = p[i].t_start;
1079 	}
1080 
1081 	/* Repopulate the EULER structure given the rotation parameters */
1082 
1083 	spotter_xyw_to_struct_euler (p, elon, elat, ew, n, true, stage_rates);
1084 
1085 	gmt_M_free (GMT, elon);
1086 	gmt_M_free (GMT, elat);
1087 	gmt_M_free (GMT, ew);
1088 
1089 	spotter_reverse_rotation_order (p, n);	/* Flip order since stages go from oldest to youngest */
1090 }
1091 
spotter_stages_to_total(struct GMT_CTRL * GMT,struct EULER p[],unsigned int n,bool finite_rates,bool stage_rates)1092 void spotter_stages_to_total (struct GMT_CTRL *GMT, struct EULER p[], unsigned int n, bool finite_rates, bool stage_rates) {
1093 	/* Convert stage rotations to finite rotations */
1094 	/* p[]		: Array of structure elements with rotation parameters
1095 	 * n		: Number of rotations
1096 	 * finite_rates	: true if finite rotations should be returned in degree/my [else we return opening angle]
1097 	 * stage_rates	: true if stage rotations given in degree/my [else we have opening angle]
1098 	 */
1099 
1100 	unsigned int stage;
1101 	double *elon = NULL, *elat = NULL, *ew = NULL;
1102 	double R_young[3][3], R_old[3][3], R_stage[3][3];
1103 
1104 	/* Expects stage pole models to have oldest poles first, so we must flip order */
1105 
1106 	spotter_reverse_rotation_order (p, n);	/* Expects stage pole models to have oldest poles first, so we must flip order */
1107 
1108 	elon = gmt_M_memory (GMT, NULL, n, double);
1109 	elat = gmt_M_memory (GMT, NULL, n, double);
1110 	ew   = gmt_M_memory (GMT, NULL, n, double);
1111 
1112 	spotter_set_I_matrix (R_old);		/* The first time, R_old is simply I */
1113 
1114 	for (stage = 0; stage < n; stage++) {
1115 		if (stage_rates) p[stage].omega *= p[stage].duration;					/* Convert opening rate to opening angle */
1116 		gmt_make_rot_matrix (GMT, p[stage].lon, p[stage].lat, p[stage].omega, R_stage);	/* Make matrix from rotation parameters */
1117 		spotter_matrix_mult (GMT, R_old, R_stage, R_young);					/* Set R_young = R_old * R_stage */
1118 		gmt_M_memcpy (R_old, R_young, 9, double);							/* Set R_old = R_young for next time around */
1119 		spotter_matrix_to_pole (GMT, R_young, &elon[stage], &elat[stage], &ew[stage]);		/* Get rotation parameters from matrix */
1120 		if (elon[stage] > 180.0) elon[stage] -= 360.0;						/* Adjust lon */
1121 	}
1122 
1123 	/* Repopulate the EULER structure given the rotation parameters */
1124 
1125 	spotter_xyw_to_struct_euler (p, elon, elat, ew, n, false, finite_rates);
1126 
1127 	gmt_M_free (GMT, elon);
1128 	gmt_M_free (GMT, elat);
1129 	gmt_M_free (GMT, ew);
1130 }
1131 
spotter_add_rotations(struct GMT_CTRL * GMT,struct EULER a[],int n_a_in,struct EULER b[],int n_b_in,struct EULER * c[],unsigned int * n_c)1132 void spotter_add_rotations (struct GMT_CTRL *GMT, struct EULER a[], int n_a_in, struct EULER b[], int n_b_in, struct EULER *c[], unsigned int *n_c) {
1133 	/* Takes two finite rotation models and adds them together.
1134 	 * We do this by first converting both to stage poles.  We then
1135 	 * determine all the time knots needed, and then resample both
1136 	 * stage rotation models onto the same list of knots.  This is
1137 	 * easy to do with stage poles since we end up using partial
1138 	 * stage rotations.  To do this with finite poles would involve
1139 	 * computing intermediate stages anyway.  When we have the resampled
1140 	 * stage rotations we convert back to finite rotations and then
1141 	 * simply add each pair of rotations using matrix multiplication.
1142 	 * The final finite rotation model is returned in c. */
1143 
1144 	struct EULER *a2 = NULL, *b2 = NULL, *c2 = NULL;
1145 	double *t = NULL, t_min, t_max, Ra[3][3], Rb[3][3], Rab[3][3], lon, lat, w, sign_a, sign_b;
1146 	double tmp[3][3], RaT[3][3], Ca[3][3], Cb[3][3];
1147 	unsigned int i, j, k, n_k = 0, n_a, n_b;
1148 	bool a_ok = true, b_ok = true;
1149 
1150 	sign_a = (n_a_in > 0) ? +1.0 : -1.0;
1151 	sign_b = (n_b_in > 0) ? +1.0 : -1.0;
1152 	n_a = abs (n_a_in);
1153 	n_b = abs (n_b_in);
1154 	/* Allocate more than we need, must likely */
1155 
1156 	t = gmt_M_memory (GMT, NULL, n_a + n_b, double);
1157 
1158 	/* First convert the two models to stage poles */
1159 
1160 	spotter_total_to_stages (GMT, a, n_a, true, true);		/* Return stage poles */
1161 	spotter_total_to_stages (GMT, b, n_b, true, true);		/* Return stage poles */
1162 
1163 	/* Find all the time knots used by the two models */
1164 
1165 	t_max = MIN (a[0].t_start, b[0].t_start);
1166 	t_min = MAX (a[n_a-1].t_stop, b[n_b-1].t_stop);
1167 	t[n_k++] = t_max;
1168 	i = j = 0;
1169 	while (i < n_a && a[i].t_stop >= t[0]) i++;
1170 	if (i == (n_a - 1)) a_ok = false;
1171 	while (j < n_b && b[j].t_stop >= t[0]) j++;
1172 	if (j == (n_b - 1)) b_ok = false;
1173 	while (a_ok || b_ok) {
1174 		if (a_ok && !b_ok) {		/* Only a left */
1175 			t[n_k] = a[i++].t_stop;
1176 			if (i == (n_a - 1)) a_ok = false;
1177 		}
1178 		else if (b_ok && !a_ok) {	/* Only b left */
1179 			t[n_k] = b[j++].t_stop;
1180 			if (j == (n_b - 1)) b_ok = false;
1181 		}
1182 		else if (a_ok && a[i].t_stop > b[j].t_stop) {
1183 			t[n_k] = a[i++].t_stop;
1184 			if (i == (n_a - 1)) a_ok = false;
1185 		}
1186 		else if (b_ok && b[j].t_stop > a[i].t_stop) {
1187 			t[n_k] = b[j++].t_stop;
1188 			if (j == (n_b - 1)) b_ok = false;
1189 		}
1190 		else {	/* Same time for both */
1191 			t[n_k] = b[j++].t_stop;
1192 			i++;
1193 			if (i == (n_a - 1)) a_ok = false;
1194 			if (j == (n_b - 1)) b_ok = false;
1195 		}
1196 		n_k++;
1197 	}
1198 	t[n_k++] = t_min;
1199 	n_k--;	/* Number of structure elements is one less than number of knots */
1200 
1201 	b2 = gmt_M_memory (GMT, NULL, n_k, struct EULER);
1202 	a2 = gmt_M_memory (GMT, NULL, n_k, struct EULER);
1203 	c2 = gmt_M_memory (GMT, NULL, n_k, struct EULER);
1204 
1205 	for (k = i = j = 0; k < n_k; k++) {	/* Resample the two stage pole models onto the same knots */
1206 		/* First resample p onto p2 */
1207 		while (a[i].t_stop >= t[k]) i++;				/* Wind up */
1208 		a2[k] = a[i];							/* First copy everything */
1209 		if (a2[k].t_start > t[k]) a2[k].t_start = t[k];			/* Adjust start time */
1210 		if (a2[k].t_stop < t[k+1]) a2[k].t_stop = t[k+1];		/* Adjust stop time */
1211 		a2[k].duration = a2[k].t_start - a2[k].t_stop;			/* Set the duration */
1212 
1213 		/* Then resample a onto a2 */
1214 		while (b[j].t_stop >= t[k]) j++;				/* Wind up */
1215 		b2[k] = b[j];							/* First copy everything */
1216 		if (b2[k].t_start > t[k]) b2[k].t_start = t[k];			/* Adjust start time */
1217 		if (b2[k].t_stop < t[k+1]) b2[k].t_stop = t[k+1];		/* Adjust stop time */
1218 		b2[k].duration = b2[k].t_start - b2[k].t_stop;			/* Set the duration */
1219 	}
1220 
1221 	gmt_M_free (GMT, t);
1222 
1223 	/* Now switch to finite rotations again to do the additions */
1224 
1225 	spotter_stages_to_total (GMT, a2, n_k, false, true);	/* Return opening angles, not rates this time */
1226 	spotter_stages_to_total (GMT, b2, n_k, false, true);
1227 
1228 	for (i = 0; i < n_k; i++) {	/* Add each pair of rotations */
1229 		gmt_make_rot_matrix (GMT, a2[i].lon, a2[i].lat, sign_a * a2[i].omega, Ra);
1230 		gmt_make_rot_matrix (GMT, b2[i].lon, b2[i].lat, sign_b * b2[i].omega, Rb);
1231 		spotter_matrix_mult (GMT, Rb, Ra, Rab);	/* Rot a + Rot b = RB * Ra ! */
1232 		spotter_matrix_to_pole (GMT, Rab, &lon, &lat, &w);
1233 		c2[i].lon = lon;
1234 		c2[i].lat = lat;
1235 		c2[i].t_start = a2[i].t_start;
1236 		c2[i].t_stop  = 0.0;
1237 		c2[i].duration = c2[i].t_start;
1238 		c2[i].omega = w / c2[i].duration;	/* Return rates again */
1239 		c2[i].id[0] = a2[i].id[0];
1240 		c2[i].id[1] = b2[i].id[1];
1241 		if (a2[i].has_cov && b2[i].has_cov) {	/* May compute combined covariance matrix, assuming khats = 1 */
1242 			double fa, fb;
1243 			c2[i].df = a2[i].df + b2[i].df;
1244 			if (a2[i].k_hat != 1.0 || b2[i].k_hat != 1.0) {	/* Kappas are not one, use pooled estimate */
1245 				c2[i].k_hat = (c2[i].df / (a2[i].df / a2[i].k_hat + b2[i].df / b2[i].k_hat));
1246 				fa = a2[i].k_hat / c2[i].k_hat;
1247 				fb = b2[i].k_hat / c2[i].k_hat;
1248 			}
1249 			else
1250 				c2[i].k_hat = fa = fb = 1.0;
1251 
1252 			spotter_matrix_transpose (GMT, RaT, Ra);
1253 			if (sign_a < 0.0)
1254 				spotter_cov_of_inverse (GMT, &a2[i], Ca);
1255 			else
1256 				gmt_M_memcpy (Ca, a2[i].C, 9, double);
1257 			if (sign_b < 0.0)
1258 				spotter_cov_of_inverse (GMT, &b2[i], Cb);
1259 			else
1260 				gmt_M_memcpy (Cb, b2[i].C, 9, double);
1261 			spotter_matrix_mult (GMT, Cb, Ra, tmp);
1262 			spotter_matrix_mult (GMT, RaT, tmp, c2[i].C);
1263 			for (k = 0; k < 3; k++) for (j = 0; j < 3; j++) c2[i].C[k][j] *= fb;
1264 			for (k = 0; k < 3; k++) for (j = 0; j < 3; j++) tmp[k][j] = fa * Ca[k][j];
1265 			spotter_matrix_add (GMT, c2[i].C, tmp, c2[i].C);
1266 			c2[i].has_cov = true;
1267 			c2[i].g = MIN(a2[i].g, b2[i].g);
1268 		}
1269 	}
1270 	gmt_M_free (GMT, a2);
1271 	gmt_M_free (GMT, b2);
1272 
1273 	*n_c = n_k;
1274 	*c = c2;
1275 }
1276 
spotter_t2w(struct GMT_CTRL * GMT,struct EULER a[],unsigned int n,double t)1277 double spotter_t2w (struct GMT_CTRL *GMT, struct EULER a[], unsigned int n, double t) {
1278 	/* Take time, return cumulative omega */
1279 
1280 	int i;
1281 	double w = 0.0;
1282 	gmt_M_unused(GMT);
1283 
1284 	i = n - 1;
1285 	while (i >= 0 && t > a[i].t_start) {
1286 		w += fabs (a[i].omega * a[i].duration);
1287 		i--;
1288 	}
1289 	if (i >= 0 && t > a[i].t_stop) {
1290 		w += fabs (a[i].omega * (t - a[i].t_stop));
1291 	}
1292 
1293 	return (w);
1294 }
1295 
1296 #if 0	/* Seems to be unused */
1297 void spotter_set_rot_angle (double w, double R[3][3], double E[])
1298 {	/* Sets R using R(no_omega) and the given rotation angle w in radians */
1299 	double sin_w, cos_w, c, E_x, E_y, E_z;
1300 
1301 	sincos (w, &sin_w, &cos_w);
1302 	c = 1.0 - cos_w;
1303 
1304 	E_x = E[0] * sin_w;
1305 	E_y = E[1] * sin_w;
1306 	E_z = E[2] * sin_w;
1307 
1308 	R[0][0] = R[0][0] * c + cos_w;
1309 	R[0][1] = R[0][1] * c - E_z;
1310 	R[0][2] = R[0][2] * c + E_y;
1311 
1312 	R[1][0] = R[1][0] * c + E_z;
1313 	R[1][1] = R[1][1] * c + cos_w;
1314 	R[1][2] = R[1][2] * c - E_x;
1315 
1316 	R[2][0] = R[2][0] * c - E_y;
1317 	R[2][1] = R[2][1] * c + E_x;
1318 	R[2][2] = R[2][2] * c + cos_w;
1319 }
1320 #endif
1321 
spotter_matrix_mult(struct GMT_CTRL * GMT,double a[3][3],double b[3][3],double c[3][3])1322 void spotter_matrix_mult (struct GMT_CTRL *GMT, double a[3][3], double b[3][3], double c[3][3]) {
1323 	/* C = A * B */
1324 	unsigned int i, j, k;
1325 	gmt_M_unused(GMT);
1326 
1327 	for (i = 0; i < 3; i++) {
1328 		for (j = 0; j < 3; j++) {
1329 			c[i][j] = 0.0;
1330 			for (k = 0; k < 3; k++) c[i][j] += a[i][k] * b[k][j];
1331 		}
1332 	}
1333 }
1334 
spotter_matrix_transpose(struct GMT_CTRL * GMT,double At[3][3],double A[3][3])1335 void spotter_matrix_transpose (struct GMT_CTRL *GMT, double At[3][3], double A[3][3]) {
1336 	/* Computes the matrix transpose */
1337 
1338 	unsigned int i, j;
1339 	gmt_M_unused(GMT);
1340 	for (j = 0; j < 3; j++) {
1341 		for (i = 0; i < 3; i++) {
1342 			At[i][j] = A[j][i];
1343 		}
1344 	}
1345 }
1346 
spotter_matrix_add(struct GMT_CTRL * GMT,double A[3][3],double B[3][3],double C[3][3])1347 void spotter_matrix_add (struct GMT_CTRL *GMT, double A[3][3], double B[3][3], double C[3][3]) {
1348 	/* Computes the matrix addition */
1349 
1350 	unsigned int i, j;
1351 	gmt_M_unused(GMT);
1352 	for (j = 0; j < 3; j++) {
1353 		for (i = 0; i < 3; i++) {
1354 			C[i][j] = A[i][j] + B[i][j];
1355 		}
1356 	}
1357 }
1358 
spotter_get_rotation(struct GMT_CTRL * GMT,struct EULER * p,unsigned int np,double t,double * lon,double * lat,double * w)1359 void spotter_get_rotation (struct GMT_CTRL *GMT, struct EULER *p, unsigned int np, double t, double *lon, double *lat, double *w) {
1360 	/* Given finite rotations and a time t, return the rotation (lon,lat,w) for that time via interpolation */
1361 	/* We have already checked that t is within range of p */
1362 	unsigned int i;
1363 	struct EULER e[2];
1364 	double R[3][3], dR[3][3], X[3][3], omega;
1365 
1366 	for (i = 0; i < np && t > p[i].t_start; i++);	/* Wind to first partial rotation stage */
1367 	if (doubleAlmostEqualZero (t, p[i].t_start)) {	/* Hit an exact finite rotation, just return those parameters */
1368 		*lon = p[i].lon;
1369 		*lat = p[i].lat;
1370 		*w = p[i].omega * p[i].duration;
1371 		return;
1372 	}
1373 	if (i == 0) {	/* Just need a partial rotation of the first full rotation */
1374 		*lon = p[0].lon;
1375 		*lat = p[0].lat;
1376 		*w = p[0].omega * t;
1377 		return;
1378 	}
1379 
1380 	/* Here we must add a partial rotation to the last finite rotation */
1381 
1382 	i--;
1383 	if ((i+2) > np) {	/* p[i] and p[i+1] must should exist but Coverity is not sure */
1384 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "spotter_get_rotation: Cannot copy two rotations!");
1385 		return;
1386 	}
1387 	gmt_M_memcpy (&e[0], &p[i], 1, struct EULER);	/* Duplicate the two finite rotations bracketing the desired time */
1388 	gmt_M_memcpy (&e[1], &p[i+1], 1, struct EULER);
1389 	spotter_total_to_stages (GMT, e, 2, true, true);	/* Convert total reconstruction poles to forward stage poles */
1390 	gmt_make_rot_matrix (GMT, e[1].lon, e[1].lat, e[1].omega * e[1].duration, R);	/* Get matrix R for main rotation */
1391 	omega = e[1].omega * (t - e[0].t_stop);						/* Compute rotation angle for the partial rotation */
1392 	gmt_make_rot_matrix (GMT, e[0].lon, e[0].lat, omega, dR);			/* Get matrix Dr for the partial rotation */
1393 	spotter_matrix_mult (GMT, R, dR, X);						/* Calculate the combined rotation ,X */
1394 	spotter_matrix_to_pole (GMT, X, lon, lat, w);					/* Convert to rotation parameters lon, lat, w */
1395 }
1396 
spotter_conf_ellipse(struct GMT_CTRL * GMT,double lon,double lat,double t,struct EULER * p,unsigned int np,char flag,bool forward,double out[])1397 bool spotter_conf_ellipse (struct GMT_CTRL *GMT, double lon, double lat, double t, struct EULER *p, unsigned int np, char flag, bool forward, double out[]) {
1398 	/* Given time and rotation parameters, calculate uncertainty in the
1399 	 * reconstructed point in the form of a confidence ellipse.  To follow
1400 	 * the stuff below, it helps to realize that the covariance matrix C that
1401 	 * is stored with each rotation R is for the rotation R which rotates a
1402 	 * point of age t along a chain back to the hotspot.  However, in this
1403 	 * context (the error in a reconstructed point along the chain) we are
1404 	 * actually using the inverse rotation R^t (negative opening angle).  For
1405 	 * that rotation, the covariance matrix is R * cov(r) * R^t.
1406 	 * forward is true if we rotate from past to now and false if we
1407 	 * rotate from now to the past (e.g., move a hotspot up the chain).
1408 	 */
1409 
1410 	unsigned int matrix_dim = 3;
1411 	unsigned int i, j, kk = 3, nrots;
1412 	int k;
1413 	double R[3][3], x[3], y[3], M[3][3], RMt[3][3], Rt[3][3], MRt[3][3], cov[3][3], tmp[3][3], C[9];
1414 	double z_unit_vector[3], EigenValue[3], EigenVector[9], work1[3], work2[3], x_in_plane[3], y_in_plane[3];
1415 	double x_comp, y_comp, w;
1416 
1417 	/* Find the unique rotation in question */
1418 
1419 	for (i = 0, k = GMT_NOTSET; k < 0 && i < np; ++i) if (doubleAlmostEqualZero (p[i].t_start, t))
1420 		k = i;
1421 	if (k == GMT_NOTSET) return (true);	/* Did not match finite rotation time */
1422 
1423 	/* Make M(x), the skew-symmetric matrix needed to compute cov of rotated point */
1424 
1425 	spotter_set_M (GMT, lon, lat, M);
1426 
1427 	w = p[k].omega * p[k].duration;
1428 	if (forward) w = -w;	/* Want the inverse rotation */
1429 	gmt_make_rot_matrix (GMT, p[k].lon, p[k].lat, w, R);
1430 	spotter_matrix_transpose (GMT, Rt, R);			/* Get the transpose of R^t */
1431 	if (!forward) {		/* Rotate the point into the present */
1432 		gmt_M_memcpy (cov, p[k].C, 9, double);	/* The rotation's covarience matrix */
1433 	}
1434 	else {	/* Use inverse rotation to rotate the point from the present to past rotations */
1435 		/* We change the sign of w so then R is actually R^t */
1436 		/* Since we are using the inverse rotation we must first get the cov matrix of the
1437 		   inverse rotation: cov(r^t) = R cov(r) R^t.
1438 		   Here, R actually contains R^t so we need the original R (which we will call R^t) as well. */
1439 
1440 		spotter_matrix_mult (GMT, p[k].C, R, tmp);			/* Calculate the cov(r) *R^t product */
1441 		spotter_matrix_mult (GMT, Rt, tmp, cov);			/* cov(r^t) = R^t cov(r) R */
1442 	}
1443 
1444 	/* Calculate cov(y) = R * M^T * cov_R * M * R^T */
1445 
1446 	spotter_matrix_mult (GMT, M, Rt, MRt);		/* Calculate the M * R^T product */
1447 	spotter_matrix_transpose (GMT, RMt, MRt);		/* Get the transpose (M*R^T)^T = R * M^T */
1448 	spotter_matrix_mult (GMT, cov, MRt, tmp);		/* Get C * M * R^T */
1449 	spotter_matrix_mult (GMT, RMt, tmp, M);		/* Finally get R * M * C * M^T * R^T, store result in M */
1450 
1451 	for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) C[3*i+j] = M[i][j];	/* Reformat to 1-D format for gmt_jacobi */
1452 
1453 	/* Get projected point y = R*x */
1454 
1455 	gmt_geo_to_cart (GMT, lat, lon, x, true);
1456 	for (i = 0; i < 3; i++) y[i] = R[i][0] * x[0] + R[i][1] * x[1] + R[i][2] * x[2];
1457         gmt_cart_to_geo (GMT, &out[1], &out[0], y, true);
1458 	if (flag == 't')
1459 		out[2] = t;
1460 	else if (flag == 'a')
1461 		out[2] = w;
1462 	else
1463 		kk = 2;
1464 
1465 	gmt_jacobi (GMT, C, matrix_dim, matrix_dim, EigenValue, EigenVector, work1, work2, &nrots);	/* Solve eigen-system C = EigenVector * EigenValue * EigenVector^T */
1466 
1467 	z_unit_vector[0] = z_unit_vector[1] = 0.0;	z_unit_vector[2] = 1.0;	/* z unit vector */
1468 	gmt_cross3v (GMT, z_unit_vector, y, x_in_plane);	/* Local x-axis in plane normal to mean pole */
1469 	gmt_cross3v (GMT, y, x_in_plane, y_in_plane);	/* Local y-axis in plane normal to mean pole */
1470 	x_comp = gmt_dot3v (GMT, EigenVector, x_in_plane);	/* x-component of major axis in tangent plane */
1471 	y_comp = gmt_dot3v (GMT, EigenVector, y_in_plane);	/* y-component of major axis in tangent plane */
1472 	out[kk] = fmod (360.0 + (90.0 - atan2d (y_comp, x_comp)), 360.0);	/* Azimuth of major axis */
1473 	if (out[kk] > 180.0) out[kk] -= 180.0;
1474 	out[++kk] = 2.0 * sqrt (EigenValue[0]) * EQ_RAD * SQRT_CHI2;	/* Report full-length major axis (not semi) */
1475 	out[++kk] = 2.0 * sqrt (EigenValue[1]) * EQ_RAD * SQRT_CHI2;	/* Report full-length minor axis (not semi) */
1476 
1477 	return (false);
1478 }
1479 
spotter_matrix_1Dto2D(struct GMT_CTRL * GMT,double * M,double X[3][3])1480 void spotter_matrix_1Dto2D (struct GMT_CTRL *GMT, double *M, double X[3][3]) {
1481 	unsigned int i, j;
1482 	gmt_M_unused(GMT);
1483 	for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) X[i][j] = M[3*i+j];
1484 }
1485 
spotter_matrix_2Dto1D(struct GMT_CTRL * GMT,double * M,double X[3][3])1486 void spotter_matrix_2Dto1D (struct GMT_CTRL *GMT, double *M, double X[3][3]) {
1487 	unsigned int i, j;
1488 	gmt_M_unused(GMT);
1489 	for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) M[3*i+j] = X[i][j];
1490 }
1491 
spotter_inv_cov(struct GMT_CTRL * GMT,double Ci[3][3],double C[3][3])1492 void spotter_inv_cov (struct GMT_CTRL *GMT, double Ci[3][3], double C[3][3]) {
1493 	/* Return the inverse of a covariance matrix
1494 	 * (or any symmetric 3x3 matrix) */
1495 
1496 	double inv_D;
1497 	gmt_M_unused(GMT);
1498 	inv_D = 1.0 / (-C[0][0]*C[1][1]*C[2][2] + C[0][0]*C[1][2]*C[1][2] + C[0][1]*C[0][1]*C[2][2] - 2.0*C[0][1]*C[0][2]*C[1][2] + C[0][2]*C[0][2]*C[1][1]);
1499 	Ci[0][0] = (-C[1][1] * C[2][2] + C[1][2]*C[1][2]) * inv_D;
1500 	Ci[0][1] = Ci[1][0] = (C[0][1]*C[2][2] - C[0][2]*C[1][2]) * inv_D;
1501 	Ci[0][2] = Ci[2][0] = (C[0][2]*C[1][1] - C[0][1]*C[1][2]) * inv_D;
1502 	Ci[1][1] = (C[0][2]*C[0][2] - C[0][0]*C[2][2]) * inv_D;
1503 	Ci[1][2] = Ci[2][1] = (C[0][0]*C[1][2] - C[0][1]*C[0][2]) * inv_D;
1504 	Ci[2][2] = (C[0][1]*C[0][1] - C[0][0]*C[1][1]) * inv_D;
1505 }
1506 
spotter_confregion_radial(struct GMT_CTRL * GMT,double alpha,struct EULER * p,double ** X,double ** Y)1507 unsigned int spotter_confregion_radial (struct GMT_CTRL *GMT, double alpha, struct EULER *p, double **X, double **Y) {
1508 	/* RADIAL PROJECTION */
1509 	/* alpha:	Level of significance, e.g., 0.95 for 95% confidence region */
1510 	/* p:		Euler rotation structure for the current rotation */
1511 	/* X, Y:	Pointers to arrays that will hold the confidence region polygon */
1512 
1513 #if DEBUG
1514 	bool dump = true;
1515 	FILE *fp = NULL;
1516 #endif
1517 	unsigned int i, j, ii, jj, na, try, n, matrix_dim = 3, nrots, fake = 0, axis[3];
1518 	bool done, got_it, bail;
1519 	size_t n_alloc;
1520 	char *name = "uvw";
1521 	double sa, ca, angle, d, V[3][3], Vt[3][3], C[9], fval = 0.0005;
1522 	double EigenValue[3], EigenVector[9], work1[3], work2[3], r_center[3];
1523 	double axis_length[3], i_axis_length[3], r_t[3], delta, b1, b2, num, radix, c2, t1, t2, uvw[3], N[3], N_xyz[3];
1524 	double uvw_prime[3], r_tangent_path[3], s, c, omega, new_angle, r_center_unit[3], r_t_unit[3];
1525 	double phi[SPOTTER_N_FINE_STEPS+1], t[SPOTTER_N_FINE_STEPS+1], t_inner[SPOTTER_N_FINE_STEPS+1], *lon = NULL, *lat = NULL;
1526 
1527 	double nu = 3.0;	/* Three degrees of freedom for the rotation pole */
1528 
1529 #if 0
1530 	if (rot_debug) spotter_confregion_radial_debug (alpha, p);	/* For testing purposes only */
1531 #endif
1532 	c2 = gmt_chi2crit (GMT, alpha, nu);
1533 	c = sqrt (c2);
1534 	gmt_M_memset (phi, SPOTTER_N_FINE_STEPS+1, double);
1535 	gmt_M_memset (t, SPOTTER_N_FINE_STEPS+1, double);
1536 	gmt_M_memset (t_inner, SPOTTER_N_FINE_STEPS+1, double);
1537 
1538 	spotter_matrix_2Dto1D (GMT, C, p->C);			/* Reformat p->C to 1-D format C for gmt_jacobi */
1539 	if (fake) {
1540 		gmt_M_memset (C, 9, double);
1541 		C[0] = C[4] = C[8] = fval;
1542 	}
1543 	gmt_jacobi (GMT, C, matrix_dim, matrix_dim, EigenValue, EigenVector, work1, work2, &nrots);	/* Solve eigen-system C = EigenVector * EigenValue * EigenVector^T */
1544 	spotter_matrix_1Dto2D (GMT, EigenVector, Vt);	/* Reformat back to 2-D format */
1545 	spotter_matrix_transpose (GMT, V, Vt);		/* Get the transpose of V */
1546 	gmt_geo_to_cart (GMT, p->lat_r, p->lon_r, r_center_unit, false);	/* Rotation pseudo-vector */
1547 	omega = p->omega * p->duration;
1548 	for (ii = 0; ii < 3; ii++) {
1549 		r_center[ii] = r_center_unit[ii] * D2R * omega;
1550 		axis_length[ii] = sqrt (EigenValue[ii]);
1551 		i_axis_length[ii] = 1.0 / axis_length[ii];
1552 	}
1553 	gmt_matrix_vect_mult (GMT, 3U, Vt, r_center, r_t);		/* r_center expressed in eigen coordinates */
1554 	gmt_matrix_vect_mult (GMT, 3U, Vt, r_center_unit, r_t_unit);	/* unit vector r_center expressed in eigen coordinates */
1555 
1556 	/* Determine which of u, v, and w unit vectors are most parallel with r_t, then use the two other axes for the
1557 	 * loop over angles. The two horizontal axes are indicated by the indices axis[]GMT_X] and axis[GMT_Y], with
1558 	 * axis[GMT_Z] being the axis most parallel with r_t .*/
1559 
1560 	gmt_M_memset (N, 3, double);	N[0] = 1.0;
1561 	angle = gmt_dot3v (GMT, r_t_unit, N);	axis[GMT_Z] = GMT_X;
1562 	gmt_M_memset (N, 3, double);	N[1] = 1.0;
1563 	new_angle = gmt_dot3v (GMT, r_t_unit, N);
1564 	if (new_angle > angle) {axis[GMT_Z] = GMT_Y; angle = new_angle;}
1565 	gmt_M_memset (N, 3, double);	N[2] = 1.0;
1566 	new_angle = gmt_dot3v (GMT, r_t_unit, N);
1567 	if (new_angle > angle) {axis[GMT_Z] = GMT_Z; angle = new_angle;}
1568 	axis[GMT_X] = (axis[GMT_Z] == 0) ? 1 : 0;	/* u will be either first or second original axis */
1569 	axis[GMT_Y] = axis[GMT_X] + 1;				/* Let v be the next (either second or third) */
1570 	if (axis[GMT_Y] == axis[GMT_Z]) axis[GMT_Y]++;	/* Occupied by w, go to next */
1571 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Spinning in the %c-%c coordinate system\n", name[axis[GMT_X]], name[axis[GMT_Y]]);
1572 
1573 	/* We will loop over 360 degrees and determine where the radial vector intersects the projected ellipse P'(u,v)
1574 	 * (1) If the origin (0,0) is inside P' then we will always get two real roots that differ in sign. in that case
1575 	 * we simply always go with the positive root for all angles.  (2) If origin is outside P'(u,v) then there will
1576 	 * be angles for which the radius vector does not intersect the curve (and we get two complex roots to ignore),
1577 	 * otherwise the real roots come in pairs of the same sign.  Since the negative pairs repeat the information
1578 	 * of the positive pairs (except they are 180 degrees shifted) we only keep the positive pairs.  These two
1579 	 * roots represents two different angles 180 degrees apart so therefore we store both and do the stitching
1580 	 * further down.
1581 	*/
1582 
1583 	na = SPOTTER_N_STEPS;	/* Initial trial */
1584 	done = bail = false;
1585 	try = 0;
1586 	do {
1587 		delta = TWO_PI / na;
1588 		try++;
1589 		for (i = j = 0; i <= na; i++) {
1590 			sincos ((angle = i * delta), &sa, &ca);
1591 			b1 = r_t[axis[GMT_X]] * ca * i_axis_length[axis[GMT_X]] + r_t[axis[GMT_Y]] * sa * i_axis_length[axis[GMT_Y]];
1592 			b2 = r_t[axis[GMT_Z]] * i_axis_length[axis[GMT_Z]];
1593 			num = b1 * b1 + b2 * b2;
1594 			radix = num - c2;
1595 			if (radix < 0.0) continue;	/* Complex roots not interesting here */
1596 			s = b2 * c * sqrt (radix);
1597 			b1 *= c2;
1598 			t1 = (-b1 + s) / num;
1599 			t2 = (-b1 - s) / num;
1600 			/* OK, determine a valid series of t(theta) */
1601 			if ((t1*t2) < 0.0) {    /* Easy, just do the root with positive t */
1602 				t[j] = MAX (t1,t2);		/* Only pick the positive solution */
1603   				phi[j] = angle;
1604 				j++;
1605 				done = true;	/* No need to redo at finer spacing */
1606 			}
1607 			else if (na == SPOTTER_N_STEPS) {	/* Only gets here in case 2 */
1608 				/* We get here immediately and j = 0.  Break out and redo with more points to handle grazing angles */
1609 				na = SPOTTER_N_FINE_STEPS;
1610 				break;
1611 			}
1612 			else if (t1 >= 0.0) {	/* Two positive roots, we have inner/outer branch */
1613 				t[j] = MAX (t1,t2);		/* Pick the largest for outer branch */
1614   				phi[j] = angle;
1615 				t_inner[j] = MIN(t1,t2);	/* Pick the smallest for inner branch */
1616 				j++;
1617 				done = true;	/* Since we now are using the finer spacing */
1618 			}
1619 		}
1620 		if (j == 0 && try == 2) {	/* Found no roots at all - bail */
1621 			done = bail = true;
1622 		}
1623 	} while (!done);
1624 	if (bail) {
1625 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Could not obtain confidence regions for rotation at t = %.2f\n", p->t_start);
1626 		return (0);
1627 	}
1628 	if (na == SPOTTER_N_FINE_STEPS) {	/* Two branches, stitch together */
1629 		i = j;
1630 		while (j > 0) {	/* Wind backwards to reverse t_inner and append to t */
1631 			j--;
1632 			t[i] = t_inner[j];
1633 			phi[i] = phi[j];
1634 			i++;
1635 		}
1636 		na = i;
1637 	}
1638 	/* Here we have a valid t(phi) relationship.  Build output arrays lon,lat */
1639 
1640 	n_alloc = na;
1641 	lon = gmt_M_memory (GMT, NULL, n_alloc, double);
1642 	lat = gmt_M_memory (GMT, NULL, n_alloc, double);
1643 	n = 0;
1644 #if DEBUG
1645 	if (dump) fp = fopen ("dump_r.txt","w");
1646 #endif
1647 	for (i = 0; i < na; i++) {
1648 		sincos (phi[i], &sa, &ca);
1649 		uvw[axis[GMT_X]] = axis_length[axis[GMT_X]] * ca * t[i];
1650 		uvw[axis[GMT_Y]] = axis_length[axis[GMT_Y]] * sa * t[i];
1651 		uvw[axis[GMT_Z]] = axis_length[axis[GMT_Z]] * sqrt (c2 - t[i]*t[i]);
1652 #if DEBUG
1653 		if (dump) fprintf (fp, "%g\t%g\t%g\n", uvw[0], uvw[1], uvw[2]);
1654 #endif
1655 		got_it = false;
1656 		try = 0;
1657 		while (!got_it && try < 2) {
1658 			try++;
1659 			uvw[axis[GMT_Z]] = -uvw[axis[GMT_Z]];	/* Try the opposite sign of last time */
1660 			spotter_ellipsoid_normal (GMT, uvw, axis_length, c, N);	/* Get normal vector */
1661 			gmt_matrix_vect_mult (GMT, 3U, V, N, N_xyz);		/* Upper normal in (x,y,z) coordinates */
1662 			gmt_matrix_vect_mult (GMT, 3U, V, uvw, uvw_prime);	/* potential tangent point in (x,y,z) coordinates */
1663 			for (jj = 0; jj < 3; jj++) r_tangent_path[jj] = uvw_prime[jj] + r_center[jj];	/* r to (upper) tangent path in (x,y,z) coordinates */
1664 			d = fabs (gmt_dot3v (GMT, N_xyz, r_tangent_path));
1665 			got_it = (d < SPOTTER_D_CUT);	/* The surface normal at (u,v,+w) is normal to the position vector */
1666 		}
1667 		if (got_it) {	/* Got a valid point, now compute geographical coordinates */
1668 			gmt_normalize3v (GMT, r_tangent_path);
1669 			gmt_cart_to_geo (GMT, &lat[n], &lon[n], r_tangent_path, true);
1670 			n++;
1671 			if (n == n_alloc) {
1672 				n_alloc <<= 1;
1673 				lon = gmt_M_memory (GMT, lon, n_alloc, double);
1674 				lat = gmt_M_memory (GMT, lat, n_alloc, double);
1675 			}
1676 		}
1677 		else {
1678 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "No (u,v,w) solution for angle %g\n", phi[i]);
1679 		}
1680 	}
1681 #ifdef DEBUG
1682 	if (dump) fclose (fp);
1683 #endif
1684 	if (gmt_polygon_is_open (GMT, lon, lat, n)) {	/* Must close the polygon */
1685 		lon[n] = lon[0];
1686 		lat[n] = lat[1];
1687 		n++;
1688 	}
1689 	if (n < n_alloc) {
1690 		lon = gmt_M_memory (GMT, lon, n, double);
1691 		lat = gmt_M_memory (GMT, lat, n, double);
1692 	}
1693 
1694 	*X = lon;
1695 	*Y = lat;
1696 	return (n);
1697 }
1698 
spotter_confregion_ortho(struct GMT_CTRL * GMT,double alpha,struct EULER * p,double ** X,double ** Y)1699 unsigned int spotter_confregion_ortho (struct GMT_CTRL *GMT, double alpha, struct EULER *p, double **X, double **Y) {
1700 	/* ORTHOGRAPHIC PROJECTION */
1701 	/* alpha:	Level of significance, e.g., 0.95 for 95% confidence region */
1702 	/* p:		Euler rotation structure for the current rotation */
1703 	/* X, Y:	Pointers to arrays that will hold the confidence region polygon */
1704 
1705 	unsigned int i;
1706 #ifdef DEBUG
1707 	bool dump = true;
1708 	FILE *fp = NULL;
1709 #endif
1710 	double sa, ca, angle = 0.0, R[3][3], Rt[3][3], T[3][3], C[3][3];
1711 	double par[3], delta, mu, dx_local, dy_local, dr_local, azimuth, dr_dist, sin_phi, cos_phi;
1712 	double *lon = NULL, *lat = NULL;
1713 
1714 	double nu = 3.0;	/* Three degrees of freedom for the rotation pole */
1715 
1716 	mu = sqrt (gmt_chi2crit (GMT, alpha, nu));	/* Scaling of all axes */
1717 	spotter_tangentplane (GMT, p->lon, p->lat, R);	/* Rotation that relates tangent plane coordinates (p,q,s) with Earth (x,y,z) */
1718 	spotter_matrix_transpose (GMT, Rt, R);				/* Get the transpose of R */
1719 	spotter_matrix_mult (GMT, R, p->C, T);				/* Compute C' = R * C * Rt */
1720 	spotter_matrix_mult (GMT, T, Rt, C);
1721 	spotter_project_ellipsoid_new (GMT, C, par);	/* Get projection of ellipsoid onto tangent plane */
1722 	sincosd (par[0], &sin_phi, &cos_phi);
1723 
1724 #ifdef DEBUG
1725 	if (dump) fp = fopen ("dump_o.txt","w");
1726 #endif
1727 	delta = 360.0 / (SPOTTER_N_STEPS-1);
1728 	lon = gmt_M_memory (GMT, NULL, SPOTTER_N_STEPS, double);
1729 	lat = gmt_M_memory (GMT, NULL, SPOTTER_N_STEPS, double);
1730 	mu /= (p->duration * p->omega * D2R);	/* Scale up so vector touches the Earth's surface (r = 1) */
1731 	for (i = 0; i < SPOTTER_N_STEPS; i++) {
1732 		sincosd ((angle = i * delta), &sa, &ca);
1733 		dx_local = mu * (par[1] * ca * cos_phi - par[2] * sa * sin_phi);
1734 		dy_local = mu * (par[2] * sa * cos_phi + par[1] * ca * sin_phi);
1735 		dr_local  = hypot (dx_local, dy_local);
1736 		dr_dist = R2D * d_asin (dr_local);	/* Undo orthographic projection to get great-circle distance */
1737 		azimuth = atan2 (dy_local, dx_local) * R2D;
1738 		/* Determine lon,lat of a point dr_dist away in the azim direction from center lon,lat */
1739 		gmtlib_get_point_from_r_az (GMT, p->lon, p->lat, dr_dist, azimuth, &lon[i], &lat[i]);
1740 #ifdef DEBUG
1741 		if (dump) fprintf (fp, "%g\t%g\t%g\t%g\t%g\t%g\n", dx_local, dy_local, dr_dist, azimuth, lon[i], lat[i]);
1742 #endif
1743 	}
1744 #ifdef DEBUG
1745 	if (dump) fclose (fp);
1746 #endif
1747 
1748 	*X = lon;
1749 	*Y = lat;
1750 	return (i);
1751 }
1752 
spotter_project_ellipsoid(struct GMT_CTRL * GMT,double axis[],double D[3][3],double * par)1753 void spotter_project_ellipsoid (struct GMT_CTRL *GMT, double axis[], double D[3][3], double *par) {
1754 	/* Project an arbitrarily oriented ellipsoid orthographically onto a plane
1755 	 * using the method of Gendzwill and Stauffer, 1981, "Analysis of triaxial
1756 	 * ellipsoids: Their shapes, plane sections, and plane projections", Math.
1757 	 * Geol., 13 (2), 135-152.
1758 	 * axes: The three axes lengths (eigenvalues)
1759 	 * D: The rotation matrix that relates the (J, K, L) coordinates of the
1760 	 * ellipsoid to the E, N, V coordinates of the real world.
1761 	 * par: Returns azimuth, major, minor axes in the E-N plane.
1762 	 * Note: I rederived to project onto E-N rather than E-V.
1763 	 */
1764 #ifdef DEBUG
1765 	bool override = false;
1766 	double tmp[3][3];
1767 #endif
1768 	double A, B, C, F, G, H, a2, b2, c2, r;
1769 	gmt_M_unused(GMT);
1770 
1771 #ifdef DEBUG
1772 	if (override) {
1773 		spotter_matrix_transpose (GMT, tmp, D);
1774 		gmt_M_memcpy (D, tmp, 9, double);
1775 	}
1776 #endif
1777 
1778 	/* Get square of each axis */
1779 
1780 	a2 = axis[0] * axis[0];	b2 = axis[1] * axis[1];	c2 = axis[2] * axis[2];
1781 
1782 	/* Get F, G, H, per equation (20) */
1783 
1784 	F = D[0][0] * D[0][2] / a2 + D[1][0] * D[1][2] / b2 + D[2][0] * D[2][2] / c2;
1785 	G = D[0][1] * D[0][2] / a2 + D[1][1] * D[1][2] / b2 + D[2][1] * D[2][2] / c2;
1786 	H = D[0][2] * D[0][2] / a2 + D[1][2] * D[1][2] / b2 + D[2][2] * D[2][2] / c2;
1787 
1788 	/* Then get A, B, C per equation (23) */
1789 
1790 	A = pow (D[0][0] - D[0][2] * F / H, 2.0) / a2 + pow (D[1][0] - D[1][2] * F / H, 2.0) / b2 + pow (D[2][0] - D[2][2] * F / H, 2.0) / c2;
1791 	B = 2.0 * (D[0][0] - D[0][2] * F / H) * (D[0][1] - D[0][2] * G / H) / a2 +
1792 	    2.0 * (D[1][0] - D[1][2] * F / H) * (D[1][1] - D[1][2] * G / H) / b2 +
1793 		2.0 * (D[2][0] - D[2][2] * F / H) * (D[2][1] - D[2][2] * G / H) / c2;
1794 	C = pow (D[0][1] - D[0][2] * G / H, 2.0) / a2 + pow (D[1][1] - D[1][2] * G / H, 2.0) / b2 + pow (D[2][1] - D[2][2] * G / H, 2.0) / c2;
1795 
1796 	r = sqrt (A*A - 2*A*C + C*C + 4*B*B);
1797 	par[1] = 1.0/sqrt (0.5 * (A + C + r));
1798 	par[2] = 1.0/sqrt (0.5 * (A + C - r));
1799 	par[0] = (gmt_M_is_zero (B)) ? ((A > C) ? 90.0 : 0.0) : 90.0 - atan2 (-0.5 * (A - C - r)/B, 1.0) * R2D;
1800 	if (par[2] > par[1]) {	/* Switch so that par[1] is the major axis */
1801 		gmt_M_double_swap (par[1], par[2]);
1802 		par[0] += 90.0;
1803 		if (par[0] >= 180.0) par[0] -= 180.0;
1804 	}
1805 }
1806 
spotter_project_ellipsoid_new(struct GMT_CTRL * GMT,double X[3][3],double * par)1807 void spotter_project_ellipsoid_new (struct GMT_CTRL *GMT, double X[3][3], double *par) {
1808 	/* Project an arbitrarily oriented ellipsoid orthographically onto the x-y plane
1809 	 */
1810 	double a, b, c, r;
1811 	gmt_M_unused(GMT);
1812 
1813 	a = X[0][0] - (X[0][2] * X[0][2] / X[2][2]);
1814 	b = X[0][1] - (X[0][2] * X[1][2] / X[2][2]);
1815 	c = X[1][1] - (X[1][2] * X[1][2] / X[2][2]);
1816 	r = sqrt (a*a - 2.0*a*c + c*c + 4.0*b*b);
1817 	par[1] = sqrt (0.5 * (a + c + r));
1818 	par[2] = sqrt (0.5 * (a + c - r));
1819 	par[0] = (gmt_M_is_zero (b)) ? ((a > c) ? 90.0 : 0.0) : 90.0 - atan2 (-0.5 * (a - c - r)/b, 1.0) * R2D;
1820 	if (par[2] > par[1]) {	/* Switch so that par[1] is the major axis */
1821 		gmt_M_double_swap (par[1], par[2]);
1822 		par[0] += 90.0;
1823 		if (par[0] >= 180.0) par[0] -= 180.0;
1824 	}
1825 }
1826 
spotter_tangentplane(struct GMT_CTRL * GMT,double lon,double lat,double R[3][3])1827 void spotter_tangentplane (struct GMT_CTRL *GMT, double lon, double lat, double R[3][3]) {
1828 	/* Returns the rotation R that will relate the Earth X = (x,y,z)
1829 	 * coordinates to the local tangent plane cooardinates T = (tx,ty,tz)
1830 	 * at the point (lon,lat).  Here tx is horizontal (east) coordinate
1831 	 * ty is vertical (north) and tz is radially up.  Usage:
1832 	 *    T = R * X
1833 	 */
1834 
1835 	double sa, ca, Rlat[3][3], Rlon[3][3];
1836 
1837 	sincosd (lat, &sa, &ca);
1838 	Rlat[0][0] = 1.0;	Rlat[0][1] = 0.0;	Rlat[0][2] = 0.0;
1839 	Rlat[1][0] = 0.0;	Rlat[1][1] = -sa;	Rlat[1][2] = ca;
1840 	Rlat[2][0] = 0.0;	Rlat[2][1] = ca;	Rlat[2][2] = sa;
1841 	sincosd (lon, &sa, &ca);
1842 	Rlon[0][0] = -sa;	Rlon[0][1] = ca;	Rlon[0][2] = 0.0;
1843 	Rlon[1][0] = ca;	Rlon[1][1] = sa;	Rlon[1][2] = 0.0;
1844 	Rlon[2][0] = 0.0;	Rlon[2][1] = 0.0;	Rlon[2][2] = 1.0;
1845 	spotter_matrix_mult (GMT, Rlat, Rlon, R);			/* R converts between (x,y,z) and( tx, ty, tz) coordinates in tangent plane */
1846 }
1847 
spotter_on_the_ellipse(double xyz[3],double L[3],double c)1848 GMT_LOCAL bool spotter_on_the_ellipse (double xyz[3], double L[3], double c) {
1849 	unsigned int i;
1850 	double sum;
1851 	sum = c * c;
1852 	for (i = 0; i < 3; i++) sum -= pow (xyz[i]/L[i],2.0);
1853 	return (gmt_M_is_zero (sum));
1854 }
1855 
spotter_ellipsoid_normal(struct GMT_CTRL * GMT,double X[3],double L[3],double c,double N[3])1856 void spotter_ellipsoid_normal (struct GMT_CTRL *GMT, double X[3], double L[3], double c, double N[3]) {
1857 	/* Compute normal vector N at given point X ON the ellipse */
1858 	if (!spotter_on_the_ellipse (X, L, c)) {
1859 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Point X is not on the ellipsoid in ellipsoid_normal!");
1860 		return;
1861 	}
1862 	if (gmt_M_is_zero (X[GMT_Z])) {	/* Normal vector lies entirely in (x-y) plane */
1863 		if (gmt_M_is_zero (X[GMT_Y])) {	/* Special case where N is aligned with x-axis */
1864 			N[GMT_X] = copysign (1.0, X[GMT_X]);	/* Pointing in the sign(x) direction */
1865 			N[GMT_Y] = N[GMT_Z] = 0.0;
1866 		}
1867 		else {	/* May compute dy/dx to get tx = (1, dy/dx) so n = (dydx,1,0) with sign taken from quadrants */
1868 			N[GMT_X] = copysign (fabs (L[GMT_Y]*L[GMT_Y]*X[GMT_X]/(L[GMT_X]*L[GMT_X]*X[GMT_Y])), X[GMT_X]);
1869 			N[GMT_Y] = copysign (1.0, X[GMT_Y]);
1870 			N[GMT_Z] = 0.0;
1871 		}
1872 	}
1873 	else {	/* Straight forward solution */
1874 		double L2, tx[3], ty[3];
1875 		tx[GMT_X] = 1.0;
1876 		tx[GMT_Y] = 0.0;
1877 		L2 = L[GMT_Z]*L[GMT_Z];
1878 		tx[GMT_Z] = -L2*X[GMT_X]/(L[GMT_X]*L[GMT_X]*X[GMT_Z]);
1879 		ty[GMT_X] = 0.0;
1880 		ty[GMT_Y] = 1.0;
1881 		ty[GMT_Z] = -L2*X[GMT_Y]/(L[GMT_Y]*L[GMT_Y]*X[GMT_Z]);
1882 		gmt_cross3v (GMT, tx, ty, N);	/* Normal is cross-product of x and y tangent vectors */
1883 	}
1884 }
1885