1 /*--------------------------------------------------------------------
2  *
3  *	Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4  *	See LICENSE.TXT file for copying and redistribution conditions.
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU Lesser General Public License as published by
8  *	the Free Software Foundation; version 3 or any later version.
9  *
10  *	This program is distributed in the hope that it will be useful,
11  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *	GNU Lesser General Public License for more details.
14  *
15  *	Contact info: www.generic-mapping-tools.org
16  *--------------------------------------------------------------------*/
17 /*
18  * Author:	G. Patau, IGPG, w/ Kurt Feigl
19  * Date:	1-JAN-2010
20  * Version:	6 API
21  *    Copyright (c) 1996-2012 by G. Patau, then donated to the GMT project
22  *    by G. Patau upon her retirement from IGPG
23  *
24  * Roots:		based on psxy.c
25  * Adapted to version 3.3 by Genevieve Patau (25 June 1999)
26  * Last modified : 18 February 2000.  Ported to GMT 5 by P. Wessel in 2013.
27  * Updated 3 March 2021 to add color enhancements by P. Wessel
28  *
29  * Brief synopsis: psvelo will read coordinates and plot geodetic symbols
30  * such as velocity ellipses, strain crosses, or strain wedges on maps.
31  */
32 
33 #include "gmt_dev.h"
34 
35 #define THIS_MODULE_CLASSIC_NAME	"psvelo"
36 #define THIS_MODULE_MODERN_NAME	"velo"
37 #define THIS_MODULE_LIB		"geodesy"
38 #define THIS_MODULE_PURPOSE	"Plot velocity vectors, crosses, anisotropy bars and wedges"
39 #define THIS_MODULE_KEYS	"<D{,>X}"
40 #define THIS_MODULE_NEEDS	"Jd"
41 #define THIS_MODULE_OPTIONS "-:>BJKOPRUVXYdehipqt" GMT_OPT("c")
42 
43 #define CINE 1
44 #define ANISO 2
45 #define WEDGE 3
46 #define CROSS 4
47 
48 #define DEFAULT_FONTSIZE	9.0	/* In points */
49 
50 #define READ_ELLIPSE	0
51 #define READ_ROTELLIPSE	1
52 #define READ_ANISOTROPY	2
53 #define READ_WEDGE	4
54 #define READ_CROSS	8
55 
56 /* parameters for writing text */
57 #define ANGLE		0.0
58 #define FORM		0
59 
60 /* Control structure for psvelo */
61 
62 struct PSVELO_CTRL {
63 	struct PSVELO_A {	/* -A */
64 		bool active;
65 		struct GMT_SYMBOL S;
66 	} A;
67 	struct PSVELO_C {	/* -C<cpt> */
68 		bool active;
69 		char *file;
70 	} C;
71 	struct PSVELO_D {	/* -D */
72 		bool active;
73 		double scale;
74 	} D;
75  	struct PSVELO_E {	/* -E<fill> */
76 		bool active;
77 		struct GMT_FILL fill;
78 	} E;
79  	struct PSVELO_G {	/* -G<fill> */
80 		bool active;
81 		struct GMT_FILL fill;
82 	} G;
83 	struct PSVELO_H {	/* -H read overall scaling factor for symbol size and pen width */
84 		bool active;
85 		unsigned int mode;
86 		double value;
87 	} H;
88 	struct PSVELO_I {	/* -I[<intensity>] */
89 		bool active;
90 		unsigned int mode;	/* 0 if constant, 1 if read from file */
91 		double value;
92 	} I;
93 	struct PSVELO_L {	/* -L[<pen>] */
94 		bool active;
95 		bool error_pen;
96 		struct GMT_PEN pen;
97 	} L;
98 	struct PSVELO_N {	/* -N */
99 		bool active;
100 	} N;
101 	struct PSVELO_S {	/* -S<symbol> */
102 		bool active;
103 		bool read;	/* True if no scale given; must be first column after the required ones */
104 		int symbol;
105 		unsigned int readmode;
106 		unsigned int n_cols;
107 		double scale, wedge_amp, conrad;
108 		double confidence;
109 		struct GMT_FILL fill;
110 		struct GMT_FONT font;
111 	} S;
112 	struct PSVELO_W {	/* -W<pen> */
113 		bool active;
114 		struct GMT_PEN pen;
115 	} W;
116 	struct PSVELO_Z {	/* -Z */
117 		bool active;
118 		unsigned int mode;
119 		unsigned int item;
120 	} Z;
121 };
122 
123 enum Psvelo_scaletype {
124 	PSVELO_READ_SCALE	= 0,
125 	PSVELO_CONST_SCALE	= 1};
126 
127 enum psvelo_types {
128 	PSVELO_G_FILL = 0,
129 	PSVELO_E_FILL = 1,
130 	PSVELO_V_MAG	= 0,
131 	PSVELO_V_EAST,
132 	PSVELO_V_NORTH,
133 	PSVELO_R_MAG,
134 	PSVELO_V_USER
135 };
136 
137 /* Content of old utilvelo.c is here */
138 
139 #define squared(x) ((x) * (x))
140 #define EPSIL 0.0001
141 
142 /************************************************************************/
psvelo_get_trans(struct GMT_CTRL * GMT,double slon,double slat,double * t11,double * t12,double * t21,double * t22)143 GMT_LOCAL void psvelo_get_trans (struct GMT_CTRL *GMT, double slon, double slat, double *t11, double *t12, double *t21, double *t22) {
144 	/* determine local transformation between (lon,lat) and (x,y) */
145 	/* return this in the 2 x 2 matrix t */
146 	/* this is useful for drawing velocity vectors in X,Y coordinates */
147 	/* even on a map which is not a Cartesian projection */
148 
149  	/* Kurt Feigl, from code by T. Herring */
150 
151 	/* INPUT */
152 	/*   slat        - latitude, in degrees  */
153 	/*   slon        - longitude in degrees  */
154 
155 	/* OUTPUT (returned) */
156 	/*   t11,t12,t21,t22 transformation matrix */
157 	/* COMMENT BY PW: Fails as provided if slat > 89.0 and for projection that
158 	 * gives the same x-coordinates for two different longitudes, as might happen
159 	 * at the N or S pole.  Some minor protections were added below to handle this.
160 	 */
161 
162 	/* LOCAL VARIABLES */
163 	double su, sv, udlat, vdlat, udlon, vdlon, dudlat, dvdlat, dudlon, dvdlon, dl;
164 	int flip = 0;
165 
166 	/* how much does x,y change for a 1 degree change in lon,lon ? */
167 	gmt_geo_to_xy (GMT, slon,     slat,     &su,    &sv );
168 	if ((slat+1.0) >= 90.0) {	/* PW: Must do something different at/near NP */
169 	        gmt_geo_to_xy (GMT, slon,     slat-1.0, &udlat, &vdlat);
170 		flip = 1;
171 	}
172 	else
173 		gmt_geo_to_xy (GMT, slon,     slat+1.0, &udlat, &vdlat);
174 	gmt_geo_to_xy (GMT, slon+1.0, slat    , &udlon, &vdlon);
175 
176 	/* Compute dudlat, dudlon, dvdlat, dvdlon */
177 	dudlat = udlat - su;
178 	dvdlat = vdlat - sv;
179 	dudlon = udlon - su;
180 	dvdlon = vdlon - sv;
181 	if (flip) {	/* Fix what we did above */
182 		dudlat = -dudlat;
183 		dvdlat = -dvdlat;
184 	}
185 
186 	/* Make unit vectors for the long (e/x) and lat (n/y) */
187 	/* to construct local transformation matrix */
188 
189 	dl = sqrt (dudlon*dudlon + dvdlon*dvdlon);
190 	*t11 = (dl == 0.0) ? 0.0 : dudlon/dl;
191 	*t21 = (dl == 0.0) ? 0.0 : dvdlon/dl;
192 
193 	dl = sqrt (dudlat*dudlat + dvdlat*dvdlat);
194 	*t12 = (dl == 0.0) ? 0.0 : dudlat/dl;
195 	*t22 = (dl == 0.0) ? 0.0 : dvdlat/dl;
196 }
197 
psvelo_transform_local(double x0,double y0,double dxp,double dyp,double scale,double t11,double t12,double t21,double t22,double * x1,double * y1)198 GMT_LOCAL void psvelo_transform_local (double x0, double y0, double dxp, double dyp, double scale, double t11, double t12, double t21, double t22, double *x1, double *y1) {
199 	/* perform local transformation on offsets (dxp,dyp) from */
200 	/* "origin point" x0,y0 given transformation matrix T */
201 
202 	/* Kurt Feigl, from code by T. Herring */
203 
204 	/* INPUT */
205 	/*   x0,y0       - dxp,dyp with respect to this point */
206 	/*   dxp         - x component of arrow */
207 	/*   dyp         - y component of arrow */
208 	/*   scale       - scaling for arrow    */
209 	/*   t11,t12,t21,t22 transformation matrix */
210 
211 	/* OUTPUT (returned) */
212 	/*   x1,y1       - paper coordinates of arrow tail */
213 
214 	/* LOCAL VARIABLES */
215 	double du, dv;
216 
217 	/* perform local transformation */
218 	du = scale * (t11*dxp + t12*dyp);
219 	dv = scale * (t21*dxp + t22*dyp);
220 
221 	/*  Now add to origin  and return values */
222 	*x1 = x0 + du;
223 	*y1 = y0 + dv;
224 
225 }
226 
psvelo_trace_arrow(struct GMT_CTRL * GMT,double slon,double slat,double dxp,double dyp,double scale,double * x1,double * y1,double * x2,double * y2)227 GMT_LOCAL void psvelo_trace_arrow (struct GMT_CTRL *GMT, double slon, double slat, double dxp, double dyp, double scale, double *x1, double *y1, double *x2, double *y2) {
228 	/* convert a vector arrow (delx,dely) arrow from (lat,lon) */
229 
230 	/* Kurt Feigl, from code by T. Herring */
231 
232 	/* INPUT */
233 	/*   slat        - latitude, in degrees of arrow tail */
234 	/*   slon        - longitude in degrees of arrow tail */
235 	/*   dxp         - x component of arrow */
236 	/*   dyp         - y component of arrow */
237 	/*   scale       - scaling for arrow    */
238 
239 	/* OUTPUT (returned) */
240 	/*   x1,y1       - paper coordinates of arrow tail */
241 	/*   x2,y2       - paper coordinates of arrow head */
242 
243 	/* local */
244 	double t11, t12, t21, t22, xt, yt;
245 
246 	/* determine local transformation between (lon, lat) and (x, y) */
247 	/* return this in the 2 x 2 matrix t */
248 	psvelo_get_trans (GMT, slon, slat, &t11, &t12, &t21, &t22);
249 
250 	/* map start of arrow from lat, lon to x, y */
251 	gmt_geo_to_xy (GMT, slon, slat, &xt, &yt);
252 
253 	/* perform the transformation */
254 	psvelo_transform_local (xt, yt, dxp, dyp, scale, t11, t12, t21, t22, x2, y2);
255 
256 	/* return values */
257 
258 	*x1 = xt;
259 	*y1 = yt;
260 }
261 
psvelo_trace_ellipse(double angle,double major,double minor,int npoints,double * x,double * y)262 GMT_LOCAL void psvelo_trace_ellipse (double angle, double major, double minor, int npoints, double *x, double *y) {
263 	/* Given specs for an ellipse, return it in x,y */
264 	double phi = 0.0, sd, cd, s, c;
265 	int i;
266 
267 	sincosd (angle, &sd, &cd);
268 
269 	for (i = 0; i < 360; i++) {
270 		sincos (phi, &s, &c);
271 		*x++ = major * c * cd - minor * s * sd;
272 		*y++ = major * c * sd + minor * s * cd;
273 		phi += M_PI*2.0/(npoints-2);
274 	}
275 }
276 
psvelo_ellipse_convert(double sigx,double sigy,double rho,double conrad,double * eigen1,double * eigen2,double * ang)277 GMT_LOCAL void psvelo_ellipse_convert (double sigx, double sigy, double rho, double conrad, double *eigen1, double *eigen2, double *ang) {
278 	/* convert from one parameterization of an ellipse to another
279 
280 	 * Kurt Feigl, from code by T. Herring
281 
282 	 * INPUT
283 	 *   sigx, sigy  - Sigmas in the x and y directions.
284 	 *   rho         - Correlation coefficient between x and y
285 
286 	 * OUTPUT (returned)
287 	 *   eigen1      - the smaller eigenvalue
288 	 *   eigen2      - the larger eigenvalue
289 	 *   ang         - Orientation of ellipse relative to X axis in radians
290 	 *               - should be counter-clockwise from X axis
291 
292 	 * LOCAL VARIABLES
293 
294 	 *   a,b,c,d,e   - Constants used in getting eigenvalues
295 	 *   conrad      - Radius for the confidence interval
296 	 */
297 
298 	double a, b, c, d, e;
299 
300 	/* confidence scaling */
301 	/*   confid      - Confidence interval wanted (0-1) */
302 	/* conrad = sqrt( -2.0 * log(1.0 - confid)); */
303 
304 	/* the formulas for this part may be found in Bomford, p. 719 */
305 
306 	a = squared (sigy*sigy - sigx*sigx);
307 	b = 4. * squared (rho*sigx*sigy);
308 	c = squared (sigx) + squared (sigy);
309 
310 	/* minimum eigenvector (semi-minor axis) */
311 	*eigen1 = conrad * sqrt ((c - sqrt(a + b))/2.0);
312 
313 	/* maximu eigenvector (semi-major axis) */
314 	*eigen2 = conrad * sqrt ((c + sqrt(a + b))/2.0);
315 
316 	d = 2. * rho * sigx * sigy;
317 	e = squared (sigx) - squared (sigy);
318 
319 	*ang = atan2 (d, e)/2.0;
320 
321 	/*    that is all */
322 }
323 
psvelo_paint_ellipse(struct GMT_CTRL * GMT,double x0,double y0,double angle,double major,double minor,double scale,double t11,double t12,double t21,double t22,int polygon,struct GMT_FILL * fill,int outline)324 GMT_LOCAL void psvelo_paint_ellipse (struct GMT_CTRL *GMT, double x0, double y0, double angle, double major, double minor, double scale, double t11, double t12, double t21, double t22, int polygon, struct GMT_FILL *fill, int outline) {
325 	/* Make an ellipse at center x0,y0  */
326 #define NPOINTS_ELLIPSE 362
327 
328 	int npoints = NPOINTS_ELLIPSE, i;
329 	/* relative to center of ellipse */
330 	double dxe[NPOINTS_ELLIPSE],dye[NPOINTS_ELLIPSE];
331 	/* absolute paper coordinates */
332 	double axe[NPOINTS_ELLIPSE],aye[NPOINTS_ELLIPSE];
333 
334 	psvelo_trace_ellipse (angle, major, minor, npoints, dxe, dye);
335 
336 	for (i = 0; i < npoints - 2; i++) psvelo_transform_local (x0, y0, dxe[i], dye[i], scale, t11, t12, t21, t22, &axe[i], &aye[i]);
337 	if (polygon) {
338 		gmt_setfill (GMT, fill, outline);
339 		PSL_plotpolygon (GMT->PSL, axe, aye, npoints - 2);
340 	}
341 	else
342 		PSL_plotline (GMT->PSL, axe, aye, npoints - 2, PSL_MOVE|PSL_STROKE|PSL_CLOSE);
343 }
344 
345 /************************************************************************/
psvelo_trace_cross(struct GMT_CTRL * GMT,double slon,double slat,double eps1,double eps2,double theta,double sscale,double v_width,double h_length,double h_width,double vector_shape,int outline,struct GMT_PEN * pen)346 GMT_LOCAL int psvelo_trace_cross (struct GMT_CTRL *GMT, double slon, double slat, double eps1, double eps2, double theta, double sscale, double v_width, double h_length, double h_width, double vector_shape, int outline, struct GMT_PEN *pen) {
347 	/* make a Strain rate cross at(slat,slon) */
348 
349 	/* Kurt Feigl, from code by D. Dong */
350 
351 	/*   INPUT VARIABLES: */
352 	/*   slat        - latitude, in degrees of arrow tail */
353 	/*   slon        - longitude in degrees of arrow tail */
354 	/*   sscale      : scaling factor for size of cloverleaf */
355 	/*   theta       : azimuth of more compressive eigenvector (deg) */
356 	/*   eps1,eps2   : eigenvalues of strain rate (1/yr) */
357 	/*   v_width, h_length,h_width,vector_shape: arrow characteristics */
358 
359 	/* local */
360 	double dx, dy, x1, x2, y1, y2, hl, hw, vw, s, c, dim[PSL_MAX_DIMS];
361 	gmt_M_unused(outline);
362 
363 	gmt_M_memset (dim, PSL_MAX_DIMS, double);
364 	gmt_setpen (GMT, pen);			/* Pen for segment line */
365 	PSL_setfill (GMT->PSL, pen->rgb, 0);	/* Same color for arrow head fill with no outline */
366 	sincosd (theta, &s, &c);
367 
368 	/*  extension component */
369 	dx =  eps1 * c;
370 	dy = -eps1 * s;
371 
372 	/* arrow is outward from slat,slon */
373 	psvelo_trace_arrow (GMT, slon, slat, dx, dy, sscale, &x1, &y1, &x2, &y2);
374 
375 	if (eps1 < 0.0) {
376 		gmt_M_double_swap (x1, x2);
377 		gmt_M_double_swap (y1, y2);
378 	}
379 
380 	if (hypot (x1-x2,y1-y2) <= 1.5 * h_length) {
381 		hl = hypot (x1-x2, y1-y2) * 0.6;
382 		hw = hl * h_width / h_length;
383 		vw = hl * v_width / h_length;
384 		if (vw < 2.0/PSL_DOTS_PER_INCH) vw = 2.0/PSL_DOTS_PER_INCH;
385 	}
386 	else {
387 		hw = h_width;
388 		hl = h_length;
389 		vw = v_width;
390 	}
391 
392 	dim[PSL_VEC_XTIP] = x2;
393 	dim[PSL_VEC_YTIP] = y2;
394 	dim[PSL_VEC_TAIL_WIDTH] = vw;
395 	dim[PSL_VEC_HEAD_LENGTH] = hl;
396 	dim[PSL_VEC_HEAD_WIDTH] = hw;
397 	dim[PSL_VEC_HEAD_SHAPE] = vector_shape, dim[PSL_VEC_STATUS] = PSL_VEC_END | PSL_VEC_FILL;
398 	PSL_plotsymbol (GMT->PSL, x1, y1, dim, PSL_VECTOR);
399 
400 	/* second, extensional arrow in opposite direction */
401 
402 	psvelo_trace_arrow (GMT, slon, slat, -dx, -dy, sscale, &x1, &y1, &x2, &y2);
403 
404 	if (eps1 < 0.0) {
405 		gmt_M_double_swap (x1, x2);
406 		gmt_M_double_swap (y1, y2);
407 	}
408 
409 	if (hypot (x1-x2,y1-y2) <= 1.5 * h_length) {
410 		hl = hypot (x1-x2,y1-y2) * 0.6;
411 		hw = hl * h_width / h_length;
412 		vw = hl * v_width / h_length;
413 		if (vw < 2.0/PSL_DOTS_PER_INCH) vw = 2.0/PSL_DOTS_PER_INCH;
414 	}
415 	else {
416 		hw = h_width;
417 		hl = h_length;
418 		vw = v_width;
419 	}
420 
421 	dim[PSL_VEC_XTIP] = x2;
422 	dim[PSL_VEC_YTIP] = y2;
423 	dim[PSL_VEC_TAIL_WIDTH] = vw;
424 	dim[PSL_VEC_HEAD_LENGTH] = hl;
425 	dim[PSL_VEC_HEAD_WIDTH] = hw;
426 	PSL_plotsymbol (GMT->PSL, x1, y1, dim, PSL_VECTOR);
427 
428 	/* compression component */
429 	dx = eps2 * s;
430 	dy = eps2 * c;
431 	dim[PSL_VEC_STATUS] = PSL_VEC_BEGIN | PSL_VEC_FILL;
432 	psvelo_trace_arrow (GMT, slon, slat, dx, dy, sscale, &x1, &y1, &x2, &y2);
433 
434 	if (eps2 > 0.0) {
435 		gmt_M_double_swap (x1, x2);
436 		gmt_M_double_swap (y1, y2);
437 	}
438 
439 	/* arrow should go toward slat, slon */
440 	if (hypot (x1-x2,y1-y2) <= 1.5 * h_length) {
441 		hl = hypot (x1-x2,y1-y2) * 0.6;
442 		hw = hl * h_width / h_length;
443 		vw = hl * v_width / h_length;
444 		if (vw < 2.0/PSL_DOTS_PER_INCH) vw = 2.0/PSL_DOTS_PER_INCH;
445 	}
446 	else {
447 		hw = h_width;
448 		hl = h_length;
449 		vw = v_width;
450 	}
451 
452 	dim[PSL_VEC_XTIP] = x2;
453 	dim[PSL_VEC_YTIP] = y2;
454 	dim[PSL_VEC_TAIL_WIDTH] = vw;
455 	dim[PSL_VEC_HEAD_LENGTH] = hl;
456 	dim[PSL_VEC_HEAD_WIDTH] = hw;
457 	PSL_plotsymbol (GMT->PSL, x1, y1, dim, PSL_VECTOR);
458 
459 	/* second, compressional arrow in opposite direction */
460 
461 	psvelo_trace_arrow (GMT, slon, slat, -dx, -dy, sscale, &x1, &y1, &x2, &y2);
462 
463 	if (eps2 > 0.0) {
464 		gmt_M_double_swap (x1, x2);
465 		gmt_M_double_swap (y1, y2);
466 	}
467 
468 	/* arrow should go toward slat, slon */
469 
470 	if (hypot (x1-x2,y1-y2) <= 1.5 * h_length) {
471 		hl = hypot (x1-x2,y1-y2) * 0.6;
472 		hw = hl * h_width / h_length;
473 		vw = hl * v_width / h_length;
474 		if (vw < 2.0/PSL_DOTS_PER_INCH) vw = 2.0/PSL_DOTS_PER_INCH;
475 	}
476 	else {
477 		hw = h_width;
478 		hl = h_length;
479 		vw = v_width;
480 	}
481 
482 	dim[PSL_VEC_XTIP] = x2;
483 	dim[PSL_VEC_YTIP] = y2;
484 	dim[PSL_VEC_TAIL_WIDTH] = vw;
485 	dim[PSL_VEC_HEAD_LENGTH] = hl;
486 	dim[PSL_VEC_HEAD_WIDTH] = hw;
487 	PSL_plotsymbol (GMT->PSL, x1, y1, dim, PSL_VECTOR);
488 
489 	return 0;
490 }
491 
psvelo_trace_wedge(double spin,double sscale,double wedge_amp,int lines,double * x,double * y)492 GMT_LOCAL int psvelo_trace_wedge (double spin, double sscale, double wedge_amp, int lines, double *x, double *y) {
493 	/* make a rotation rate wedge and return in x,y */
494 
495 	/* Kurt Feigl, from code by D. Dong */
496 
497 	/*   INPUT VARIABLES: */
498 	/*   slat        - latitude, in degrees of arrow tail */
499 	/*   slon        - longitude in degrees of arrow tail */
500 	/*   sscale      : scaling factor for size (radius) of wedge */
501 	/*   wedge_amp   : scaling factor for angular size of wedge */
502 	/*   spin        : CW spin rate in rad/yr */
503 	/*   line        : if true, draw lines                  */
504 
505 	int nstep, i1, i, nump;
506 	double th, x0, y0, spin10, th0, x1, y1, s, c;
507 
508 	/*     How far would we spin */
509 	spin10 = wedge_amp * spin;
510 
511 	/*     set origin */
512 	th0 = x0 = y0 = 0.0;
513 
514 	/*     go to zero */
515 	nump = 1;
516 	*x++ = x0;
517 	*y++ = y0;
518 	nstep = 100;
519 
520 	/*     make a wedge as wide as the rotation in 10 Myr, */
521 	/*     with a line for every 0.2 microrad/yr */
522 
523 	i1 = nstep;
524 	for (i = 0; i <= i1 ; ++i) {
525 		th = i * spin10 / nstep;
526 		sincos (th, &s, &c);
527 		x1 = x0 + s * sscale;
528 		y1 = y0 + c * sscale;
529 		++nump;
530 		*x++ = x1;
531 		*y++ = y1;
532 		if (lines && fabs (th-th0) >= 0.2) {
533 			/*          draw a line to the middle */
534 			/*           go to zero and come back */
535 			++nump;
536 			*x++ = x0;
537 			*y++ = y0;
538 			++nump;
539 			*x++ = x1;
540 			*y++ = y1;
541 			th0 = th;
542 		}
543 	}
544 
545 	/*     go to zero */
546 	++nump;
547 	*x++ = x0;
548 	*y++ = y0;
549 
550 	return nump;
551 }
552 
psvelo_trace_sigwedge(double spin,double spinsig,double sscale,double wedge_amp,double * x,double * y)553 GMT_LOCAL int psvelo_trace_sigwedge (double spin, double spinsig, double sscale, double wedge_amp, double *x, double *y) {
554 	/* make a rotation rate uncertainty wedge and return in x,y */
555 
556 	/* Kurt Feigl, from code by D. Dong */
557 
558 	/*   INPUT VARIABLES: */
559 	/*   slat        - latitude, in degrees of arrow tail */
560 	/*   slon        - longitude in degrees of arrow tail */
561 	/*   sscale      : scaling factor for size (radius) of wedge */
562 	/*   wedge_amp   : scaling factor for angular size of wedge */
563 	/*   spin,spinsig:CW rotation rate and sigma in rad/yr */
564 
565 	int nstep, i, nump;
566 	double th, x0, y0, spin10, sig10, th0, x1, y1, s, c;
567 
568 	/*     How far would we spin */
569 	spin10 = wedge_amp * spin;
570 	sig10  = wedge_amp * spinsig;
571 
572 	/*     set origin */
573 	x0 = y0 = th0 = 0.0;
574 
575 	/*     go to zero */
576 	nump = 1;
577 	*x++ = x0;
578 	*y++ = y0;
579 
580 	/*     make a dense wedge to show the uncertainty */
581 	nstep = 30;
582 	for (i = -nstep; i <= nstep; ++i) {
583 		th = spin10 + i * sig10 / nstep;
584 		sincos (th, &s, &c);
585 		x1 = x0 + s * sscale * .67;
586 		y1 = y0 + c * sscale * .67;
587 		++nump;
588 		*x++ = x1;
589 		*y++ = y1;
590 	}
591 
592 	/* return to zero */
593 
594 	++nump;
595 	*x++ = x0;
596 	*y++ = y0;
597 	return nump;
598 }
599 
psvelo_paint_wedge(struct PSL_CTRL * PSL,double x0,double y0,double spin,double spinsig,double sscale,double wedge_amp,double t11,double t12,double t21,double t22,int polygon,double * rgb,int epolygon,double * ergb,int outline)600 GMT_LOCAL void psvelo_paint_wedge (struct PSL_CTRL *PSL, double x0, double y0, double spin, double spinsig, double sscale, double wedge_amp, double t11, double t12, double t21, double t22, int polygon, double *rgb, int epolygon, double *ergb, int outline) {
601 
602 	/* Make a wedge at center x0,y0  */
603 
604 #define NPOINTS 1000
605 
606 	int npoints = NPOINTS, i;
607 
608 	/* relative to center of ellipse */
609 	double dxe[NPOINTS], dye[NPOINTS];
610 	/* absolute paper coordinates */
611 	double axe[NPOINTS], aye[NPOINTS];
612 	gmt_M_unused(outline);
613 
614 	/* draw wedge */
615 
616 	npoints = psvelo_trace_wedge (spin, 1.0, wedge_amp, true, dxe, dye);
617 
618 	for (i = 0; i <= npoints - 1; i++)
619 		psvelo_transform_local (x0, y0, dxe[i], dye[i], sscale, t11, t12, t21, t22, &axe[i], &aye[i]);
620 
621 	if (polygon) {
622 		PSL_setfill (PSL, rgb, 1);
623 		PSL_plotpolygon (PSL, axe, aye, npoints);
624 	}
625 	else
626 		PSL_plotline (PSL, axe, aye, npoints, PSL_MOVE|PSL_STROKE|PSL_CLOSE);
627 
628 	/* draw uncertainty wedge */
629 
630 	npoints = psvelo_trace_sigwedge (spin, spinsig, 1.0,wedge_amp, dxe, dye);
631 
632 	for (i = 0; i < npoints - 1; i++) psvelo_transform_local (x0, y0, dxe[i], dye[i], sscale, t11, t12, t21, t22, &axe[i], &aye[i]);
633 
634 	if (epolygon) {
635 		PSL_setfill (PSL, ergb, 1);
636 		PSL_plotpolygon (PSL, axe, aye, npoints - 1);
637 	}
638 	else
639 		PSL_plotline (PSL, axe, aye, npoints - 1, PSL_MOVE|PSL_STROKE|PSL_CLOSE);
640 }
641 
642 /* end of utilvelo.c */
643 
New_Ctrl(struct GMT_CTRL * GMT)644 static void *New_Ctrl (struct GMT_CTRL *GMT) {	/* Allocate and initialize a new control structure */
645 	struct PSVELO_CTRL *C;
646 
647 	C = gmt_M_memory (GMT, NULL, 1, struct PSVELO_CTRL);
648 
649 	/* Initialize values whose defaults are not 0/false/NULL */
650 
651 	C->A.S.size_x = VECTOR_HEAD_LENGTH * GMT->session.u2u[GMT_PT][GMT_INCH];	/* 9p */
652 	C->A.S.v.h_length = (float)C->A.S.size_x;	/* 9p */
653 	C->A.S.v.v_angle = 30.0f;
654 	C->A.S.v.status = PSL_VEC_END + PSL_VEC_FILL + PSL_VEC_OUTLINE;
655 	C->A.S.v.pen = GMT->current.setting.map_default_pen;
656 	if (gmt_M_compat_check (GMT, 4)) GMT->current.setting.map_vector_shape = 0.4;	/* Historical reasons */
657 	C->A.S.v.v_shape = (float)GMT->current.setting.map_vector_shape;
658 	C->D.scale = 1.0;
659 	gmt_init_fill (GMT, &C->E.fill, 1.0, 1.0, 1.0);
660 	gmt_init_fill (GMT, &C->G.fill, 0.0, 0.0, 0.0);
661 	C->S.wedge_amp = 1.e7;
662 	C->S.conrad = 1.0;
663 	C->S.font = GMT->current.setting.font_annot[GMT_PRIMARY];
664 	C->S.font.size = 9;
665 	C->W.pen = GMT->current.setting.map_default_pen;
666 	return (C);
667 }
668 
Free_Ctrl(struct GMT_CTRL * GMT,struct PSVELO_CTRL * C)669 static void Free_Ctrl (struct GMT_CTRL *GMT, struct PSVELO_CTRL *C) {	/* Deallocate control structure */
670 	if (!C) return;
671 	gmt_M_free (GMT, C);
672 }
673 
usage(struct GMTAPI_CTRL * API,int level)674 static int usage (struct GMTAPI_CTRL *API, int level) {
675 	/* This displays the psvelo synopsis and optionally full usage information */
676 
677 	const char *name = gmt_show_name_and_purpose (API, THIS_MODULE_LIB, THIS_MODULE_CLASSIC_NAME, THIS_MODULE_PURPOSE);
678 	if (level == GMT_MODULE_PURPOSE) return (GMT_NOERROR);
679 	GMT_Usage (API, 0, "usage: %s [<table>] %s %s -S<symbol>[<scale>][</args>][+f<font>] [-A<vecpar>] [%s] [-C<cpt>] [-D<scale>] [-E<fill>] "
680 		"[-G<fill>] [-H[<scale>]] [-I[<intens>]] %s[-L[<pen>][+c[f|l]]] [-N] %s%s[%s] [%s] "
681 		"[-W[<pen>][+c[f|l]]] [%s] [%s] [-Z[m|e|n|u][+e]] %s[%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s] [%s]\n",
682 		name, GMT_J_OPT, GMT_Rgeo_OPT, GMT_B_OPT, API->K_OPT, API->O_OPT, API->P_OPT, GMT_U_OPT, GMT_V_OPT, GMT_X_OPT,
683 		GMT_Y_OPT, API->c_OPT, GMT_di_OPT, GMT_e_OPT, GMT_h_OPT, GMT_i_OPT, GMT_p_OPT, GMT_qi_OPT, GMT_tv_OPT, GMT_colon_OPT, GMT_PAR_OPT);
684 
685 	if (level == GMT_SYNOPSIS) return (GMT_MODULE_SYNOPSIS);
686 
687 	GMT_Message (API, GMT_TIME_NONE, "  REQUIRED ARGUMENTS:\n");
688 	GMT_Option (API, "<");
689 	GMT_Option (API, "J-,R");
690 	GMT_Usage (API, 1, "\n-S<symbol>[<scale>][</args>][+f<font>]");
691 	GMT_Usage (API, -2, "Select symbol type and <scale> (plus optional font; see documentation). "
692 		"If <scale> is not given then it is read from the first column after the required columns. "
693 		"Choose from the following geodetic symbols:");
694 	GMT_Usage (API, 3, "e: Velocity ellipses: in X,Y,Vx,Vy,SigX,SigY,CorXY,name format. "
695 		"Append <confidence> value (0-1) for error ellipse or give 0 to not draw the ellipse.");
696 	GMT_Usage (API, 3, "r: Velocity ellipses: in X,Y,Vx,Vy,a,b,theta,name format. "
697 		"Append <confidence> value (0-1) for error ellipse or give 0 to not draw the ellipse.");
698 	GMT_Usage (API, 3, "n: Anisotropy: in X,Y,Vx,Vy.");
699 	GMT_Usage (API, 3, "w: Rotational wedges: in X,Y,Spin,Spinsig. "
700 		"Append <wedgemag> value to scale the the Spin values [1].");
701 	GMT_Usage (API, 3, "x: Strain crosses: in X,Y,Eps1,Eps2,Theta.n");
702 	GMT_Message (API, GMT_TIME_NONE, "\n  OPTIONAL ARGUMENTS:\n");
703 	GMT_Option (API, "B-");
704 	GMT_Usage (API, 1, "\n-A<vecpar>");
705 	GMT_Usage (API, -2, "Specify arrow head attributes:");
706 	gmt_vector_syntax (API->GMT, 15, 3);
707 	GMT_Usage (API, -2, "Default is %gp+gblack+p1p", VECTOR_HEAD_LENGTH);
708 	GMT_Usage (API, 1, "\n-C<cpt>");
709 	GMT_Usage (API, -2, "Use CPT to assign colors based on vector magnitude. "
710 		"For other coloring options, see -W and -Z.");
711 	GMT_Usage (API, 1, "\n-D<scale>");
712 	GMT_Usage (API, -2, "Multiply uncertainties by <scale> (for -Se and -Sw options only).");
713 	GMT_Usage (API, 1, "\n-E<fill>");
714 	GMT_Usage (API, -2, "Set color used for uncertainty ellipses and wedges [no fill]; see -G for syntax. "
715 		"For other coloring options, see -L and -Z.");
716 	gmt_fill_syntax (API->GMT, 'G', NULL, "Specify color or pattern for symbol fill [no fill].");
717 	GMT_Usage (API, 1, "\n-H[<scale>]");
718 	GMT_Usage (API, -2, "Scale symbol sizes (set via -S or input column) and pen attributes by factors read from scale column. "
719 		"he scale column follows the symbol size column.  Alternatively, append a fixed <scale>.");
720 	GMT_Usage (API, 1, "\n-I[<intens>]");
721 	GMT_Usage (API, -2, "Use the intensity to modulate the symbol fill color (requires -C or -G). "
722 		"If no intensity is given we expect it to follow the required columns in the data record.");
723 	GMT_Option (API, "K");
724 	GMT_Usage (API, 1, "\n-L[<pen>][+c[f|l]]");
725 	GMT_Usage (API, -2, "Draw line or symbol outline using the current pen (see -W). "
726 		"Optionally, append separate pen for error outlines [Same as -W].");
727 	GMT_Usage (API, 1, "\n-N Do Not skip/clip symbols that fall outside map border [Default will ignore those outside].");
728 	GMT_Option (API, "O,P");
729 	GMT_Option (API, "U,V");
730 	GMT_Usage (API, 1, "\n-W[<pen>][+c[f|l]]");
731 	GMT_Usage (API, -2, "Set pen attributes [%s].", gmt_putpen (API->GMT, &API->GMT->current.setting.map_default_pen));
732 	GMT_Option (API, "X");
733 	GMT_Usage (API, 1, "\n-Z[m|e|n|u][+e]");
734 	GMT_Usage (API, -2, "Select quantity to use with -C to look-up colors.  Choose among:");
735 	GMT_Usage (API, 3, "m: Magnitude of vector or rotation [Default].");
736 	GMT_Usage (API, 3, "e: East velocity component.");
737 	GMT_Usage (API, 3, "n: North velocity component.");
738 	GMT_Usage (API, 3, "u: User column (given after required columns).");
739 	GMT_Usage (API, -2, "Note: Color selected will replace -G<fill>.  Append +e to instead act as -E<fill>.");
740 	GMT_Option (API, "c,di,e,h,i,p,qi,T,:,.");
741 
742 	return (GMT_MODULE_USAGE);
743 }
744 
parse(struct GMT_CTRL * GMT,struct PSVELO_CTRL * Ctrl,struct GMT_OPTION * options)745 static int parse (struct GMT_CTRL *GMT, struct PSVELO_CTRL *Ctrl, struct GMT_OPTION *options) {
746 	/* This parses the options provided to psvelo and sets parameters in Ctrl.
747 	 * Note Ctrl has already been initialized and non-zero default values set.
748 	 * Any GMT common options will override values set previously by other commands.
749 	 * It also replaces any file names specified as input or output with the data ID
750 	 * returned when registering these sources/destinations with the API.
751 	 */
752 
753 	unsigned int n_errors = 0, n_set;
754 	int n;
755 	bool got_A = false, got_shape = false;
756 	char txt[GMT_LEN256] = {""}, txt_b[GMT_LEN256] = {""}, txt_c[GMT_LEN256] = {""}, symbol, *c = NULL;
757 	struct GMT_OPTION *opt = NULL;
758 	struct GMTAPI_CTRL *API = GMT->parent;
759 
760 	symbol = (gmt_M_is_geographic (GMT, GMT_IN)) ? '=' : 'v';	/* Type of vector */
761 
762 	for (opt = options; opt; opt = opt->next) {	/* Process all the options given */
763 
764 		switch (opt->option) {
765 
766 			case '<':	/* Skip input files */
767 				if (GMT_Get_FilePath (API, GMT_IS_DATASET, GMT_IN, GMT_FILE_REMOTE, &(opt->arg))) n_errors++;;
768 				break;
769 
770 			/* Processes program-specific parameters */
771 
772 			case 'A':	/* Change size of arrow head */
773 				n_errors += gmt_M_repeated_module_option (API, Ctrl->A.active);
774 				got_A = true;
775 				if (gmt_M_compat_check (GMT, 4) && (strchr (opt->arg, '/') && !strchr (opt->arg, '+'))) {	/* Old-style args */
776 					sscanf (opt->arg, "%[^/]/%[^/]/%s", txt, txt_b, txt_c);
777 					Ctrl->A.S.v.v_width = (float)gmt_M_to_inch (GMT, txt);
778 					Ctrl->A.S.v.h_length = (float)gmt_M_to_inch (GMT, txt_b);
779 					Ctrl->A.S.v.h_width = (float)gmt_M_to_inch (GMT, txt_c);
780 					Ctrl->A.S.v.v_angle = (float)atand (0.5 * Ctrl->A.S.v.h_width / Ctrl->A.S.v.h_length);
781 					Ctrl->A.S.v.status |= PSL_VEC_OUTLINE2;
782 					Ctrl->A.S.symbol = GMT_SYMBOL_VECTOR_V4;
783 				}
784 				else {
785 					if (opt->arg[0] == '+') {	/* No size (use default), just attributes */
786 						n_errors += gmt_parse_vector (GMT, symbol, opt->arg, &Ctrl->A.S);
787 					}
788 					else {	/* Size, plus possible attributes */
789 						n = sscanf (opt->arg, "%[^+]%s", txt, txt_b);	/* txt_a should be symbols size with any +<modifiers> in txt_b */
790 						if (n == 1) txt_b[0] = 0;	/* No modifiers present, set txt_b to empty */
791 						Ctrl->A.S.size_x = gmt_M_to_inch (GMT, txt);	/* Length of vector */
792 						n_errors += gmt_parse_vector (GMT, symbol, txt_b, &Ctrl->A.S);
793 					}
794 					Ctrl->A.S.symbol = PSL_VECTOR;
795 					if (strstr (opt->arg, "+h")) got_shape = true;	/* User specified vector head shape */
796 				}
797 				break;
798 			case 'C':	/* Select CPT for coloring */
799 				n_errors += gmt_M_repeated_module_option (API, Ctrl->C.active);
800 				Ctrl->C.active = true;
801 				if (opt->arg[0]) Ctrl->C.file = strdup (opt->arg);
802 				break;
803 			case 'D':	/* Rescale sigmas */
804 				n_errors += gmt_M_repeated_module_option (API, Ctrl->D.active);
805 				Ctrl->D.active = true;
806 				Ctrl->D.scale = atof (opt->arg);
807 				break;
808 			case 'E':	/* Set color for error ellipse  */
809 				n_errors += gmt_M_repeated_module_option (API, Ctrl->E.active);
810 				if (gmt_getfill (GMT, opt->arg, &Ctrl->E.fill)) {
811 					gmt_fill_syntax (GMT, 'E', NULL, " ");
812 					n_errors++;
813 				}
814 				Ctrl->E.active = true;
815 				break;
816 			case 'G':	/* Set Gray shade for polygon */
817 				n_errors += gmt_M_repeated_module_option (API, Ctrl->G.active);
818 				Ctrl->G.active = true;
819 				if (gmt_getfill (GMT, opt->arg, &Ctrl->G.fill)) {
820 					gmt_fill_syntax (GMT, 'G', NULL, " ");
821 					n_errors++;
822 				}
823 				break;
824 			case 'H':		/* Overall symbol/pen scale column provided */
825 				n_errors += gmt_M_repeated_module_option (API, Ctrl->H.active);
826 				Ctrl->H.active = true;
827 				if (opt->arg[0]) {	/* Gave a fixed scale - no reading from file */
828 					Ctrl->H.value = atof (opt->arg);
829 					Ctrl->H.mode = PSVELO_CONST_SCALE;
830 				}
831 				break;
832 			case 'I':	/* Adjust symbol color via intensity */
833 				n_errors += gmt_M_repeated_module_option (API, Ctrl->I.active);
834 				Ctrl->I.active = true;
835 				if (opt->arg[0])
836 					Ctrl->I.value = atof (opt->arg);
837 				else
838 					Ctrl->I.mode = 1;
839 				break;
840 			case 'L':	/* Draw the outline */
841 				n_errors += gmt_M_repeated_module_option (API, Ctrl->L.active);
842 				Ctrl->L.active = true;
843 				if (opt->arg[0]) {
844 					Ctrl->L.error_pen = true;
845 					if (gmt_getpen (GMT, opt->arg, &Ctrl->L.pen)) {
846 						gmt_pen_syntax (GMT, 'L', NULL, " ", NULL, 0);
847 						n_errors++;
848 					}
849 				}
850 				break;
851 			case 'N':	/* Do not skip points outside border */
852 				n_errors += gmt_M_repeated_module_option (API, Ctrl->N.active);
853 				Ctrl->N.active = true;
854 				break;
855 			case 'S':	/* Get symbol [and size] */
856 				n_errors += gmt_M_repeated_module_option (API, Ctrl->S.active);
857  				txt_b[0] = '\0';
858 				if ((c = strstr (opt->arg, "+f"))) {	/* Gave font directly so handle that first */
859  					if (c[2] == '0')
860  						Ctrl->S.font.size = 0;
861 					else
862 						n_errors += gmt_getfont (GMT, &c[2], &(Ctrl->S.font));
863 					c[0] = '\0';	/* Temporarily chop off the font specification */
864 				}
865  				if (strchr ("er", opt->arg[0])) {	/* Error ellipse with vector symbol for -Se and -Sr */
866 					strncpy (txt, &opt->arg[1], GMT_LEN256);	/* Copy of the args after -Se|r */
867 					n = 0;
868 					if (strchr (txt, '/')) {	/* We clearly have scale/confidence and possibly /fontsize (deprecated) */
869 						while (txt[n] && txt[n] != '/') n++; txt[n++] = 0;	/* Hide the /confidence part */
870 						Ctrl->S.scale = gmt_M_to_inch (GMT, txt);	/* Get symbol size */
871 					}
872 					sscanf (&txt[n], "%lf/%s", &Ctrl->S.confidence, txt_b);
873 					/* confidence scaling */
874 					Ctrl->S.conrad = sqrt (-2.0 * log (1.0 - Ctrl->S.confidence));
875 					/* Check for deprecated font syntax */
876 					if (txt_b[0]) Ctrl->S.font.size = gmt_convert_units (GMT, txt_b, GMT_PT, GMT_PT);
877 				}
878 				else if (strchr ("nx", opt->arg[0])) {	/* Simple one-parameter argument for -Sn and -Sx */
879 					if (opt->arg[1]) Ctrl->S.scale = gmt_M_to_inch (GMT, &opt->arg[1]);
880 				}
881 				else if (opt->arg[0] == 'w') {	/* Rotational wedge */
882 					strncpy (txt, &opt->arg[1], GMT_LEN256);
883 					n = 0;
884 					if (strchr (txt, '/')) {	/* We clearly have scale/wedgemag  */
885 						while (txt[n] && txt[n] != '/') n++; txt[n++] = 0;	/* Hide the /wedgemag part */
886 						Ctrl->S.scale = gmt_M_to_inch (GMT, txt);	/* Get symbol size */
887 					}
888 					Ctrl->S.wedge_amp = (txt[n]) ? atof (&txt[n]) : 1.0;
889 				}
890 				switch (opt->arg[0]) {	/* Set modes and expected input columns */
891 					case 'e':
892 						Ctrl->S.symbol = CINE;	Ctrl->S.n_cols = 7;
893 						Ctrl->S.readmode = READ_ELLIPSE;
894 						break;
895 					case 'r':
896 						Ctrl->S.symbol = CINE;	Ctrl->S.n_cols = 7;
897 						Ctrl->S.readmode = READ_ROTELLIPSE;
898 						break;
899 					case 'n':
900 						Ctrl->S.symbol = ANISO;	Ctrl->S.n_cols = 4;
901 						Ctrl->S.readmode = READ_ANISOTROPY;
902 						break;
903 					case 'w':
904 						Ctrl->S.symbol = WEDGE;	Ctrl->S.n_cols = 4;
905 						Ctrl->S.readmode = READ_WEDGE;
906 						break;
907 					case 'x':
908 						Ctrl->S.symbol = CROSS;	Ctrl->S.n_cols = 5;
909 						Ctrl->S.readmode = READ_CROSS;
910 						break;
911 					default:
912 						GMT_Report (API, GMT_MSG_ERROR, "Option -S: Unrecognized symbol code %s\n", opt->arg);
913 						n_errors++;
914 						break;
915 				}
916 				if (c) c[0] = '+';	/* Restore font specification */
917 				if (gmt_M_is_zero (Ctrl->S.scale)) Ctrl->S.read = true;	/* Must get size from input file */
918 				break;
919 			case 'W':	/* Set line attributes */
920 				n_errors += gmt_M_repeated_module_option (API, Ctrl->W.active);
921 				Ctrl->W.active = true;
922 				if (opt->arg[0] && gmt_getpen (GMT, opt->arg, &Ctrl->W.pen)) {
923 					gmt_pen_syntax (GMT, 'W', NULL, " ", NULL, 0);
924 					n_errors++;
925 				}
926 				break;
927 			case 'Z':	/* Set items to control CPT coloring */
928 				n_errors += gmt_M_repeated_module_option (API, Ctrl->Z.active);
929 				Ctrl->Z.active = true;
930 				if (opt->arg[0] && (c = strstr (opt->arg, "+e"))) {	/* Paint error part of symbol instead (-E) */
931 					Ctrl->Z.item = PSVELO_E_FILL;
932 					c[0] = '\0';	/* Temporarily chop off the modifier */
933 				}
934 				switch (opt->arg[0]) {
935 					case 'm':	case '\0': Ctrl->Z.mode = PSVELO_V_MAG;	break;
936 					case 'e':	Ctrl->Z.mode = PSVELO_V_EAST;	break;
937 					case 'n':	Ctrl->Z.mode = PSVELO_V_NORTH;	break;
938 					case 'r':	Ctrl->Z.mode = PSVELO_R_MAG;	break;
939 					case 'u':	Ctrl->Z.mode = PSVELO_V_USER;	break;
940 					default:
941 						GMT_Report (API, GMT_MSG_ERROR, "Option -Z: Unrecognized mode %s\n", opt->arg);
942 						n_errors++;
943 						break;
944 				}
945 				if (c) c[0] = '+';	/* Restore modifier */
946 				break;
947 
948 			/* Illegal options */
949 
950 		}
951 	}
952 
953 	if (Ctrl->S.symbol == CROSS && !got_shape) Ctrl->A.S.v.v_shape = 0.1;	/* Traditional default cross vector shape if none given */
954 	gmt_consider_current_cpt (API, &Ctrl->C.active, &(Ctrl->C.file));
955 
956         /* Only one allowed */
957 	n_set = (Ctrl->S.readmode == READ_ELLIPSE) + (Ctrl->S.readmode == READ_ROTELLIPSE) + (Ctrl->S.readmode == READ_ANISOTROPY) + (Ctrl->S.readmode == READ_CROSS) + (Ctrl->S.readmode == READ_WEDGE);
958 	n_errors += gmt_M_check_condition (GMT, !GMT->common.R.active[RSET], "Must specify -R option\n");
959 	n_errors += gmt_M_check_condition (GMT, n_set > 1, "Only one -S setting is allowed.\n");
960 	n_errors += gmt_M_check_condition (GMT, Ctrl->D.active && ! (Ctrl->S.readmode == READ_ELLIPSE || Ctrl->S.readmode == READ_WEDGE), "Option -D requires -Se|w.\n");
961 	n_errors += gmt_M_check_condition (GMT, Ctrl->Z.active && !Ctrl->C.active, "Option -Z requires -C.\n");
962 	n_errors += gmt_M_check_condition (GMT, Ctrl->C.active && Ctrl->Z.item == PSVELO_E_FILL && Ctrl->E.active, "Options -C -Z+e cannot be combined with -E.\n");
963 	n_errors += gmt_M_check_condition (GMT, Ctrl->C.active && Ctrl->Z.item == PSVELO_G_FILL && Ctrl->G.active, "Options -C -Z cannot be combined with -G.\n");
964 
965 	if (!got_A && Ctrl->W.active) Ctrl->A.S.v.pen = Ctrl->W.pen;	/* Set vector pen to that given by -W if -A was not set */
966 	return (n_errors ? GMT_PARSE_ERROR : GMT_NOERROR);
967 }
968 
969 #define bailout(code) {gmt_M_free_options (mode); return (code);}
970 #define Return(code) {Free_Ctrl (GMT, Ctrl); gmt_end_module (GMT, GMT_cpy); bailout (code);}
971 
psvelo_set_colorfill(struct GMT_CTRL * GMT,struct PSVELO_CTRL * Ctrl,struct GMT_PALETTE * P,double value,double ival)972 GMT_LOCAL void psvelo_set_colorfill (struct GMT_CTRL *GMT, struct PSVELO_CTRL *Ctrl, struct GMT_PALETTE *P, double value, double ival) {
973 	/* Called if -C was given.  Selects and updates color fills and possibly pen colors */
974 	struct GMT_FILL *F = (Ctrl->Z.item == PSVELO_G_FILL) ? &Ctrl->G.fill : &Ctrl->E.fill;
975 	if (P) gmt_get_fill_from_z (GMT, P, value, F);	/* Update color based on value and CPT */
976 	if (Ctrl->I.active) {	/* Modulate color based on intensity ival */
977 		gmt_illuminate (GMT, ival, F->rgb);
978 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Illumination value used is %h\n", ival);
979 	}
980 	if (Ctrl->L.pen.cptmode & 1) {	/* Also change error pen color via CPT */
981 		gmt_M_rgb_copy (Ctrl->L.pen.rgb, F->rgb);
982 		gmt_setpen (GMT, &Ctrl->L.pen);
983 	}
984 	if (Ctrl->W.pen.cptmode & 1) {	/* Also change pen color via CPT */
985 		gmt_M_rgb_copy (Ctrl->W.pen.rgb, F->rgb);
986 		gmt_setpen (GMT, &Ctrl->W.pen);
987 		if (!Ctrl->L.error_pen)	/* No -L pen so duplicate the change */
988 			gmt_M_rgb_copy (Ctrl->L.pen.rgb, Ctrl->W.pen.rgb);
989 	}
990 }
991 
GMT_psvelo(void * V_API,int mode,void * args)992 EXTERN_MSC int GMT_psvelo (void *V_API, int mode, void *args) {
993 	int ix = 0, iy = 1, n_rec = 0, justify;
994 	int plot_ellipse = true, plot_vector = true, error = false;
995 	unsigned int xcol = 0, tcol_f = 0, tcol_s = 0, scol = 0, icol = 0, n_warn = 0;
996 	bool set_g_fill, set_e_fill;
997 
998 	double plot_x, plot_y, vxy[2], plot_vx, plot_vy, length, s, dim[PSL_MAX_DIMS];
999 	double eps1 = 0.0, eps2 = 0.0, spin = 0.0, spinsig = 0.0, theta = 0.0, *in = NULL;
1000 	double direction = 0, small_axis = 0, great_axis = 0, sigma_x, sigma_y, corr_xy;
1001 	double t11 = 1.0, t12 = 0.0, t21 = 0.0, t22 = 1.0, hl, hw, vw, ssize, headpen_width = 0.0;
1002 	double z_val, e_val, value, scale, size, i_value, nominal_size;
1003 
1004 	char *station_name = NULL;
1005 
1006 	struct GMT_RECORD *In = NULL;
1007 	struct GMT_PALETTE *CPT = NULL;
1008 	struct GMT_PEN current_pen;
1009 	struct PSVELO_CTRL *Ctrl = NULL;
1010 	struct GMT_CTRL *GMT = NULL, *GMT_cpy = NULL;		/* General GMT internal parameters */
1011 	struct GMT_OPTION *options = NULL;
1012 	struct PSL_CTRL *PSL = NULL;		/* General PSL internal parameters */
1013 	struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);	/* Cast from void to GMTAPI_CTRL pointer */
1014 
1015 	/*----------------------- Standard module initialization and parsing ----------------------*/
1016 
1017 	if (API == NULL) return (GMT_NOT_A_SESSION);
1018 	if (mode == GMT_MODULE_PURPOSE) return (usage (API, GMT_MODULE_PURPOSE));	/* Return the purpose of program */
1019 	options = GMT_Create_Options (API, mode, args);	if (API->error) return (API->error);	/* Set or get option list */
1020 
1021 	if ((error = gmt_report_usage (API, options, 0, usage)) != GMT_NOERROR) bailout (error);	/* Give usage if requested */
1022 
1023 	/* Parse the command-line arguments; return if errors are encountered */
1024 
1025 	if ((GMT = gmt_init_module (API, THIS_MODULE_LIB, THIS_MODULE_CLASSIC_NAME, THIS_MODULE_KEYS, THIS_MODULE_NEEDS, NULL, &options, &GMT_cpy)) == NULL) bailout (API->error); /* Save current state */
1026 	if (GMT_Parse_Common (API, THIS_MODULE_OPTIONS, options)) Return (API->error);
1027 	Ctrl = New_Ctrl (GMT);	/* Allocate and initialize a new control structure */
1028 	if ((error = parse (GMT, Ctrl, options)) != 0) Return (error);
1029 
1030 	/*---------------------------- This is the psvelo main code ----------------------------*/
1031 
1032 	set_e_fill = Ctrl->E.active;	set_g_fill = Ctrl->G.active;
1033 	if (Ctrl->C.active) {
1034 		if ((CPT = GMT_Read_Data (API, GMT_IS_PALETTE, GMT_IS_FILE, GMT_IS_NONE, GMT_READ_NORMAL, NULL, Ctrl->C.file, NULL)) == NULL) {
1035 			Return (API->error);
1036 		}
1037 		if (Ctrl->Z.item == PSVELO_G_FILL) set_g_fill = true;	/* Since we will set it via CPT lookup */
1038 		if (Ctrl->Z.item == PSVELO_E_FILL) set_e_fill = true;	/* Since we will set it via CPT lookup */
1039 	}
1040 	else if (Ctrl->I.active && Ctrl->I.mode == 0) {	/* Constant illumination with constant intensity can be done once before data loop */
1041 		gmt_illuminate (GMT, Ctrl->I.value, Ctrl->E.fill.rgb);
1042 		gmt_illuminate (GMT, Ctrl->I.value, Ctrl->G.fill.rgb);
1043 		Ctrl->I.active = false;	/* So we don't do this again */
1044 	}
1045 	i_value = Ctrl->I.value;	/* May be replaced in the loop if variable intensity was given */
1046 	if (Ctrl->S.symbol == CINE && Ctrl->S.confidence > 0.0 && !(Ctrl->E.active || Ctrl->L.active))
1047 		Ctrl->L.active = true;	/* If confidence is > 0 but neither -E or -L is set then we turn on -L to pick up -W pen (below) */
1048 	if (Ctrl->L.active && !Ctrl->L.error_pen)	/* Duplicate -W to -L */
1049 		gmt_M_memcpy (&Ctrl->L.pen, &Ctrl->W.pen, 1, struct GMT_PEN);
1050 
1051 	if (gmt_map_setup (GMT, GMT->common.R.wesn)) Return (GMT_PROJECTION_ERROR);
1052 
1053 	if ((PSL = gmt_plotinit (GMT, options)) == NULL) Return (GMT_RUNTIME_ERROR);
1054 	gmt_set_basemap_orders (GMT, Ctrl->N.active ? GMT_BASEMAP_FRAME_BEFORE : GMT_BASEMAP_FRAME_AFTER, GMT_BASEMAP_GRID_BEFORE, GMT_BASEMAP_ANNOT_AFTER);
1055 	gmt_plane_perspective (GMT, GMT->current.proj.z_project.view_plane, GMT->current.proj.z_level);
1056 	gmt_plotcanvas (GMT);	/* Fill canvas if requested */
1057 	gmt_map_basemap (GMT);	/* Basemap before data */
1058 
1059 	gmt_M_memset (&current_pen, 1, struct GMT_PEN);
1060 	gmt_M_memset (dim, PSL_MAX_DIMS, double);
1061 	gmt_setpen (GMT, &Ctrl->W.pen);
1062 	PSL_setfont (PSL, GMT->current.setting.font_annot[GMT_PRIMARY].id);
1063 
1064 	if (!Ctrl->N.active) gmt_map_clip_on (GMT, GMT->session.no_rgb, 3);
1065 	gmt_init_vector_param (GMT, &Ctrl->A.S, true, Ctrl->W.active, &Ctrl->W.pen, Ctrl->G.active, &Ctrl->G.fill);
1066 	if (Ctrl->A.S.symbol == PSL_VECTOR) Ctrl->A.S.v.v_width = (float)(Ctrl->W.pen.width * GMT->session.u2u[GMT_PT][GMT_INCH]);
1067 
1068 	ix = (GMT->current.setting.io_lonlat_toggle[0]);	iy = 1 - ix;	/* Deal with -: */
1069 
1070 	  /* 1. Add user column for coloring, if requested */
1071 	if (Ctrl->Z.mode == PSVELO_V_USER) Ctrl->S.n_cols++;	/* Need to read one extra column */
1072 	/* 2. Add scale from file, if missing */
1073 	if (Ctrl->S.read) {	/* Read symbol size from file */
1074 		scol = Ctrl->S.n_cols;	/* Column ID with scales */
1075 		Ctrl->S.n_cols++;	/* Must read an extra column */
1076 		gmt_set_column_type (GMT, GMT_IN, scol, GMT_IS_DIMENSION);
1077 	}
1078 	else	/* Fixed symbol scale */
1079 		nominal_size = scale = Ctrl->S.scale;
1080 	/* 3. Add scaling from file, if requested */
1081 	if (Ctrl->H.active && Ctrl->H.mode == PSVELO_READ_SCALE) {
1082 		xcol = Ctrl->S.n_cols;
1083 		Ctrl->S.n_cols++;	/* Read scaling from data file */
1084 		gmt_set_column_type (GMT, GMT_IN, xcol, GMT_IS_FLOAT);
1085 	}
1086 	/* 4. Add intensity from file, if requested */
1087 	if (Ctrl->I.mode) {	/* Read intensity from data file */
1088 		icol = Ctrl->S.n_cols;	/* Column id for intensity */
1089 		Ctrl->S.n_cols++;	/* One more data column required */
1090 		gmt_set_column_type (GMT, GMT_IN, icol, GMT_IS_FLOAT);
1091 	}
1092 	/* 5. Add transparencies from file, if requested */
1093 	if (GMT->common.t.variable) {	/* Need one or two transparencies from file */
1094 		if (GMT->common.t.mode & GMT_SET_FILL_TRANSP) {
1095 			tcol_f = Ctrl->S.n_cols;
1096 			Ctrl->S.n_cols++;	/* Read fill transparencies from data file */
1097 			gmt_set_column_type (GMT, GMT_IN, tcol_f, GMT_IS_FLOAT);
1098 		}
1099 		if (GMT->common.t.mode & GMT_SET_PEN_TRANSP) {
1100 			tcol_s = Ctrl->S.n_cols;
1101 			Ctrl->S.n_cols++;	/* Read stroke transparencies from data file */
1102 			gmt_set_column_type (GMT, GMT_IN, tcol_s, GMT_IS_FLOAT);
1103 		}
1104 	}
1105 
1106 	GMT_Set_Columns (API, GMT_IN, Ctrl->S.n_cols, GMT_COL_FIX);
1107 
1108 	if (GMT_Init_IO (API, GMT_IS_DATASET, GMT_IS_POINT, GMT_IN, GMT_ADD_DEFAULT, 0, options) != GMT_NOERROR) {	/* Register data input */
1109 		Return (API->error);
1110 	}
1111 	if (GMT_Begin_IO (API, GMT_IS_DATASET, GMT_IN, GMT_HEADER_ON) != GMT_NOERROR) {	/* Enables data input and sets access mode */
1112 		Return (API->error);
1113 	}
1114 
1115 	if (Ctrl->S.readmode == READ_ELLIPSE || Ctrl->S.readmode == READ_ROTELLIPSE) GMT_Report (API, GMT_MSG_INFORMATION, "psvelo: 2-D confidence interval and scaling factor %f %f\n", Ctrl->S.confidence, Ctrl->S.conrad);
1116 
1117 	if (Ctrl->D.active)  GMT_Report (API, GMT_MSG_INFORMATION, "Rescaling uncertainties by a factor of %f\n", Ctrl->D.scale);
1118 
1119 	if (Ctrl->S.symbol == CINE || Ctrl->S.symbol == CROSS) {
1120 		if (Ctrl->A.S.v.status & PSL_VEC_OUTLINE2) {	/* Vector head outline pen specified separately */
1121 			PSL_defpen (PSL, "PSL_vecheadpen", Ctrl->A.S.v.pen.width, Ctrl->A.S.v.pen.style, Ctrl->A.S.v.pen.offset, Ctrl->A.S.v.pen.rgb);
1122 			headpen_width = 0.5 * Ctrl->A.S.v.pen.width;
1123 		}
1124 		else {	/* Reset to default pen */
1125 			if (Ctrl->W.active) {	/* Vector head outline pen default is half that of stem pen */
1126 				PSL_defpen (PSL, "PSL_vecheadpen", Ctrl->W.pen.width, Ctrl->W.pen.style, Ctrl->W.pen.offset, Ctrl->W.pen.rgb);
1127 				headpen_width = 0.5 * Ctrl->W.pen.width;
1128 			}
1129 		}
1130 	}
1131 	do {	/* Keep returning records until we reach EOF */
1132 		if ((In = GMT_Get_Record (API, GMT_READ_MIXED, NULL)) == NULL) {	/* Read next record, get NULL if special case */
1133 			if (gmt_M_rec_is_error (GMT)) 		/* Bail if there are any read errors */
1134 				Return (GMT_RUNTIME_ERROR);
1135 			if (gmt_M_rec_is_any_header (GMT)) 	/* Skip all table and segment headers */
1136 				continue;
1137 			if (gmt_M_rec_is_eof (GMT)) 		/* Reached end of file */
1138 				break;
1139 			assert (In->text != NULL);						/* Should never get here */
1140 		}
1141 		if (In->data == NULL) {
1142 			gmt_quit_bad_record (API, In);
1143 			Return (API->error);
1144 		}
1145 
1146 		if (gmt_M_is_dnan (In->data[GMT_X]) || gmt_M_is_dnan (In->data[GMT_Y]))	/* Probably a non-recognized header since we got NaNs */
1147 			continue;
1148 
1149 		in = In->data;
1150 		station_name = In->text;
1151 
1152 		/* Data record to process */
1153 
1154 		n_rec++;
1155 
1156 		if (Ctrl->S.readmode == READ_ELLIPSE) {
1157 			vxy[ix] = in[2];	vxy[iy] = in[3];
1158 			sigma_x = in[4];	sigma_y = in[5];
1159 			corr_xy = in[6];
1160 			if (Ctrl->C.active) {	/* Compute/select the value parameters */
1161 				switch (Ctrl->Z.mode) {
1162 					case PSVELO_V_MAG:   z_val = hypot (vxy[0], vxy[1]);	e_val = hypot (sigma_x, sigma_y); break;
1163 					case PSVELO_V_EAST:  z_val = vxy[0]; e_val = sigma_x;	break;
1164 					case PSVELO_V_NORTH: z_val = vxy[1]; e_val = sigma_y;	break;
1165 					case PSVELO_V_USER:  z_val = e_val = in[Ctrl->S.n_cols-1];	break;
1166 				}
1167 			}
1168 			/* rescale uncertainties if necessary */
1169 			if (Ctrl->D.active) {
1170 				sigma_x = Ctrl->D.scale * sigma_x;
1171 				sigma_y = Ctrl->D.scale * sigma_y;
1172 			}
1173 			if (fabs (sigma_x) < EPSIL && fabs (sigma_y) < EPSIL)
1174 				plot_ellipse = false;
1175 			else {
1176 				plot_ellipse = true;
1177 				psvelo_ellipse_convert (sigma_x, sigma_y, corr_xy, Ctrl->S.conrad, &small_axis, &great_axis, &direction);
1178 
1179 				/* convert to degrees */
1180 				direction = direction * R2D;
1181 			}
1182 		}
1183 		else if (Ctrl->S.readmode == READ_ROTELLIPSE) {
1184 			vxy[ix] = in[2];	vxy[iy] = in[3];
1185 			great_axis = Ctrl->S.conrad*in[4];
1186 			small_axis = Ctrl->S.conrad*in[5];
1187 			direction = in[6];
1188 			if (Ctrl->C.active) {	/* Compute/select the value parameters */
1189 				switch (Ctrl->Z.mode) {
1190 					case PSVELO_V_MAG:   z_val = hypot (vxy[0], vxy[1]);	e_val = hypot (great_axis, small_axis); break;
1191 					case PSVELO_V_EAST:  z_val = vxy[0]; e_val = great_axis;	break;
1192 					case PSVELO_V_NORTH: z_val = vxy[1]; e_val = small_axis;	break;
1193 					case PSVELO_V_USER:  z_val = e_val = in[Ctrl->S.n_cols-1];	break;
1194 				}
1195 			}
1196 			if (fabs (great_axis) < EPSIL && fabs (small_axis) < EPSIL)
1197 				plot_ellipse = false;
1198 			else
1199 				plot_ellipse = true;
1200 		}
1201 		else if (Ctrl->S.readmode == READ_ANISOTROPY) {
1202 			vxy[ix] = in[2];
1203 			vxy[iy] = in[3];
1204 		}
1205 		else if (Ctrl->S.readmode == READ_CROSS) {
1206 			eps1  = in[2];
1207 			eps2  = in[3];
1208 			theta = in[4];
1209 		}
1210 		else if (Ctrl->S.readmode == READ_WEDGE) {
1211 			spin    = in[2];
1212 			spinsig = in[3];
1213 			if (Ctrl->C.active) {
1214 				switch (Ctrl->Z.mode) {
1215 					case PSVELO_V_MAG:  z_val = spin;	e_val = spinsig; break;
1216 					case PSVELO_V_USER: z_val = e_val = in[Ctrl->S.n_cols-1];	break;
1217 				}
1218 			}
1219 			if (Ctrl->D.active) spinsig = spinsig * Ctrl->D.scale;
1220 		}
1221 		if (Ctrl->S.read) nominal_size = scale = in[scol];
1222 
1223 		if (!Ctrl->N.active) {
1224 			gmt_map_outside (GMT, in[GMT_X], in[GMT_Y]);
1225 			if (abs (GMT->current.map.this_x_status) > 1 || abs (GMT->current.map.this_y_status) > 1) continue;
1226 		}
1227 
1228 		gmt_geo_to_xy (GMT, in[GMT_X], in[GMT_Y], &plot_x, &plot_y);
1229 
1230 		value = (Ctrl->Z.item == PSVELO_E_FILL) ? e_val : z_val;	/* Select which value for color lookup - if active */
1231 		if (Ctrl->I.mode) i_value = in[icol];
1232 		/* Possibly update E or G fills based on value and/ore intensities, then set updated colors in PS */
1233 		psvelo_set_colorfill (GMT, Ctrl, CPT, value, i_value);
1234 		gmt_init_vector_param (GMT, &Ctrl->A.S, true, Ctrl->W.active, &Ctrl->W.pen, Ctrl->G.active, &Ctrl->G.fill);
1235 
1236 		if (GMT->common.t.variable) {	/* Update the transparency for current symbol (or -t was given) */
1237 			double transp[2] = {0.0, 0.0};
1238 			if (GMT->common.t.n_transparencies == 2) {	/* Requested two separate values to be read from file */
1239 				transp[GMT_FILL_TRANSP] = 0.01 * in[tcol_f];
1240 				transp[GMT_PEN_TRANSP]  = 0.01 * in[tcol_s];
1241 			}
1242 			else if (GMT->common.t.mode & GMT_SET_FILL_TRANSP) {	/* Gave fill transparency */
1243 				transp[GMT_FILL_TRANSP] = 0.01 * in[tcol_f];
1244 				if (GMT->common.t.n_transparencies == 0) transp[GMT_PEN_TRANSP] = transp[GMT_FILL_TRANSP];	/* Implied to be used for stroke also */
1245 			}
1246 			else {	/* Gave stroke transparency */
1247 				transp[GMT_PEN_TRANSP] = 0.01 * in[tcol_s];
1248 				if (GMT->common.t.n_transparencies == 0) transp[GMT_FILL_TRANSP] = transp[GMT_PEN_TRANSP];	/* Implied to be used for fill also */
1249 			}
1250 			PSL_settransparencies (PSL, transp);
1251 		}
1252 		size = scale;
1253 		if (Ctrl->H.active) {	/* Variable scaling of symbol size and pen width */
1254 			double scl = (Ctrl->H.mode == PSVELO_READ_SCALE) ? in[xcol] : Ctrl->H.value;
1255 			size *= scl;
1256 		}
1257 
1258 		switch (Ctrl->S.symbol) {
1259 			case CINE:
1260 				plot_vector = (hypot (vxy[0], vxy[1]) < 1.e-8) ? false : true;
1261 				psvelo_trace_arrow (GMT, in[GMT_X], in[GMT_Y], vxy[0], vxy[1], size, &plot_x, &plot_y, &plot_vx, &plot_vy);
1262 				psvelo_get_trans (GMT, in[GMT_X], in[GMT_Y], &t11, &t12, &t21, &t22);
1263 				if (plot_ellipse) {	/* Optionally fill [-E] and optionally outline [-L] the error ellipse */
1264 					if (Ctrl->L.active) {	/* Draw ellipse outline */
1265 						current_pen = Ctrl->L.pen;
1266 						if (Ctrl->H.active) {
1267 							double scl = (Ctrl->H.mode == PSVELO_READ_SCALE) ? in[xcol] : Ctrl->H.value;
1268 							gmt_scale_pen (GMT, &current_pen, scl);
1269 						}
1270 						gmt_setpen (GMT, &current_pen);
1271 					}
1272 					/* Draw the ellipse */
1273 					psvelo_paint_ellipse (GMT, plot_vx, plot_vy, direction, great_axis, small_axis, size,
1274 						t11, t12, t21, t22, Ctrl->E.active, &Ctrl->E.fill, Ctrl->L.active);
1275 				}
1276 				if (plot_vector) {	/* Verify that vector length is not ridiculously small */
1277 					length = hypot (plot_x-plot_vx, plot_y-plot_vy);	/* Length of arrow */
1278 					if (length < Ctrl->A.S.v.h_length && Ctrl->A.S.v.v_norm < 0.0) {	/* No shrink requested yet head length exceeds total vector length */
1279 						GMT_Report (API, GMT_MSG_INFORMATION, "Vector head length exceeds overall vector length near line %d. Consider adding +n<norm> to -A\n", n_rec);
1280 						n_warn++;
1281 					}
1282 					s = (length < Ctrl->A.S.v.v_norm) ? length / Ctrl->A.S.v.v_norm : 1.0;
1283 					hw = s * Ctrl->A.S.v.h_width;
1284 					hl = s * Ctrl->A.S.v.h_length;
1285 					vw = s * Ctrl->A.S.v.v_width;
1286 					if (vw < (2.0/PSL_DOTS_PER_INCH)) vw = 2.0/PSL_DOTS_PER_INCH;	/* Minimum width set */
1287 					if (Ctrl->A.S.v.status & PSL_VEC_OUTLINE2) {
1288 						current_pen = Ctrl->A.S.v.pen;
1289 						if (Ctrl->H.active) {
1290 							double scl = (Ctrl->H.mode == PSVELO_READ_SCALE) ? in[xcol] : Ctrl->H.value;
1291 							gmt_scale_pen (GMT, &current_pen, scl);
1292 						}
1293 						gmt_setpen (GMT, &current_pen);
1294 					}
1295 					dim[PSL_VEC_XTIP]        = plot_vx;
1296 					dim[PSL_VEC_YTIP]        = plot_vy;
1297 					dim[PSL_VEC_TAIL_WIDTH]  = vw;
1298 					dim[PSL_VEC_HEAD_LENGTH] = hl;
1299 					dim[PSL_VEC_HEAD_WIDTH]  = hw;
1300 					dim[PSL_VEC_HEAD_SHAPE]  = Ctrl->A.S.v.v_shape;
1301 					if (Ctrl->W.active) {
1302 						current_pen = Ctrl->W.pen;
1303 						if (Ctrl->H.active) {
1304 							double scl = (Ctrl->H.mode == PSVELO_READ_SCALE) ? in[xcol] : Ctrl->H.value;
1305 							gmt_scale_pen (GMT, &current_pen, scl);
1306 						}
1307 						gmt_setpen (GMT, &current_pen);
1308 					}
1309 					if (Ctrl->A.S.symbol == GMT_SYMBOL_VECTOR_V4) {	/* Old GMT4 vector selected */
1310 						double *this_rgb = (set_g_fill) ? Ctrl->G.fill.rgb : GMT->session.no_rgb;
1311 						psl_vector_v4 (PSL, plot_x, plot_y, dim, this_rgb, Ctrl->L.active);
1312 					}
1313 					else {
1314 						dim[PSL_VEC_STATUS]          = (double)Ctrl->A.S.v.status;
1315 						dim[PSL_VEC_HEAD_TYPE_BEGIN] = (double)Ctrl->A.S.v.v_kind[0];
1316 						dim[PSL_VEC_HEAD_TYPE_END]   = (double)Ctrl->A.S.v.v_kind[1];
1317 						dim[PSL_VEC_HEAD_PENWIDTH]   = (headpen_width > 0.0) ? headpen_width : 0.5 * Ctrl->W.pen.width;
1318 						if (Ctrl->A.S.v.status & PSL_VEC_FILL2)
1319 							gmt_setfill (GMT, &Ctrl->A.S.v.fill, Ctrl->W.active);
1320 						else if (set_g_fill)
1321 							gmt_setfill (GMT, &Ctrl->G.fill, Ctrl->W.active);
1322 						PSL_plotsymbol (PSL, plot_x, plot_y, dim, PSL_VECTOR);
1323 					}
1324 					justify = ((plot_vx - plot_x) > 0.0) ? PSL_MR : PSL_ML;
1325 					if (Ctrl->S.font.size > 0.0 && station_name && station_name[0])	/* 1 inch = 2.54 cm */
1326 						PSL_plottext (PSL, plot_x + (PSL_MC - justify) / 25.4 , plot_y, Ctrl->S.font.size, station_name, ANGLE, justify, FORM);
1327 				}
1328 				else {	/* vector too small, just place an circle there instead */
1329 					if (set_g_fill)
1330 						gmt_setfill (GMT, &Ctrl->G.fill, 1);
1331 					ssize = GMT_DOT_SIZE;
1332 					PSL_plotsymbol (PSL, plot_x, plot_y, &ssize, PSL_CIRCLE);
1333 					justify = PSL_TC;
1334 					if (Ctrl->S.font.size > 0.0 && station_name && station_name[0])	/* Place station name */
1335 						PSL_plottext (PSL, plot_x, plot_y - 1.0 / 25.4, Ctrl->S.font.size, station_name, ANGLE, justify, FORM);
1336 				}
1337 				break;
1338 			case ANISO:
1339 				psvelo_trace_arrow (GMT, in[GMT_X], in[GMT_Y], vxy[0], vxy[1], size, &plot_x, &plot_y, &plot_vx, &plot_vy);
1340 				current_pen = Ctrl->W.pen;
1341 				if (Ctrl->H.active) {
1342 					double scl = (Ctrl->H.mode == PSVELO_READ_SCALE) ? in[xcol] : Ctrl->H.value;
1343 					gmt_scale_pen (GMT, &current_pen, scl);
1344 				}
1345 				gmt_setpen (GMT, &current_pen);
1346 				PSL_plotsegment (PSL, plot_x, plot_y, plot_vx, plot_vy);
1347 				break;
1348 			case CROSS:
1349 				/* triangular arrowheads */
1350 				current_pen = Ctrl->W.pen;
1351 				if (Ctrl->H.active) {
1352 					double scl = (Ctrl->H.mode == PSVELO_READ_SCALE) ? in[xcol] : Ctrl->H.value;
1353 					gmt_scale_pen (GMT, &current_pen, scl);
1354 				}
1355 				psvelo_trace_cross (GMT, in[GMT_X],in[GMT_Y],eps1,eps2,theta,size,Ctrl->A.S.v.v_width,Ctrl->A.S.v.h_length,
1356 					Ctrl->A.S.v.h_width,Ctrl->A.S.v.v_shape,Ctrl->L.active,&(current_pen));
1357 				break;
1358 			case WEDGE:
1359 				PSL_comment (PSL, "begin wedge number %li", n_rec);
1360 				gmt_geo_to_xy (GMT, in[GMT_X], in[GMT_Y], &plot_x, &plot_y);
1361 				psvelo_get_trans (GMT, in[GMT_X], in[GMT_Y], &t11, &t12, &t21, &t22);
1362 				psvelo_paint_wedge (PSL, plot_x, plot_y, spin, spinsig, size, Ctrl->S.wedge_amp, t11, t12, t21, t22,
1363 					set_g_fill, Ctrl->G.fill.rgb, set_e_fill, Ctrl->E.fill.rgb, Ctrl->L.active);
1364 				break;
1365 		}
1366 	} while (true);
1367 
1368 	if (GMT_End_IO (API, GMT_IN, 0) != GMT_NOERROR) {	/* Disables further data input */
1369 		Return (API->error);
1370 	}
1371 
1372 	if (GMT->common.t.variable) {	/* Reset the transparencies */
1373 		double transp[2] = {0.0, 0.0};
1374 		PSL_settransparencies (PSL, transp);
1375 	}
1376 
1377 	if (n_warn) GMT_Report (API, GMT_MSG_INFORMATION, "%d vector heads had length exceeding the vector length and were skipped. Consider the +n<norm> modifier to -A\n", n_warn);
1378 	GMT_Report (API, GMT_MSG_INFORMATION, "Number of records read: %li\n", n_rec);
1379 
1380 	if (!Ctrl->N.active) gmt_map_clip_off (GMT);
1381 
1382 	PSL_setdash (PSL, NULL, 0);
1383 
1384 	gmt_map_basemap (GMT);	/* Basemap after data */
1385 	gmt_plane_perspective (GMT, -1, 0.0);
1386 	gmt_plotend (GMT);
1387 
1388 	Return (GMT_NOERROR);
1389 }
1390 
GMT_velo(void * V_API,int mode,void * args)1391 EXTERN_MSC int GMT_velo (void *V_API, int mode, void *args) {
1392 	/* This is the GMT6 modern mode name */
1393 	struct GMTAPI_CTRL *API = gmt_get_api_ptr (V_API);	/* Cast from void to GMTAPI_CTRL pointer */
1394 	if (API->GMT->current.setting.run_mode == GMT_CLASSIC && !API->usage) {
1395 		GMT_Report (API, GMT_MSG_ERROR, "Shared GMT module not found: velo\n");
1396 		return (GMT_NOT_A_VALID_MODULE);
1397 	}
1398 	return GMT_psvelo (V_API, mode, args);
1399 }
1400