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