1 /*--------------------------------------------------------------------
2  *
3  *	Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4  *	See LICENSE.TXT file for copying and redistribution conditions.
5  *
6  *	This program is free software; you can redistribute it and/or modify
7  *	it under the terms of the GNU Lesser General Public License as published by
8  *	the Free Software Foundation; version 3 or any later version.
9  *
10  *	This program is distributed in the hope that it will be useful,
11  *	but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *	GNU Lesser General Public License for more details.
14  *
15  *	Contact info: www.generic-mapping-tools.org
16  *--------------------------------------------------------------------*/
17 /*
18  *
19  *			G M T _ P L O T . C
20  *
21  *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
22  * GMT_plot.c contains code related to plotting maps.  These functions requires
23  * we pass both the GMT and PSL control structure pointers.
24  *
25  * Author:	Paul Wessel
26  * Date:	1-JAN-2010
27  * Version:	5
28  *
29  * PUBLIC Functions include (39):
30  *
31  *	gmt_draw_map_scale 	 : Plot map scale
32  *	gmt_draw_map_rose 	 : Plot map rose
33  *	gmt_draw_map_panel 	 : Plot map panel
34  *	gmt_draw_front 		 : Draw a front line
35  *	gmt_geo_line 		 : Plots line in lon/lat on maps, takes care of periodicity jumps
36  *	gmt_plot_geo_ellipse 	 : Plots ellipse in lon/lat on maps, takes care of periodicity jumps
37  *	gmt_geo_polygons 	 : Plots polygon in lon/lat on maps, takes care of periodicity jumps
38  *	gmt_geo_rectangle 	 : Plots rectangle in lat/lon on maps, takes care of periodicity jumps
39  *	gmt_map_basemap 	 : Generic basemap function
40  *	gmt_map_clip_off 	 : Deactivate map region clip path
41  *	gmt_map_clip_on 	 : Activate map region clip path
42  *	gmt_BB_clip_on		 : Activate Bounding Box clip path
43  *	gmt_plane_perspective	 : Adds PS matrix to simulate perspective plotting
44  *	gmt_plot_line 		 : Plots path (in projected coordinates), takes care of boundary crossings
45  *	gmt_vertical_axis 	 : Draw 3-D vertical axes
46  *	gmt_xy_axis 		 : Draw x or y axis
47  *	gmt_linearx_grid 	 : Draw linear x grid lines
48  *	gmt_setfill              :
49  *	gmt_setfont              :
50  *	gmt_draw_map_inset       :
51  *	gmt_setpen               :
52  *	gmt_draw_custom_symbol   :
53  *	gmt_add_label_record     :
54  *	gmt_contlabel_save_begin :
55  *	gmt_contlabel_save_end   :
56  *	gmt_textpath_init        :
57  *	gmt_contlabel_plot       :
58  *	gmt_export2proj4         :
59  *	gmt_plotinit             :
60  *	gmt_plotcanvas           :
61  *	gmt_plotend              :
62  *	gmt_geo_polarcap_segment :
63  *	gmt_geo_vector           :
64  *	gmtlib_create_ps            :
65  *	gmtlib_free_ps_ptr          :
66  *	gmtlib_free_ps              :
67  *	gmtlib_read_ps              :
68  *	gmtlib_write_ps             :
69  *	gmtlib_duplicate_ps         :
70  *
71  */
72 
73 #include "gmt_dev.h"
74 #include "gmt_internals.h"
75 #ifdef _WIN32
76 #include <windows.h>
77 #include <tlhelp32.h>
78 #endif
79 
80 #define gmt_M_axis_is_geo_strict(C,axis) (((axis) == GMT_X && gmt_M_type (C, GMT_IN, axis) & GMT_IS_LON) || ((axis) == GMT_Y && gmt_M_type (C, GMT_IN, axis) & GMT_IS_LAT))
81 #define PSL_IZ(PSL,z) ((int)lrint ((z) * PSL->internal.dpu))
82 
83 /* Local variables to this file */
84 
85 static unsigned int GMT_n_annotations_skip[4] = {0, 0, 0, 0};
86 static size_t GMT_n_annotations[4] = {0, 0, 0, 0};
87 static size_t GMT_alloc_annotations[4] = {0, 0, 0, 0};
88 static double *GMT_x_annotation[4] = {NULL, NULL, NULL, NULL}, *GMT_y_annotation[4] = {NULL, NULL, NULL, NULL};
89 
90 /* THese functions are public but used in a static function so declared here to avoid resorting */
91 void gmt_linearx_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval);
92 
93 /* Get bitmapped 600 dpi GMT glyph for timestamp.  The glyph is a 90 x 220 pixel 1-bit image
94 	and it is here represented as ceil (220 / 8) * 90 = 2520 bytes */
95 
96 static unsigned char GMT_glyph[2520] = {
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
118 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
120 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
121 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
122 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0x80, 0x00,
123 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x0f,
125 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
126 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
127 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f,
128 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
129 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
130 0xc0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xfc, 0x00, 0x00, 0x01,
131 0xff, 0xc0, 0x00, 0x00, 0x01, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x0f,
132 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0x00, 0x00, 0x01, 0xff, 0xe0, 0x00, 0x00,
133 0x01, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x0f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x7f,
135 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff,
136 0xff, 0xc0, 0x00, 0x03, 0xff, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0xff, 0xff,
137 0xff, 0xe0, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x03,
138 0xff, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x0f,
139 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x07, 0xff, 0xf8, 0x00, 0x00,
140 0x00, 0xff, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
141 0x1f, 0xff, 0xfc, 0x3f, 0xff, 0xe0, 0x00, 0x07, 0xff, 0xf8, 0x00, 0x00, 0x00, 0xff, 0xff, 0x07,
142 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0x80, 0x07,
143 0xff, 0xf0, 0x00, 0x07, 0xff, 0xfc, 0x00, 0x00, 0x00, 0xff, 0xff, 0x07, 0x80, 0x00, 0xff, 0xfc,
144 0x00, 0x0f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xf8, 0x00, 0x07,
145 0xff, 0xfc, 0x00, 0x00, 0x00, 0xff, 0xff, 0x80, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x00, 0x0f,
146 0x00, 0x00, 0x00, 0x07, 0xff, 0xe0, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x07, 0xff, 0xfc, 0x00, 0x00,
147 0x00, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x1f,
148 0xff, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0,
149 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00,
150 0x3f, 0xf8, 0x00, 0x0f, 0xff, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x03, 0xff,
151 0x80, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x0f,
152 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x0f,
153 0x00, 0x00, 0x03, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x0f, 0xff, 0xff, 0x00, 0x00,
154 0x00, 0xff, 0xbf, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x07, 0xff,
155 0x80, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x0f, 0xfd, 0xff, 0x80, 0x00, 0x01, 0xff, 0xbf, 0xf0,
156 0x00, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x00, 0x00, 0x00,
157 0x1f, 0xf8, 0x00, 0x0f, 0xfd, 0xff, 0x80, 0x00, 0x01, 0xff, 0x9f, 0xf8, 0x00, 0x00, 0x00, 0x7f,
158 0xf0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x0f,
159 0xfd, 0xff, 0x80, 0x00, 0x01, 0xff, 0x9f, 0xf8, 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x0f,
160 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x1f, 0xfc, 0xff, 0xc0, 0x00,
161 0x01, 0xff, 0x9f, 0xf8, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x7f, 0xf0,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0xff, 0xc0, 0x00, 0x01, 0xff, 0x8f, 0xfc,
163 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x0f, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00,
164 0x00, 0x00, 0x00, 0x1f, 0xf8, 0xff, 0xe0, 0x00, 0x01, 0xff, 0x8f, 0xfc, 0x00, 0x00, 0x00, 0x0f,
165 0xfc, 0x00, 0x00, 0x0f, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f,
166 0xf8, 0x7f, 0xe0, 0x00, 0x03, 0xff, 0x8f, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x0f,
167 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x7f, 0xe0, 0x00,
168 0x03, 0xff, 0x8f, 0xfc, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x0f, 0x00, 0x03, 0xff, 0x80,
169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x7f, 0xf0, 0x00, 0x03, 0xff, 0x0f, 0xfe,
170 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x07, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
171 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x3f, 0xf0, 0x00, 0x03, 0xff, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x07,
172 0xff, 0x00, 0x00, 0x0f, 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f,
173 0xf8, 0x3f, 0xf8, 0x00, 0x07, 0xff, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x0f,
174 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xf8, 0x3f, 0xf8, 0x00,
175 0x07, 0xff, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x07, 0xff, 0x00,
176 0x00, 0x03, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xf8, 0x1f, 0xf8, 0x00, 0x07, 0xfe, 0x07, 0xfe,
177 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x07, 0xff, 0x00, 0x00, 0x03, 0xff, 0xff,
178 0xff, 0xf8, 0x00, 0x1f, 0xf8, 0x1f, 0xfc, 0x00, 0x0f, 0xfe, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x03,
179 0xff, 0x00, 0x00, 0x0f, 0x00, 0x07, 0xff, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x1f,
180 0xf8, 0x1f, 0xfc, 0x00, 0x0f, 0xfe, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x0f,
181 0x00, 0x07, 0xff, 0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x1f, 0xf8, 0x0f, 0xfc, 0x00,
182 0x1f, 0xfc, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x07, 0xff, 0x00,
183 0x00, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x1f, 0xf8, 0x0f, 0xfe, 0x00, 0x1f, 0xfc, 0x07, 0xfe,
184 0x00, 0x00, 0x00, 0x03, 0xff, 0x00, 0x00, 0x0f, 0x00, 0x07, 0xff, 0x00, 0x00, 0x03, 0xff, 0xff,
185 0xff, 0xfc, 0x00, 0x1f, 0xf8, 0x0f, 0xfe, 0x00, 0x1f, 0xf8, 0x07, 0xfe, 0x00, 0x00, 0x00, 0x07,
186 0xff, 0x00, 0x00, 0x0f, 0x00, 0x03, 0xff, 0x80, 0x00, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x1f,
187 0xf8, 0x07, 0xfe, 0x00, 0x3f, 0xf8, 0x0f, 0xfe, 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x0f,
188 0x00, 0x03, 0xff, 0x80, 0x00, 0x00, 0x03, 0xf8, 0x0f, 0xfc, 0x00, 0x1f, 0xf8, 0x07, 0xff, 0x00,
189 0x3f, 0xf0, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x00, 0x0f, 0x00, 0x03, 0xff, 0x80,
190 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x1f, 0xf8, 0x07, 0xff, 0x00, 0x7f, 0xf0, 0x0f, 0xfc,
191 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x0f, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00,
192 0x0f, 0xfc, 0x00, 0x1f, 0xf8, 0x03, 0xff, 0x00, 0x7f, 0xe0, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x0f,
193 0xfe, 0x00, 0x00, 0x0f, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x1f,
194 0xf8, 0x03, 0xff, 0x80, 0xff, 0xe0, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x0f,
195 0x00, 0x00, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x1f, 0xfc, 0x03, 0xff, 0x80,
196 0xff, 0xc0, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x1f, 0xfc, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x3f, 0xf0,
197 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x1f, 0xfc, 0x01, 0xff, 0x81, 0xff, 0xc0, 0x1f, 0xf8,
198 0x00, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00,
199 0x07, 0xfe, 0x00, 0x0f, 0xfc, 0x01, 0xff, 0x81, 0xff, 0x80, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x3f,
200 0xf0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, 0x00, 0x0f,
201 0xfc, 0x00, 0xff, 0xc3, 0xff, 0x80, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x7f, 0xf0, 0x00, 0x00, 0x0f,
202 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0x00, 0x0f, 0xfc, 0x00, 0xff, 0xc3,
203 0xff, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x03, 0xff,
204 0xc0, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0x00, 0xff, 0xc7, 0xfe, 0x00, 0x3f, 0xe0,
205 0x00, 0x00, 0x01, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x01, 0xff, 0xe0, 0x00, 0x00, 0x00,
206 0x0f, 0xff, 0x00, 0x0f, 0xfc, 0x00, 0x7f, 0xcf, 0xfe, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0x03, 0xff,
207 0x80, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x7f, 0xf8, 0x00, 0x00, 0x00, 0x0f, 0xff, 0x80, 0x0f,
208 0xfc, 0x00, 0x7f, 0xef, 0xfc, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x07, 0xff, 0x00, 0x00, 0x00, 0x0f,
209 0x00, 0x00, 0x00, 0x1f, 0xfe, 0x00, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x07, 0xfc, 0x00, 0x7f, 0xff,
210 0xf8, 0x00, 0xff, 0xc0, 0x00, 0x00, 0x0f, 0xfe, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x0f,
211 0xff, 0x80, 0x00, 0x00, 0x1f, 0xff, 0x80, 0x07, 0xfc, 0x00, 0x7f, 0xff, 0xf0, 0x00, 0xff, 0x80,
212 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x03, 0xff, 0xf0, 0x00, 0x00,
213 0x3f, 0xff, 0xc0, 0x07, 0xfc, 0x00, 0x3f, 0xff, 0xf0, 0x01, 0xff, 0x80, 0x00, 0x00, 0x3f, 0xf8,
214 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x07,
215 0xfc, 0x00, 0x3f, 0xff, 0xe0, 0x01, 0xff, 0x00, 0x00, 0x00, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x0f,
216 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xe0, 0x03, 0xff, 0xff, 0xe0, 0x07, 0xfc, 0x00, 0x3f, 0xff,
217 0xc0, 0x03, 0xfe, 0x00, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
218 0x0f, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xe0, 0x03, 0xfe, 0x00, 0x1f, 0xff, 0x80, 0x07, 0xfe, 0x00,
219 0x00, 0x01, 0xff, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff,
220 0xff, 0xcf, 0xf0, 0x03, 0xfe, 0x00, 0x1f, 0xff, 0x00, 0x07, 0xfc, 0x00, 0x00, 0x03, 0xff, 0x00,
221 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xc7, 0xf0, 0x03,
222 0xfe, 0x00, 0x1f, 0xff, 0x00, 0x0f, 0xf8, 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x0f,
223 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0x83, 0xf0, 0x03, 0xfe, 0x00, 0x0f, 0xfe,
224 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
225 0x00, 0x01, 0xff, 0xff, 0xff, 0x81, 0xf8, 0x01, 0xfe, 0x00, 0x0f, 0xfc, 0x00, 0x1f, 0xe0, 0x00,
226 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff,
227 0xff, 0x00, 0xf8, 0x01, 0xfe, 0x00, 0x0f, 0xf8, 0x00, 0x3f, 0xe0, 0x00, 0x00, 0x7f, 0xc0, 0x00,
228 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xff, 0xfc, 0x00, 0x00, 0x00,
229 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
230 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
231 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
232 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
233 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
234 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
235 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
237 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
242 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
246 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
248 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
249 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
250 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
251 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
253 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
254 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f,
255 };
256 
257 struct GMT_CIRCLE {	/* Helper variables needed to draw great or small circle heads */
258 	double lon[2], lat[2];	/* Coordinates of arc end points */
259 	double A[3], B[3];	/* Cartesian vector of arc end points */
260 	double P[3];		/* Cartesian vector of the pole */
261 	bool longway;		/* True if the arc > 180 degrees */
262 	double r0;		/* Arc length in degrees */
263 	double r;		/* Will be 180 less if longway is true, otherwise r == r0 */
264 	double colat;		/* Colatitude of circle relative to pole */
265 	double rot;		/* Full opening angle of vector arc */
266 };
267 
268 /* Local functions */
269 
270 /* Converting LaTeX strings to EPS files.  This is based on the discussion we had at
271  * https://github.com/GenericMappingTools/gmt/issues/4563#issuecomment-743374160.
272  * As long as the user has latex, dvips and required fonts installed this should work
273  * for everybody. P. Wessel, Dec 11, 2020.
274  */
275 
gmt_text_is_latex(struct GMT_CTRL * GMT,const char * string)276 bool gmt_text_is_latex (struct GMT_CTRL *GMT, const char *string) {
277 	/* Detect if string contains LaTeX commands, i.e., "....@[LaTeX...@[ ..." or  "....<math>LaTeX...</math> ..." */
278 	char *p;
279 	gmt_M_unused (GMT);
280 	if (string == NULL || string[0] == '\0') return false;
281 	if ((p = strstr (string, "@[")) && strstr (&p[1], "@[")) return true;
282 	if ((p = strstr (string, "<math>")) && strstr (&p[1], "</math>")) return true;
283 	return false;
284 }
285 
gmtplot_has_title_breaks(struct GMT_CTRL * GMT,const char * string)286 GMT_LOCAL bool gmtplot_has_title_breaks (struct GMT_CTRL *GMT, const char *string) {
287 	/* Returns true if string has line-break escape sequences in it */
288 	gmt_M_unused (GMT);
289 	if (string == NULL || string[0] == '\0') return false;
290 	return ((strstr (string, "<break>") || strstr (string, "@^")) ? true : false);
291 }
292 
gmtplot_latex_eps(struct GMT_CTRL * GMT,struct GMT_FONT * F,const char * string,struct imageinfo * h)293 GMT_LOCAL unsigned char * gmtplot_latex_eps (struct GMT_CTRL *GMT, struct GMT_FONT *F, const char *string, struct imageinfo *h) {
294 	/* Convert a string containing LaTeX syntax to an EPS image */
295 	unsigned int i, o;
296 	int error = 0;
297 	char *text = NULL, *font, *code;
298 	char tmpdir[PATH_MAX] = {""}, here[PATH_MAX] = {""}, file[PATH_MAX] = {""}, cmd[PATH_MAX] = {""};
299 	unsigned char *picture = NULL;
300 	FILE *fp = NULL;
301 	struct GMTAPI_CTRL *API = GMT->parent;
302 
303 	if (gmtplot_has_title_breaks (GMT, string)) {
304 		GMT_Report (API, GMT_MSG_ERROR, "LaTeX expressions are only allowed in single-line strings\n");
305 		return NULL;
306 	}
307 
308 	if (gmt_check_executable (GMT, "latex", "--version", NULL, NULL)) {
309 		GMT_Report (API, GMT_MSG_DEBUG, "latex found.\n");
310 	}
311 	else {
312 		GMT_Report (API, GMT_MSG_ERROR, "latex is not installed or not in your executable path - cannot process LaTeX to DVI.\n");
313 		return NULL;
314 	}
315 	if (gmt_check_executable (GMT, "dvips", "--version", NULL, NULL)) {
316 		GMT_Report (API, GMT_MSG_DEBUG, "dvips found.\n");
317 	}
318 	else {
319 		GMT_Report (API, GMT_MSG_ERROR, "dvips is not installed or not in your executable path - cannot convert DVI to EPS.\n");
320 		return NULL;
321 	}
322 
323 	/* Create unique directory for outputs, stored in tmpdir */
324 
325 	if (gmt_get_tempname (API, "gmt_latex", NULL, tmpdir))
326 		return NULL;
327 	if (gmt_mkdir (tmpdir)) {
328 		GMT_Report (API, GMT_MSG_ERROR, "Unable to create directory %s - exiting.\n", tmpdir);
329 		return NULL;
330 	}
331 	/* Remember where we are */
332 	if (getcwd (here, PATH_MAX) == NULL) {
333 		GMT_Report (API, GMT_MSG_ERROR, "Unable to determine current working directory - exiting.\n");
334 		return NULL;
335 	}
336 	gmt_replace_backslash_in_path (here);
337 	/* Use tmpdir as the current directory */
338 	if (chdir (tmpdir)) {
339 		GMT_Report (API, GMT_MSG_ERROR, "Unable to change directory to %s - exiting.\n", tmpdir);
340 		return NULL;
341 	}
342 	/* Create LaTeX file */
343 	sprintf (file, "gmt_eq.tex");
344 	if ((fp = fopen (file, "w")) == NULL) {
345 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: Could not create LaTeX file %s.\n", file);
346 		return NULL;
347 	}
348 
349 	/* Replace any @[ or <math> ... </math> with $ */
350 	text = strdup (string);
351 	for (i = o = 0; i < strlen (string); i++) {
352 		if (string[i] == '@' && string[i+1] == '[')
353 			text[o++] = '$', i++;
354 		else if (!strncmp (&string[i], "<math>", 6U))
355 			text[o++] = '$', i += 5;
356 		else if (!strncmp (&string[i], "</math>", 7U))
357 			text[o++] = '$', i += 6;
358 		else
359 			text[o++] = string[i];
360 	}
361 	text[o] = '\0'; /* Terminate the now shortened string */
362 
363 	/* Check title font selection and pick corresponding fontpackagename and fontcode, if possible */
364 	switch (F->id) {
365 		case 0:  case 1:  case 2:   case 3: font = "helvet";    code = "phv";	break;
366 		case 4:  case 5:  case 6:   case 7: font = "mathptmx";  code = "ptm";	break;
367 		case 8:  case 9:  case 10: case 11: font = "courier";   code = "pcr";	break;
368 		case 12: font = "symbol";   code = "psy";   break;
369 		case 13: case 14: case 15: case 16: font = "avantgar";  code = "pag";	break;
370 		case 17: case 18: case 19: case 20: font = "bookman";   code = "pbk";	break;
371 		case 21: case 22: case 23: case 24: font = "helvet";    code = "phv";	break;
372 		case 25: case 26: case 27: case 28: font = "newcent";   code = "pnc";	break;
373 		case 29: case 30: case 31: case 32: font = "mathpazo";  code = "ppl";	break;
374 		case 33: font = "zapfchan"; code = "pzc";	break;
375 		case 34: font = "zapfding"; code = "pzd";	break;
376 		default: font = code = NULL;    /* Go with default */
377 	}
378 	/* Write LaTeX file content */
379 	fprintf (fp, "\\documentclass{article}\n"); /* Default to 10p font size */
380 	if (font) { /* Impose a selected font family, otherwise take default Computer Modern */
381 		GMT_Report (API, GMT_MSG_DEBUG, "gmtplot_latex_eps: Selecting font %s [%s].\n", font, code);
382 		fprintf (fp, "\\usepackage[T1]{fontenc}\\usepackage[utf8]{inputenc}\\usepackage{%s}\n", font);
383 	}
384 	fprintf (fp, "\\begin{document}\n\\thispagestyle{empty}\n");    /* No page number */
385 	if (code) /* Select font */
386 		fprintf (fp, "\\fontfamily{%s}\\selectfont\n", code);
387 	fprintf (fp, "%s\n\\end{document}\n", text);
388 	fclose (fp);
389 	gmt_M_str_free (text);
390 
391 	/* Make script file for running latex and dvips */
392 #ifdef _WIN32
393 	sprintf (file, "gmt_eq.bat");
394 	sprintf (cmd, "start /B gmt_eq.bat");
395 #else
396 	sprintf (file, "gmt_eq.sh");
397 	sprintf (cmd, "sh gmt_eq.sh");
398 #endif
399 	if ((fp = fopen (file, "w")) == NULL) {
400 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: Could not create script file %s.\n", file);
401 		return NULL;
402 	}
403 #ifdef _WIN32
404 	fprintf (fp, "latex -interaction=nonstopmode gmt_eq.tex > NUL\ndvips -q -E gmt_eq.dvi -o equation.eps\n");
405 #else
406 	fprintf (fp, "latex -interaction=nonstopmode gmt_eq.tex > /dev/null\ndvips -q -E gmt_eq.dvi -o equation.eps\n");
407 #endif
408 	fclose (fp);
409 
410 	/* Run the script via a system call */
411 	if ((error = system (cmd))) {
412 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: Running \"%s\" returned error %d.\n", cmd, error);
413 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: Please run it manually to learn what LaTeX packages you are missing.\n", cmd, error);
414 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: The script and logs can be found here: %s\n", tmpdir);
415 		return NULL;
416 	}
417 	else {  /* Success, now remove the temp files but not worry about the return code here */
418 #ifdef _WIN32
419 		system ("del gmt_eq.*");
420 #else
421 		system ("rm -f gmt_eq.*");
422 #endif
423 	}
424 	/* Retrieve the EPS code */
425 	gmt_M_memset (h, 1U, struct imageinfo); /* Initialize information struct */
426 	if (PSL_loadeps (GMT->PSL, "equation.eps", h, &picture)) {
427 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: Unable to load EPS file equation.eps\n");
428 		return NULL;
429 	}
430 	/* Clean up */
431 	if (gmt_remove_dir (API, tmpdir, false)) {
432 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: Unable to remove temporary directory %s!\n", tmpdir);
433 		PSL_free (picture);
434 		return NULL;
435 	}
436 	/* Change back to original directory */
437 	if (chdir (here)) {
438 		GMT_Report (API, GMT_MSG_ERROR, "gmtplot_latex_eps: Unable to change directory back to %s - exiting.\n", here);
439 		PSL_free (picture);
440 		return NULL;
441 	}
442 
443 	/* Return the EPS data and size information via header */
444 	return picture;
445 }
446 
447 /*	GMT_LINEAR PROJECTION MAP BOUNDARY	*/
448 
gmtplot_linear_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)449 GMT_LOCAL void gmtplot_linear_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
450 	unsigned int cap = PSL->internal.line_cap;
451 	double x_length, y_length;
452 
453 	x_length = GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO];
454 	y_length = GMT->current.proj.rect[YHI] - GMT->current.proj.rect[YLO];
455 
456 	PSL_command (PSL, "/PSL_slant_y 0 def /PSL_slant_x 0 def\n");	/* Unless x-annotations are slanted there is no adjustment. PSL_slant_y may be revised in gmt_xy_axis */
457 
458 	if (GMT->current.map.frame.draw) {
459 		/* Temporarily change to square cap so rectangular frames have neat corners */
460 		PSL_setlinecap (PSL, PSL_SQUARE_CAP);
461 
462 		if (GMT->current.map.frame.side[W_SIDE]) gmt_xy_axis (GMT, GMT->current.proj.rect[XLO], GMT->current.proj.rect[YLO], y_length, s, n,
463 			&GMT->current.map.frame.axis[GMT_Y], true, GMT->current.map.frame.side[W_SIDE]);	/* West or left y-axis */
464 		if (GMT->current.map.frame.side[E_SIDE]) gmt_xy_axis (GMT, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YLO], y_length, s, n,
465 			&GMT->current.map.frame.axis[GMT_Y], false, GMT->current.map.frame.side[E_SIDE]);	/* East or right y-axis */
466 		if (GMT->current.map.frame.side[S_SIDE]) gmt_xy_axis (GMT, GMT->current.proj.rect[XLO], GMT->current.proj.rect[YLO], x_length, w, e,
467 			&GMT->current.map.frame.axis[GMT_X], true, GMT->current.map.frame.side[S_SIDE]);	/* South or lower x-axis */
468 		if (GMT->current.map.frame.side[N_SIDE]) gmt_xy_axis (GMT, GMT->current.proj.rect[XLO], GMT->current.proj.rect[YHI], x_length, w, e,
469 			&GMT->current.map.frame.axis[GMT_X], false, GMT->current.map.frame.side[N_SIDE]);	/* North or upper x-axis */
470 
471 		PSL_setlinecap (PSL, cap);	/* Reset back to default */
472 	}
473 	if (!GMT->current.map.frame.header[0] || GMT->current.map.frame.plotted_header) return;	/* No title (and optional subtitle) today */
474 
475 	PSL_comment (PSL, "Placing plot title\n");
476 
477 	if (!GMT->current.map.frame.draw || GMT->current.map.frame.side[N_SIDE] <= GMT_AXIS_DRAW || GMT->current.setting.map_frame_type == GMT_IS_INSIDE)
478 		PSL_defunits (PSL, "PSL_H_y", GMT->current.setting.map_title_offset);	/* No ticks or annotations, offset by map_title_offset only */
479 	else
480 		PSL_command (PSL, "/PSL_H_y PSL_L_y PSL_LH add %d add def\n", PSL_IZ (PSL, GMT->current.setting.map_title_offset));	/* For title adjustment */
481 
482 	PSL_command (PSL, "%d %d PSL_H_y add PSL_slant_y add M\n", PSL_IZ (PSL, 0.5 * x_length), PSL_IZ (PSL, y_length));
483 
484 	gmt_map_title (GMT, 0.0, 0.0);
485 }
486 
gmtplot_get_primary_annot(struct GMT_PLOT_AXIS * A)487 GMT_LOCAL unsigned int gmtplot_get_primary_annot (struct GMT_PLOT_AXIS *A) {
488 	/* Return the primary annotation item number [== GMT_ANNOT_UPPER if there are no unit set]*/
489 
490 	unsigned int i, no[2] = {GMT_ANNOT_UPPER, GMT_ANNOT_LOWER};
491 	double val[2], s;
492 
493 	for (i = 0; i < 2; i++) {
494 		val[i] = 0.0;
495 		if (!A->item[no[i]].active || A->item[no[i]].type == 'i' || A->item[no[i]].type == 'I') continue;
496 		switch (A->item[no[i]].unit) {
497 			case 'Y': case 'y':
498 				s = GMT_DAY2SEC_F * 365.25;
499 				break;
500 			case 'O': case 'o':
501 				s = GMT_DAY2SEC_F * 30.5;
502 				break;
503 			case 'U': case 'u':
504 				s = GMT_DAY2SEC_F * 7.0;
505 				break;
506 			case 'K': case 'k':
507 			case 'D': case 'd':
508 				s = GMT_DAY2SEC_F;
509 				break;
510 			case 'H': case 'h':
511 				s = GMT_HR2SEC_F;
512 				break;
513 			case 'M': case 'm':
514 				s = GMT_MIN2SEC_F;
515 				break;
516 			case 'C': case 'c':
517 				s = 1.0;
518 				break;
519 			default:	/* No unit specified - probably not a time axis */
520 				s = 1.0;
521 				break;
522 		}
523 		val[i] = A->item[no[i]].interval * s;
524 	}
525 	if (A->item[GMT_ANNOT_UPPER].special) return GMT_ANNOT_UPPER;
526 	return ((val[0] > val[1]) ? GMT_ANNOT_UPPER : GMT_ANNOT_LOWER);
527 }
528 
gmtplot_skip_second_annot(unsigned int item,double x,double x2[],unsigned int n,unsigned int primary)529 GMT_LOCAL bool gmtplot_skip_second_annot (unsigned int item, double x, double x2[], unsigned int n, unsigned int primary) {
530 	unsigned int i;
531 	bool found;
532 	double small;
533 
534 	if (n < 2) return (false);	/* Need at least two points so no need to skip */
535 	if (item == primary) return (false);		/* Not working on secondary annotation */
536 	if (!x2) return (false);			/* None given */
537 
538 	small = (x2[1] - x2[0]) * GMT_CONV4_LIMIT;
539 	for (i = 0, found = false; !found && i < n; i++)
540 		found = (fabs (x2[i] - x) < small);
541 	return (found);
542 }
543 
gmtplot_map_latline(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lat,double west,double east)544 GMT_LOCAL void gmtplot_map_latline (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lat, double west, double east)		/* Draws a line of constant latitude */ {
545 	uint64_t nn;
546 	double *llon = NULL, *llat = NULL;
547 #ifdef DEBUG
548 	uint64_t k;
549 	FILE *fp = NULL;
550 	char name[GMT_LEN64] = {""};
551 	if (GMT->hidden.gridline_debug && (GMT->hidden.gridline_kind == 'x' || GMT->hidden.gridline_val != lat)) return;
552 #endif
553 	nn = gmtlib_latpath (GMT, lat, west, east, &llon, &llat);
554 #ifdef DEBUG
555 	if (GMT->hidden.gridline_debug) {
556 		snprintf (name, GMT_LEN64, "gridline_y_%g_ll.txt", lat);
557 		fp = fopen (name, "w");
558 		for (k = 0; k < nn; k++) fprintf (fp, "%g\t%g\n", llon[k], llat[k]);
559 		fclose (fp);
560 	}
561 #endif
562 	GMT->current.plot.n = gmt_geo_to_xy_line (GMT, llon, llat, nn);
563 #ifdef DEBUG
564 	if (GMT->hidden.gridline_debug) {
565 		snprintf (name, GMT_LEN64, "gridline_y_%g_xy.txt", lat);
566 		fp = fopen (name, "w");
567 		for (k = 0; k < GMT->current.plot.n; k++) fprintf (fp, "%g\t%g\t%d\n", GMT->current.plot.x[k], GMT->current.plot.y[k], GMT->current.plot.pen[k]);
568 		fclose (fp);
569 	}
570 #endif
571 
572 	if (GMT->current.plot.n > 1) {	/* Need at least 2 points for a line */
573 		PSL_comment (PSL, "Lat = %g\n", lat);
574 		if (GMT->current.map.parallel_straight) {	/* Simplify to a 2-point straight line */
575 			GMT->current.plot.x[1] = GMT->current.plot.x[GMT->current.plot.n-1];
576 			GMT->current.plot.y[1] = GMT->current.plot.y[GMT->current.plot.n-1];
577 			GMT->current.plot.pen[1] = GMT->current.plot.pen[GMT->current.plot.n-1];
578 			GMT->current.plot.n = 2;
579 		}
580 		gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
581 	}
582 	gmt_M_free (GMT, llon);
583 	gmt_M_free (GMT, llat);
584 }
585 
gmtplot_map_lonline(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lon,double south,double north)586 GMT_LOCAL void gmtplot_map_lonline (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lon, double south, double north)	/* Draws a line of constant longitude */ {
587 	uint64_t nn;
588 	double *llon = NULL, *llat = NULL;
589 #ifdef DEBUG
590 	uint64_t k;
591 	FILE *fp = NULL;
592 	char name[GMT_LEN64] = {""};
593 	if (GMT->hidden.gridline_debug && (GMT->hidden.gridline_kind == 'y' || GMT->hidden.gridline_val != lon)) return;
594 #endif
595 	nn = gmtlib_lonpath (GMT, lon, south, north, &llon, &llat);
596 #ifdef DEBUG
597 	if (GMT->hidden.gridline_debug) {
598 		snprintf (name, GMT_LEN64, "gridline_x_%g_ll.txt", lon);
599 		fp = fopen (name, "w");
600 		for (k = 0; k < nn; k++) fprintf (fp, "%g\t%g\n", llon[k], llat[k]);
601 		fclose (fp);
602 	}
603 #endif
604 	GMT->current.plot.n = gmt_geo_to_xy_line (GMT, llon, llat, nn);
605 #ifdef DEBUG
606 	if (GMT->hidden.gridline_debug) {
607 		snprintf (name, GMT_LEN64, "gridline_x_%g_xy.txt", lon);
608 		fp = fopen (name, "w");
609 		for (k = 0; k < GMT->current.plot.n; k++) fprintf (fp, "%g\t%g\t%d\n", GMT->current.plot.x[k], GMT->current.plot.y[k], GMT->current.plot.pen[k]);
610 		fclose (fp);
611 	}
612 #endif
613 
614 	if (GMT->current.plot.n > 1) {	/* Need at least 2 points for a line */
615 		PSL_comment (PSL, "Lon = %g\n", lon);
616 		if (GMT->current.map.meridian_straight) {	/* Simplify to a 2-point straight line */
617 			GMT->current.plot.x[1] = GMT->current.plot.x[GMT->current.plot.n-1];
618 			GMT->current.plot.y[1] = GMT->current.plot.y[GMT->current.plot.n-1];
619 			GMT->current.plot.pen[1] = GMT->current.plot.pen[GMT->current.plot.n-1];
620 			GMT->current.plot.n = 2;
621 		}
622 		gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
623 	}
624 	gmt_M_free (GMT, llon);
625 	gmt_M_free (GMT, llat);
626 }
627 
gmtplot_linearx_oblgrid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)628 GMT_LOCAL void gmtplot_linearx_oblgrid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
629 	/* x gridlines in oblique coordinates for all but the Oblique Mercator projection [which already is oblique] */
630 	double *x = NULL, *lon = NULL, *lat = NULL, *lat_obl = NULL, tval, p_cap, s_cap;
631 	unsigned int idup = 0, i, j, k, nx, np, nc1 = 0, nc2, npc, np1;
632 	bool cap = false;
633 	gmt_M_unused(PSL); gmt_M_unused(w); gmt_M_unused(e);
634 
635 	/* Ideally we should determine the w/e/s/n of the oblique coordinates but here we will simply
636 	 * create oblique coordinates for the full 0/360/-90/90, convert to regular coordinates and
637 	 * then truncate points outside the actual w/e/s/n */
638 
639 	/* Do we have duplicate e and w boundaries ? */
640 	p_cap = fabs (GMT->current.setting.map_polar_cap[0]);
641 	s_cap = -p_cap;
642 	idup = (gmt_M_is_azimuthal(GMT)) ? 1 : 0;
643 	cap = !doubleAlmostEqual (p_cap, 90.0);	/* true if we have a polar cap specified */
644 	tval = (n - s) * GMT->current.setting.map_line_step / GMT->current.map.height;
645 
646 	nx = gmtlib_linear_array (GMT, 0.0, TWO_PI, D2R * dval, D2R * GMT->current.map.frame.axis[GMT_X].phase, &x);
647 	np = gmtlib_linear_array (GMT, -90.0, 90.0, tval, 0.0, &lat_obl);
648 	np1 = nc2 = np - 1;	/* Nominal number of points in path */
649 	lon = gmt_M_memory (GMT, NULL, np+2, double);	/* Allow 2 more slots for possibly inserted cap-latitudes */
650 	lat = gmt_M_memory (GMT, NULL, np+2, double);
651 	for (i = 0; i < nx - idup; i++) {	/* For each oblique meridian to draw */
652 		/* Create lon,lat arrays of oblique coordinates for this meridian */
653 		for (k = j = 0; k < np; k++, j++) {
654 			gmtlib_iobl (GMT, &lon[j], &lat[j], x[i], D2R * lat_obl[k]);	/* Get regular coordinates of this point */
655 			lon[j] *= R2D;	lat[j] *= R2D;	/* Convert back to degrees */
656 			if (lat_obl[k] < s_cap && k < np1 && lat_obl[k+1] > s_cap)	{	/* Must insert S pole cap latitude point */
657 				j++;	gmtlib_iobl (GMT, &lon[j], &lat[j], x[i], D2R * s_cap);
658 				lon[j] *= R2D;	lat[j] *= R2D;	/* Back to degrees */
659 				nc1 = j;
660 			}
661 			else if (lat_obl[k] < p_cap && k < np1 && lat_obl[k+1] > p_cap) {	/* Must insert N pole cap latitude point */
662 				j++; gmtlib_iobl (GMT, &lon[j], &lat[j], x[i], D2R * p_cap);
663 				lon[j] *= R2D;	lat[j] *= R2D;	/* Back to degrees */
664 				nc2 = j;
665 			}
666 		}
667 		if (cap) {	/* Only plot the line between the two polar caps */
668 			npc = nc2 - nc1 + 1;	/* Number of points along meridian bounded by caps */
669 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, &lon[nc1], &lat[nc1], npc)) == 0) continue;
670 		}
671 		else {		/* No polar cap in effect, plot entire meridian */
672 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, j)) == 0) continue;
673 		}
674 		gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
675 	}
676 	if (nx) gmt_M_free (GMT, x);
677 	if (cap) {	/* Draw the polar cap(s) meridians with a separate lon spacing */
678 		nx = gmtlib_linear_array (GMT, 0.0, TWO_PI, D2R * GMT->current.setting.map_polar_cap[1], D2R * GMT->current.map.frame.axis[GMT_X].phase, &x);
679 		for (i = 0; i < nx - idup; i++) {
680 			for (k = j = 0; k < np; k++, j++) {
681 				gmtlib_iobl (GMT, &lon[j], &lat[j], x[i], D2R * lat_obl[k]);	/* Get regular coordinates of this point */
682 				lon[j] *= R2D;	lat[j] *= R2D;	/* Back to degrees */
683 				if (lat_obl[k] < s_cap && k < np1 && lat_obl[k+1] > s_cap)	{	/* Must insert S pole cap latitude point */
684 					j++;	gmtlib_iobl (GMT, &lon[j], &lat[j], x[i], D2R * s_cap);
685 					lon[j] *= R2D;	lat[j] *= R2D;	/* Back to degrees */
686 					nc1 = j;
687 				}
688 				else if (lat_obl[k] < p_cap && k < np1 && lat_obl[k+1] > p_cap) {	/* Must insert N pole cap latitude point */
689 					j++; gmtlib_iobl (GMT, &lon[j], &lat[j], x[i], D2R * p_cap);
690 					lon[j] *= R2D;	lat[j] *= R2D;	/* Back to degrees */
691 					nc2 = j;
692 				}
693 			}
694 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, nc1+1)) > 0)
695 				gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
696 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, &lon[nc2], &lat[nc2], j-nc2)) > 0)
697 				gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
698 		}
699 		if (nx) gmt_M_free (GMT, x);
700 	}
701 	gmt_M_free (GMT, lat_obl);
702 	gmt_M_free (GMT, lon);
703 	gmt_M_free (GMT, lat);
704 }
705 
gmtplot_lineary_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)706 GMT_LOCAL void gmtplot_lineary_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
707 	double *y = NULL;
708 	char *type = (gmt_M_y_is_lat (GMT, GMT_IN)) ? "parallel" : "y";
709 	unsigned int i, ny = 0;
710 
711 	if (GMT->current.proj.z_down) {
712 		if (GMT->current.proj.z_down == GMT_ZDOWN_Z) /* z = n - r */
713 			ny = gmtlib_linear_array (GMT, 0.0, GMT->current.proj.z_radius-s, dval, GMT->current.map.frame.axis[GMT_Y].phase, &y);
714 		else if (GMT->current.proj.z_down == GMT_ZDOWN_ZP) /* z = n - r */
715 			ny = gmtlib_linear_array (GMT, GMT->current.proj.z_radius-n, GMT->current.proj.z_radius-s, dval, GMT->current.map.frame.axis[GMT_Y].phase, &y);
716 		for (i = 0; i < ny; i++) {
717 			if (GMT->current.proj.z_down == GMT_ZDOWN_ZP)
718 				y[i] = GMT->current.proj.z_radius - y[i];	/* These are the z values needed for positioning */
719 			else
720 				y[i] = GMT->common.R.wesn[YHI] - y[i];	/* These are the radial values needed for positioning */
721 		}
722 	}
723 	else
724 		ny = gmtlib_linear_array (GMT, s, n, dval, GMT->current.map.frame.axis[GMT_Y].phase, &y);
725 	for (i = 0; i < ny; i++) {
726 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Draw %s = %g from %g to %g\n", type, y[i], w, e);
727 		gmtplot_map_latline (GMT, PSL, y[i], w, e);
728 	}
729 	if (ny) gmt_M_free (GMT, y);
730 
731 }
732 
gmtplot_x_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double s,double n,double * x,unsigned int nx)733 GMT_LOCAL void gmtplot_x_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double s, double n, double *x, unsigned int nx) {
734 	unsigned int i;
735 	double x1, y1, x2, y2;
736 
737 	for (i = 0; i < nx; i++) {
738 		if (gmt_M_x_is_lon (GMT, GMT_IN))
739 			gmtplot_map_lonline (GMT, PSL, x[i], s, n);
740 		else {
741 			gmt_geo_to_xy (GMT, x[i], s, &x1, &y1);
742 			gmt_geo_to_xy (GMT, x[i], n, &x2, &y2);
743 			PSL_plotsegment (PSL, x1, y1, x2, y2);
744 		}
745 	}
746 }
747 
gmtplot_lineary_oblgrid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)748 GMT_LOCAL void gmtplot_lineary_oblgrid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
749 	/* y gridlines in oblique coordinates for all but the Oblique Mercator projection [which already is oblique] */
750 
751 	double *y = NULL, *lon = NULL, *lon_obl = NULL, *lat = NULL, tval, p_cap;
752 	bool cap;
753 	unsigned int i, k, ny, np;
754 	gmt_M_unused(PSL); gmt_M_unused(s); gmt_M_unused(n);
755 
756 	/* Ideally we should determine the w/e/s/n of the oblique coordinates but here we will simply
757 	 * create oblique coordinates for the full 0/360/-90/90, convert to regular coordinates and
758 	 * then truncate points outside the actual w/e/s/n */
759 
760 	ny = gmtlib_linear_array (GMT, -M_PI_2, M_PI_2, D2R * dval, D2R * GMT->current.map.frame.axis[GMT_Y].phase, &y);
761 	tval = (e - w) * GMT->current.setting.map_line_step / GMT->current.map.width;
762 	np = gmtlib_linear_array (GMT, 0.0, TWO_PI, D2R * tval, 0.0, &lon_obl);
763 	lon = gmt_M_memory (GMT, NULL, np+2, double);	/* Allow 2 more slots for possibly inserted cap-latitudes */
764 	lat = gmt_M_memory (GMT, NULL, np+2, double);
765 	for (i = 0; i < ny; i++) {
766 		for (k = 0; k < np; k++) {
767 			gmtlib_iobl (GMT, &lon[k], &lat[k], lon_obl[k], y[i]);	/* Get regular coordinates of this point */
768 			lon[k] *= R2D;	lat[k] *= R2D;	/* Convert to degrees */
769 		}
770 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, np)) == 0) continue;
771 		gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
772 	}
773 	if (ny) gmt_M_free (GMT, y);
774 	p_cap = fabs (GMT->current.setting.map_polar_cap[0]);
775 	cap = !doubleAlmostEqual (p_cap, 90.0);	/* true if we have a polar cap specified */
776 	if (cap) {	/* Draw the polar cap(s) with a separate spacing */
777 		p_cap = D2R * GMT->current.setting.map_polar_cap[0];
778 		for (k = 0; k < np; k++) {	/* S polar cap */
779 			gmtlib_iobl (GMT, &lon[k], &lat[k], lon_obl[k], -p_cap);	/* Get regular coordinates of this point */
780 			lon[k] *= R2D;	lat[k] *= R2D;	/* Convert to degrees */
781 		}
782 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, np)) > 0)
783 			gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
784 		for (k = 0; k < np; k++) {	/* N polar cap */
785 			gmtlib_iobl (GMT, &lon[k], &lat[k], lon_obl[k], p_cap);	/* Get regular coordinates of this point */
786 			lon[k] *= R2D;	lat[k] *= R2D;	/* Convert to degrees */
787 		}
788 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, np)) > 0)
789 			gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
790 	}
791 	gmt_M_free (GMT, lon_obl);
792 	gmt_M_free (GMT, lon);
793 	gmt_M_free (GMT, lat);
794 }
795 
gmtplot_y_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double * y,unsigned int ny)796 GMT_LOCAL void gmtplot_y_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double *y, unsigned int ny) {
797 	unsigned int i;
798 	double x1, y1, x2, y2;
799 
800 	for (i = 0; i < ny; i++) {
801 		if (gmt_M_y_is_lat (GMT, GMT_IN))
802 			gmtplot_map_latline (GMT, PSL, y[i], w, e);
803 		else {
804 			gmt_geo_to_xy (GMT, w, y[i], &x1, &y1);
805 			gmt_geo_to_xy (GMT, e, y[i], &x2, &y2);
806 			PSL_plotsegment (PSL, x1, y1, x2, y2);
807 		}
808 	}
809 }
810 
plot_timex_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,unsigned int item)811 void plot_timex_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, unsigned int item) {
812 	unsigned int nx;
813 	double *x = NULL;
814 
815 	nx = gmtlib_time_array (GMT, w, e, &GMT->current.map.frame.axis[GMT_X].item[item], &x);
816 	gmtplot_x_grid (GMT, PSL, s, n, x, nx);
817 	if (x) gmt_M_free (GMT, x);
818 }
819 
gmtplot_timey_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,unsigned int item)820 GMT_LOCAL void gmtplot_timey_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, unsigned int item) {
821 	unsigned int ny;
822 	double *y = NULL;
823 
824 	ny = gmtlib_time_array (GMT, s, n, &GMT->current.map.frame.axis[GMT_Y].item[item], &y);
825 	gmtplot_y_grid (GMT, PSL, w, e, y, ny);
826 	if (y) gmt_M_free (GMT, y);
827 }
828 
gmtplot_logx_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)829 GMT_LOCAL void gmtplot_logx_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
830 	unsigned int nx;
831 	double *x = NULL;
832 
833 	nx = gmtlib_log_array (GMT, w, e, dval, &x);
834 	gmtplot_x_grid (GMT, PSL, s, n, x, nx);
835 	if (x) gmt_M_free (GMT, x);
836 }
837 
gmtplot_logy_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)838 GMT_LOCAL void gmtplot_logy_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
839 	unsigned int ny;
840 	double *y = NULL;
841 
842 	ny = gmtlib_log_array (GMT, s, n, dval, &y);
843 	gmtplot_y_grid (GMT, PSL, w, e, y, ny);
844 	if (y) gmt_M_free (GMT, y);
845 }
846 
gmtplot_powx_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)847 GMT_LOCAL void gmtplot_powx_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
848 	unsigned int nx;
849 	double *x = NULL;
850 
851 	nx = gmtlib_pow_array (GMT, w, e, dval, 0, &x);
852 	gmtplot_x_grid (GMT, PSL, s, n, x, nx);
853 	if (x) gmt_M_free (GMT, x);
854 }
855 
gmtplot_powy_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)856 GMT_LOCAL void gmtplot_powy_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
857 	unsigned int ny;
858 	double *y = NULL;
859 
860 	ny = gmtlib_pow_array (GMT, s, n, dval, 1, &y);
861 	gmtplot_y_grid (GMT, PSL, w, e, y, ny);
862 	if (y) gmt_M_free (GMT, y);
863 }
864 
gmtplot_shift_gridline(struct GMT_CTRL * GMT,double val,unsigned int type)865 GMT_LOCAL double gmtplot_shift_gridline (struct GMT_CTRL *GMT, double val, unsigned int type) {
866 	/* Only for oblique projections: If any of the corners are exactly multiples of annotation
867 	 * or tick intervals then the gridline intersection may fail (tangent or slightly outside
868 	 * due to round-off).  We determine which gridlines go through the corners and shift them
869 	 * a tiny bit to the inside to ensure crossings */
870 	double shift = 0.0;
871 	if (!GMT->common.R.oblique) return shift;	/* Return zero if not an oblique projection */
872 
873 	if (type == GMT_X) {
874 		if (gmt_M_360_range (val, GMT->common.R.wesn_orig[XLO])) val = GMT->common.R.wesn_orig[XLO];
875 		else if (gmt_M_360_range (val, GMT->common.R.wesn_orig[XHI])) val = GMT->common.R.wesn_orig[XHI];
876 		if (doubleAlmostEqualZero (val, GMT->common.R.wesn_orig[XLO])) shift = +GMT_CONV4_LIMIT * fabs (GMT->common.R.wesn_orig[XHI] - GMT->common.R.wesn_orig[XLO]);	/* Add this to lon to get a slightly larger longitude to ensure crossing */
877 		else if (doubleAlmostEqualZero (val, GMT->common.R.wesn_orig[XHI])) shift = -GMT_CONV4_LIMIT * fabs (GMT->common.R.wesn_orig[XHI] - GMT->common.R.wesn_orig[XLO]);	/* Add this to lon to get a slightly smaller longitude to ensure crossing */
878 	}
879 	else {
880 		if (doubleAlmostEqualZero (val, GMT->common.R.wesn_orig[YLO])) shift = +GMT_CONV4_LIMIT * fabs (GMT->common.R.wesn_orig[YHI] - GMT->common.R.wesn_orig[YLO]);	/* Add this to lon to get a slightly larger longitude to ensure crossing */
881 		else if (doubleAlmostEqualZero (val, GMT->common.R.wesn_orig[YHI])) shift = -GMT_CONV4_LIMIT * fabs (GMT->common.R.wesn_orig[YHI] - GMT->common.R.wesn_orig[YLO]);	/* Add this to lon to get a slightly smaller longitude to ensure crossing */
882 
883 	}
884 	if (shift != 0.0) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Adjusted argument %g by %g\n", val, shift);
885 	return shift;
886 }
887 
888 /*	FANCY RECTANGULAR PROJECTION MAP BOUNDARY	*/
889 
gmtplot_fancy_frame_offset(struct GMT_CTRL * GMT,double angle,double shift[2])890 GMT_LOCAL void gmtplot_fancy_frame_offset (struct GMT_CTRL *GMT, double angle, double shift[2]) {
891 	/* Given the angle of the axis, return the coordinate adjustments needed to
892 	 * shift in order to plot the outer 1-2 parallel frame lines (shift[0|1] */
893 
894 	double s, c;
895 	sincos (angle, &s, &c);
896 	shift[0] =  GMT->current.setting.map_frame_width * s;
897 	shift[1] = -GMT->current.setting.map_frame_width * c;
898 }
899 
gmtplot_fancy_frame_straightlon_checkers(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,bool secondary_too)900 GMT_LOCAL void gmtplot_fancy_frame_straightlon_checkers (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, bool secondary_too) {
901 	/* Plot checkers along straight longitude boundaries */
902 	int i, k, nx;
903 	unsigned int shade, item[2] = {GMT_TICK_UPPER, GMT_TICK_LOWER};
904 	double dx, w1, val, v1, v2, x1, x2, y1, y2, shift_s[2], shift_n[2], scale[2];
905 	struct GMT_PLOT_AXIS_ITEM *T = NULL;
906 
907 	scale[0] = (secondary_too) ? 0.5 : 1.0;
908 	scale[1] = 1.5;
909 
910 	gmt_geo_to_xy (GMT, e, s, &x1, &y1);
911 	gmt_geo_to_xy (GMT, w, s, &x2, &y2);
912 	gmtplot_fancy_frame_offset (GMT, d_atan2 (y2 - y1, x2 - x1), shift_s);
913 
914 	gmt_geo_to_xy (GMT, w, n, &x1, &y1);
915 	gmt_geo_to_xy (GMT, e, n, &x2, &y2);
916 	gmtplot_fancy_frame_offset (GMT, d_atan2 (y2 - y1, x2 - x1), shift_n);
917 
918 	for (k = 0; k < 1 + secondary_too; k++) {
919 		T = &GMT->current.map.frame.axis[GMT_X].item[item[k]];
920 		if (T->active) {
921 			dx = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, T);
922 			shade = (urint (floor ((w - GMT->current.map.frame.axis[GMT_X].phase)/ dx))) % 2;
923 			w1 = floor ((w - GMT->current.map.frame.axis[GMT_X].phase)/ dx) * dx + GMT->current.map.frame.axis[GMT_X].phase;
924 			nx = (w1 > e) ? -1 : irint (((e - w1) / dx + GMT_CONV4_LIMIT));
925 			for (i = 0; i <= nx; i++) {
926 				shade = !shade;
927 				val = w1 + i * dx;
928 				v1 = MAX (val, w);
929 				v2 = MIN (val + dx, e);
930 				if (v2 - v1 < GMT_CONV8_LIMIT) continue;
931 				PSL_setcolor (PSL, shade ? GMT->current.setting.map_frame_pen.rgb : GMT->PSL->init.page_rgb, PSL_IS_STROKE);
932 				if (GMT->current.map.frame.side[S_SIDE] & GMT_AXIS_TICK) {
933 					gmt_geo_to_xy (GMT, v1, s, &x1, &y1);
934 					gmt_geo_to_xy (GMT, v2, s, &x2, &y2);
935 					PSL_plotsegment (PSL, x1-0.5*scale[k]*shift_s[0], y1-0.5*scale[k]*shift_s[1], x2-0.5*scale[k]*shift_s[0], y2-0.5*scale[k]*shift_s[1]);
936 				}
937 				if (GMT->current.map.frame.side[N_SIDE] & GMT_AXIS_TICK) {
938 					gmt_geo_to_xy (GMT, v1, n, &x1, &y1);
939 					gmt_geo_to_xy (GMT, v2, n, &x2, &y2);
940 					PSL_plotsegment (PSL, x1-0.5*scale[k]*shift_n[0], y1-0.5*scale[k]*shift_n[1], x2-0.5*scale[k]*shift_n[0], y2-0.5*scale[k]*shift_n[1]);
941 				}
942 			}
943 		}
944 	}
945 }
946 
gmtplot_fancy_frame_curvedlon_checkers(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,bool secondary_too)947 GMT_LOCAL void gmtplot_fancy_frame_curvedlon_checkers (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, bool secondary_too) {
948 	/* Plot checkers along curved longitude boundaries */
949 	int i, k, nx;
950 	unsigned int shade, item[2] = {GMT_TICK_UPPER, GMT_TICK_LOWER};
951 	double dx, w1, v1, v2, val, x1, x2, y1, y2, az1, az2, dr, scale[2], radius_s, radius_n;
952 	struct GMT_PLOT_AXIS_ITEM *T;
953 
954 	scale[0] = (secondary_too) ? 0.5 : 1.0;
955 	scale[1] = 1.5;
956 	dr = 0.5 * GMT->current.setting.map_frame_width;
957 	gmt_geo_to_xy (GMT, w, s, &x1, &y1);
958 	gmt_geo_to_xy (GMT, w, n, &x2, &y2);
959 	radius_s = hypot (x1 - GMT->current.proj.c_x0, y1 - GMT->current.proj.c_y0);
960 	radius_n = hypot (x2 - GMT->current.proj.c_x0, y2 - GMT->current.proj.c_y0);
961 
962 	for (k = 0; k < 1 + secondary_too; k++) {
963 		T = &GMT->current.map.frame.axis[GMT_X].item[item[k]];
964 		if (T->active) {
965 			dx = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, T);
966 			shade = urint (floor ((w - GMT->current.map.frame.axis[GMT_X].phase) / dx)) % 2;
967 			w1 = floor ((w - GMT->current.map.frame.axis[GMT_X].phase)/dx) * dx + GMT->current.map.frame.axis[GMT_X].phase;
968 			nx = (w1 > e) ? -1 : irint ((e-w1) / dx + GMT_CONV4_LIMIT);
969 			for (i = 0; i <= nx; i++) {
970 				shade = !shade;
971 				val = w1 + i * dx;
972 				v1 = MAX (val, w);
973 				v2 = MIN (val + dx, e);
974 				if (v2 - v1 < GMT_CONV8_LIMIT) continue;
975 				PSL_setcolor (PSL, shade ? GMT->current.setting.map_frame_pen.rgb : GMT->PSL->init.page_rgb, PSL_IS_STROKE);
976 				if (GMT->current.map.frame.side[S_SIDE] & GMT_AXIS_TICK) {
977 					gmt_geo_to_xy (GMT, v2, s, &x1, &y1);
978 					gmt_geo_to_xy (GMT, v1, s, &x2, &y2);
979 					az1 = d_atan2d (y1 - GMT->current.proj.c_y0, x1 - GMT->current.proj.c_x0);
980 					az2 = d_atan2d (y2 - GMT->current.proj.c_y0, x2 - GMT->current.proj.c_x0);
981 					if (GMT->current.proj.north_pole) {
982 						if (az1 < az2) az1 += 360.0;
983 						PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius_s+scale[k]*dr, az2, az1, PSL_MOVE|PSL_STROKE);
984 					}
985 					else {
986 						if (az2 < az1) az2 += 360.0;
987 						PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius_s-scale[k]*dr, az1, az2, PSL_MOVE|PSL_STROKE);
988 					}
989 				}
990 				if (GMT->current.map.frame.side[N_SIDE] & GMT_AXIS_TICK) {
991 					gmt_geo_to_xy (GMT, v2, n, &x1, &y1);
992 					gmt_geo_to_xy (GMT, v1, n, &x2, &y2);
993 					az1 = d_atan2d (y1 - GMT->current.proj.c_y0, x1 - GMT->current.proj.c_x0);
994 					az2 = d_atan2d (y2 - GMT->current.proj.c_y0, x2 - GMT->current.proj.c_x0);
995 					if (GMT->current.proj.north_pole) {
996 						if (az1 < az2) az1 += 360.0;
997 						PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius_n-scale[k]*dr, az2, az1, PSL_MOVE|PSL_STROKE);
998 					}
999 					else {
1000 						if (az2 < az1) az2 += 360.0;
1001 						PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius_n+scale[k]*dr, az1, az2, PSL_MOVE|PSL_STROKE);
1002 					}
1003 				}
1004 			}
1005 		}
1006 	}
1007 }
1008 
gmtplot_fancy_frame_straightlat_checkers(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,bool secondary_too)1009 GMT_LOCAL void gmtplot_fancy_frame_straightlat_checkers (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, bool secondary_too) {
1010 	/* Plot checkers along straight latitude boundaries */
1011 	int i, k, ny;
1012 	unsigned int shade, item[2] = {GMT_TICK_UPPER, GMT_TICK_LOWER};
1013 	double dy, s1, val, v1, v2, x1, x2, y1, y2, shift_w[2], shift_e[2], scale[2];
1014 	struct GMT_PLOT_AXIS_ITEM *T = NULL;
1015 
1016 	scale[0] = (secondary_too) ? 0.5 : 1.0;
1017 	scale[1] = 1.5;
1018 
1019 	gmt_geo_to_xy (GMT, w, s, &x1, &y1);
1020 	gmt_geo_to_xy (GMT, w, n, &x2, &y2);
1021 	gmtplot_fancy_frame_offset (GMT, d_atan2 (y2 - y1, x2 - x1), shift_w);
1022 
1023 	gmt_geo_to_xy (GMT, e, s, &x1, &y1);
1024 	gmt_geo_to_xy (GMT, e, n, &x2, &y2);
1025 	gmtplot_fancy_frame_offset (GMT, d_atan2 (y2 - y1, x2 - x1), shift_e);
1026 
1027 	/* Tick S-N axes */
1028 
1029 	for (k = 0; k < 1 + secondary_too; k++) {
1030 		T = &GMT->current.map.frame.axis[GMT_Y].item[item[k]];
1031 		if (T->active) {
1032 			dy = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, T);
1033 			shade = urint (floor ((s - GMT->current.map.frame.axis[GMT_Y].phase) / dy)) % 2;
1034 			s1 = floor((s - GMT->current.map.frame.axis[GMT_Y].phase)/dy) * dy + GMT->current.map.frame.axis[GMT_Y].phase;
1035 			ny = (s1 > n) ? -1 : irint ((n-s1) / dy + GMT_CONV4_LIMIT);
1036 			for (i = 0; i <= ny; i++) {
1037 				shade = !shade;
1038 				val = s1 + i * dy;
1039 				v1 = MAX (val, s);
1040 				v2 = MIN (val + dy, n);
1041 				if (v2 - v1 < GMT_CONV8_LIMIT) continue;
1042 				PSL_setcolor (PSL, shade ? GMT->current.setting.map_frame_pen.rgb : GMT->PSL->init.page_rgb, PSL_IS_STROKE);
1043 				if (GMT->current.map.frame.side[W_SIDE] & GMT_AXIS_TICK) {
1044 					gmt_geo_to_xy (GMT, w, v1, &x1, &y1);
1045 					gmt_geo_to_xy (GMT, w, v2, &x2, &y2);
1046 					PSL_plotsegment (PSL, x1-0.5*scale[k]*shift_w[0], y1-0.5*scale[k]*shift_w[1], x2-0.5*scale[k]*shift_w[0], y2-0.5*scale[k]*shift_w[1]);
1047 				}
1048 				if (GMT->current.map.frame.side[E_SIDE] & GMT_AXIS_TICK) {
1049 					gmt_geo_to_xy (GMT, e, v1, &x1, &y1);
1050 					gmt_geo_to_xy (GMT, e, v2, &x2, &y2);
1051 					PSL_plotsegment (PSL, x1+0.5*scale[k]*shift_e[0], y1+0.5*scale[k]*shift_e[1], x2+0.5*scale[k]*shift_e[0], y2+0.5*scale[k]*shift_e[1]);
1052 				}
1053 			}
1054 		}
1055 	}
1056 }
1057 
gmtplot_fancy_frame_straight_outline(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lonA,double latA,double lonB,double latB,unsigned int side,bool secondary_too)1058 GMT_LOCAL void gmtplot_fancy_frame_straight_outline (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lonA, double latA, double lonB, double latB, unsigned int side, bool secondary_too) {
1059 	unsigned int k, kn = 1, axis;
1060 	double scale = 1.0, x[2], y[2], angle, s, c, dx, dy, Ldx, Ldy, f0 = 1.0, f1 = 1.0;
1061 	struct GMT_PLOT_AXIS_ITEM *T = NULL;
1062 
1063 	if (!GMT->current.map.frame.side[side]) return;	/* Do not draw this frame side */
1064 	switch (side) {	/* Determine if there are missing partner sides that invalidates extensions */
1065 		case S_SIDE:
1066 			if (!GMT->current.map.frame.side[W_SIDE]) f0 = 0;
1067 			if (!GMT->current.map.frame.side[E_SIDE]) f1 = 0;
1068 			break;
1069 		case E_SIDE:
1070 			if (!GMT->current.map.frame.side[S_SIDE]) f0 = 0;
1071 			if (!GMT->current.map.frame.side[N_SIDE]) f1 = 0;
1072 			break;
1073 		case N_SIDE:
1074 			if (!GMT->current.map.frame.side[E_SIDE]) f0 = 0;
1075 			if (!GMT->current.map.frame.side[W_SIDE]) f1 = 0;
1076 			break;
1077 		case W_SIDE:
1078 			if (!GMT->current.map.frame.side[N_SIDE]) f0 = 0;
1079 			if (!GMT->current.map.frame.side[S_SIDE]) f1 = 0;
1080 			break;
1081 	}
1082 
1083 	if (secondary_too) {
1084 		scale = 0.5;
1085 		++kn;
1086 	}
1087 	axis = side % 2;	/* Gives 0 for GMT_X and 1 for GMT_Y */
1088 	T = &GMT->current.map.frame.axis[axis].item[GMT_TICK_UPPER];
1089 	if (!T->active) return;
1090 
1091 	gmt_geo_to_xy (GMT, lonA, latA, &x[0], &y[0]);
1092 	gmt_geo_to_xy (GMT, lonB, latB, &x[1], &y[1]);
1093 	angle = d_atan2 (y[1] - y[0], x[1] - x[0]);
1094 	sincos (angle, &s, &c);
1095 	/* Ldx/dy is the components of the extension of the fancy frame into neighboring side frames */
1096 	Ldx = (GMT->current.setting.map_frame_type == GMT_IS_ROUNDED) ? 0.0 : GMT->current.setting.map_frame_width * c;
1097 	Ldy = (GMT->current.setting.map_frame_type == GMT_IS_ROUNDED) ? 0.0 : GMT->current.setting.map_frame_width * s;
1098 	/* dx,dy is the outward shift components to draw the outside (and possibly half-way) parallel frame outline */
1099 	dx =  GMT->current.setting.map_frame_width * s;
1100 	dy = -GMT->current.setting.map_frame_width * c;
1101 	PSL_plotsegment (PSL, x[0]-f0*Ldx, y[0]-f0*Ldy, x[1]+f1*Ldx, y[1]+f1*Ldy);
1102 	for (k = 0; k < kn; k++) {
1103 		x[0] += scale*dx;
1104 		y[0] += scale*dy;
1105 		x[1] += scale*dx;
1106 		y[1] += scale*dy;
1107 		PSL_plotsegment (PSL, x[0]-f0*Ldx, y[0]-f0*Ldy, x[1]+f1*Ldx, y[1]+f1*Ldy);
1108 	}
1109 }
1110 
gmtplot_fancy_frame_curved_outline(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lonA,double latA,double lonB,double latB,unsigned int side,bool secondary_too)1111 GMT_LOCAL void gmtplot_fancy_frame_curved_outline (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lonA, double latA, double lonB, double latB, unsigned int side, bool secondary_too) {
1112 	double scale[2] = {1.0, 1.0}, escl, x1, x2, y1, y2, radius, r_inc, az1, az2, da0, da, width, s, fw, fe;
1113 
1114 	if (!GMT->current.map.frame.side[side]) return;	/* This side is inactive */
1115 	fw = (GMT->current.map.frame.side[W_SIDE]) ? 1.0 : 0.0;	/* Only extend if W side is plotted */
1116 	fe = (GMT->current.map.frame.side[E_SIDE]) ? 1.0 : 0.0;	/* Only extend if E side is plotted */
1117 	if (secondary_too) scale[0] = scale[1] = 0.5;
1118 	width = GMT->current.setting.map_frame_width;
1119 	escl = (GMT->current.setting.map_frame_type == GMT_IS_ROUNDED) ? 0.0 : 1.0;	/* Want rounded corners */
1120 	gmt_geo_to_xy (GMT, lonA, latA, &x1, &y1);
1121 	gmt_geo_to_xy (GMT, lonB, latB, &x2, &y2);
1122 	radius = hypot (x1 - GMT->current.proj.c_x0, y1 - GMT->current.proj.c_y0);
1123 	s = ((GMT->current.proj.north_pole && side == 2) || (!GMT->current.proj.north_pole && side == 0)) ? -1.0 : +1.0;	/* North: needs shorter radius.  South: Needs longer radius (opposite in S hemi) */
1124 	r_inc = s*scale[0] * width;
1125 	if (gmt_M_is_azimuthal(GMT) && gmt_M_360_range (lonA, lonB)) {	/* Full 360-degree circle */
1126 		PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius, 0.0, 360.0, PSL_MOVE|PSL_STROKE);
1127 		PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius + r_inc, 0.0, 360.0, PSL_MOVE|PSL_STROKE);
1128 		if (secondary_too) PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius + 2.0 * r_inc, 0.0, 360.0, PSL_MOVE|PSL_STROKE);
1129 	}
1130 	else {
1131 		az1 = d_atan2d (y1 - GMT->current.proj.c_y0, x1 - GMT->current.proj.c_x0);
1132 		az2 = d_atan2d (y2 - GMT->current.proj.c_y0, x2 - GMT->current.proj.c_x0);
1133 		if (!GMT->current.proj.north_pole) gmt_M_double_swap (az1, az2);	/* In S hemisphere, must draw in opposite direction */
1134 		while (az1 < 0.0) az1 += 360.0;	/* Wind az1 to be in the 0-360 range */
1135 		while (az2 < az1) az2 += 360.0;	/* Likewise ensure az1 > az1 and is now in the 0-720 range */
1136 		da0 = R2D * escl * width /radius;
1137 		da  = R2D * escl * width / (radius + r_inc);
1138 		PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius, az1-fw*da0, az2+fe*da0, PSL_MOVE|PSL_STROKE);
1139 		PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius + r_inc, az1-fw*da, az2+fe*da, PSL_MOVE|PSL_STROKE);
1140 		if (secondary_too) {
1141 			r_inc *= 2.0;
1142 			da = R2D * escl * width / (radius + r_inc);
1143 			PSL_plotarc (PSL, GMT->current.proj.c_x0, GMT->current.proj.c_y0, radius + r_inc, az1-fw*da, az2+fe*da, PSL_MOVE|PSL_STROKE);
1144 		}
1145 	}
1146 }
1147 
gmtplot_rounded_framecorners(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,bool secondary_too)1148 GMT_LOCAL void gmtplot_rounded_framecorners (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, bool secondary_too) {
1149 	unsigned int k, kn, item[2] = {GMT_TICK_UPPER, GMT_TICK_LOWER};
1150 	double x1, y1, x2, y2, anglew, anglee, x, y, width;
1151 	struct GMT_PLOT_AXIS_ITEM *Tx = NULL, *Ty = NULL;
1152 
1153 	if (GMT->current.setting.map_frame_type != GMT_IS_ROUNDED) return;	/* Only do this if rounded corners are requested */
1154 
1155 	gmt_geo_to_xy (GMT, w, n, &x1, &y1);
1156 	gmt_geo_to_xy (GMT, w, s, &x2, &y2);
1157 	anglew = d_atan2d (y2 - y1, x2 - x1);
1158 
1159 	gmt_geo_to_xy (GMT, e, s, &x1, &y1);
1160 	gmt_geo_to_xy (GMT, e, n, &x2, &y2);
1161 	anglee = d_atan2d (y2 - y1, x2 - x1);
1162 
1163 	width = ((secondary_too) ? 0.5 : 1.0) * fabs (GMT->current.setting.map_frame_width);
1164 	kn = (secondary_too) ? 2 : 1;
1165 	for (k = 0; k < kn; k++) {
1166 		Tx = &GMT->current.map.frame.axis[GMT_X].item[item[k]];
1167 		Ty = &GMT->current.map.frame.axis[GMT_Y].item[item[k]];
1168 		if (!(Tx->active && Ty->active)) continue;
1169 		if (GMT->current.map.frame.side[S_SIDE] && GMT->current.map.frame.side[E_SIDE]) {
1170 			gmt_geo_to_xy (GMT, e, s, &x, &y);
1171 			PSL_plotarc (PSL, x, y, (k+1)*width, 180.0+anglee, 270.0+anglee, PSL_MOVE|PSL_STROKE);
1172 		}
1173 		if (GMT->current.map.frame.side[E_SIDE] && GMT->current.map.frame.side[N_SIDE]) {
1174 			gmt_geo_to_xy (GMT, e, n, &x, &y);
1175 			PSL_plotarc (PSL, x, y, (k+1)*width, 270.0+anglee, 360.0+anglee, PSL_MOVE|PSL_STROKE);
1176 		}
1177 		if (GMT->current.map.frame.side[N_SIDE] && GMT->current.map.frame.side[W_SIDE]) {
1178 			gmt_geo_to_xy (GMT, w, n, &x, &y);
1179 			PSL_plotarc (PSL, x, y, (k+1)*width, 180.0+anglew, 270.0+anglew, PSL_MOVE|PSL_STROKE);
1180 		}
1181 		if (GMT->current.map.frame.side[W_SIDE] && GMT->current.map.frame.side[S_SIDE]) {
1182 			gmt_geo_to_xy (GMT, w, s, &x, &y);
1183 			PSL_plotarc (PSL, x, y, (k+1)*width, 270.0+anglew, 360.0+anglew, PSL_MOVE|PSL_STROKE);
1184 		}
1185 		if ((gmt_M_is_azimuthal(GMT) || gmt_M_is_conical(GMT)) && GMT->current.map.frame.side[W_SIDE] && GMT->current.map.frame.side[E_SIDE]) {	/* Round off the pointy head? */
1186 			if (doubleAlmostEqual (GMT->common.R.wesn[YHI], 90.0)) {
1187 				gmt_geo_to_xy (GMT, w, n, &x, &y);
1188 				PSL_plotarc (PSL, x, y, (k+1)*width, anglee, 180.0+anglew, PSL_MOVE|PSL_STROKE);
1189 			}
1190 			else if (doubleAlmostEqual (GMT->common.R.wesn[YLO], -90.0)) {
1191 				gmt_geo_to_xy (GMT, w, s, &x, &y);
1192 				PSL_plotarc (PSL, x, y, (k+1)*width, anglew-90.0, anglee-90.0, PSL_MOVE|PSL_STROKE);
1193 			}
1194 		}
1195 	}
1196 }
1197 
1198 #if 0
1199 /* Nov-11-2014 PW: For reference until we know there are no side effects with the new one below */
1200 GMT_LOCAL void gmtplot_wesn_map_boundary_old (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1201 	uint64_t i, np = 0;
1202 	double *xx = NULL, *yy = NULL;
1203 
1204 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
1205 
1206 	if (GMT->current.map.frame.side[W_SIDE] && GMT->current.map.frame.side[E_SIDE] && GMT->current.map.frame.side[S_SIDE] && GMT->current.map.frame.side[N_SIDE]) {
1207 		/* Want the entire boundary so use a single path to void notches at corners */
1208 		np = gmt_graticule_path (GMT, &xx, &yy, 1, false, w, e, s, n);
1209 		for (i = 0; i < np; i++)
1210 			gmt_geo_to_xy (GMT, xx[i], yy[i], &xx[i], &yy[i]);
1211 		PSL_plotline (PSL, xx, yy, (int)np, PSL_MOVE + PSL_STROKE + PSL_CLOSE);
1212 		gmt_M_free (GMT, xx);
1213 		gmt_M_free (GMT, yy);
1214 		return;
1215 	}
1216 
1217 	/* Just do the sides that were requested */
1218 
1219 	if (GMT->current.map.frame.side[W_SIDE]) {	/* West */
1220 		np = gmtlib_map_path (GMT, w, s, w, n, &xx, &yy);
1221 		for (i = 0; i < np; i++)
1222 			gmt_geo_to_xy (GMT, xx[i], yy[i], &xx[i], &yy[i]);
1223 		PSL_plotline (PSL, xx, yy, (int)np, PSL_MOVE + PSL_STROKE);
1224 		gmt_M_free (GMT, xx);
1225 		gmt_M_free (GMT, yy);
1226 	}
1227 	if (GMT->current.map.frame.side[E_SIDE]) {	/* East */
1228 		np = gmtlib_map_path (GMT, e, s, e, n, &xx, &yy);
1229 		for (i = 0; i < np; i++)
1230 			gmt_geo_to_xy (GMT, xx[i], yy[i], &xx[i], &yy[i]);
1231 		PSL_plotline (PSL, xx, yy, (int)np, PSL_MOVE + PSL_STROKE);
1232 		gmt_M_free (GMT, xx);
1233 		gmt_M_free (GMT, yy);
1234 	}
1235 	if (GMT->current.map.frame.side[S_SIDE]) {	/* South */
1236 		np = gmtlib_map_path (GMT, w, s, e, s, &xx, &yy);
1237 		for (i = 0; i < np; i++)
1238 			gmt_geo_to_xy (GMT, xx[i], yy[i], &xx[i], &yy[i]);
1239 		PSL_plotline (PSL, xx, yy, (int)np, PSL_MOVE + PSL_STROKE);
1240 		gmt_M_free (GMT, xx);
1241 		gmt_M_free (GMT, yy);
1242 	}
1243 	if (GMT->current.map.frame.side[N_SIDE]) {	/* North */
1244 		np = gmtlib_map_path (GMT, w, n, e, n, &xx, &yy);
1245 		for (i = 0; i < np; i++)
1246 			gmt_geo_to_xy (GMT, xx[i], yy[i], &xx[i], &yy[i]);
1247 		PSL_plotline (PSL, xx, yy, (int)np, PSL_MOVE + PSL_STROKE);
1248 		gmt_M_free (GMT, xx);
1249 		gmt_M_free (GMT, yy);
1250 	}
1251 }
1252 #endif
1253 
gmtplot_wesn_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1254 GMT_LOCAL void gmtplot_wesn_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1255 	/* Draw 0-4 boundary sides.  If more than 1 then ensure we draw a continuous line to
1256 	 * avoid notches at sharp corners. */
1257 	uint64_t i, n_sides = 0, np = 0, n_set = 0;
1258 	int this, next = GMT_NOTSET, flag;
1259 	double lonstart[4] = {w, e, e, w}, lonstop[4] = {e, e, w, w};
1260 	double latstart[4] = {s, s, n, n}, latstop[4] = {s, n, n, s};
1261 	double *xx = NULL, *yy = NULL;
1262 
1263 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
1264 
1265 	/* Determine how many sides are requested and find first side to be skipped (if any) */
1266 
1267 	for (this = S_SIDE; this <= W_SIDE; this++) {
1268 		if (GMT->current.map.frame.side[this]) n_sides++;
1269 		else if (next == GMT_NOTSET) next = this;
1270 	}
1271 	if (n_sides == 0) return;	/* Nuthin' to do */
1272 
1273 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtplot_wesn_map_boundary n_sides = %" PRIu64 "\n", n_sides);
1274 	this = next;		/* First side to be skipped (== GMT_NOTSET if none to be skipped) */
1275 	flag = PSL_MOVE;	/* Need to move to the start of the line the first time */
1276 	while (n_set < n_sides) {	/* While more sides need to be plotted we loop counter-clockwise */
1277 		this++;		/* Go to next side (this will be S_SIDE if all 4 sides should be plotted) */
1278 		if (this > W_SIDE) this = S_SIDE;	/* Wrap around */
1279 		if (!GMT->current.map.frame.side[this]) {	/* Side to be skipped */
1280 			flag = PSL_MOVE;	/* Need to move to the start of the new line after the gap */
1281 			continue;
1282 		}
1283 		/* Get coordinates for this border */
1284 		np = gmtlib_map_path (GMT, lonstart[this], latstart[this], lonstop[this], latstop[this], &xx, &yy);
1285 		for (i = 0; i < np; i++) /* Project to inches */
1286 			gmt_geo_to_xy (GMT, xx[i], yy[i], &xx[i], &yy[i]);
1287 		next = this + 1;	/* Find ID of next side */
1288 		if (next > W_SIDE) next = S_SIDE;	/* Wrap around */
1289 		if (!GMT->current.map.frame.side[next]) flag |= PSL_STROKE;	/* Must stroke line since a gap follows */
1290 		else if (n_sides == 4 && this == W_SIDE) flag |= (PSL_STROKE+PSL_CLOSE);	/* Must close and stroke line since no gaps */
1291 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtplot_wesn_map_boundary doing side %d with flag = %d\n", this, flag);
1292 
1293 		PSL_plotline (PSL, xx, yy, (int)np, flag);
1294 		gmt_M_free (GMT, xx);
1295 		gmt_M_free (GMT, yy);
1296 		n_set++;
1297 		flag = 0;
1298 	}
1299 }
1300 
gmtplot_fancy_fat_pen_width(struct GMT_CTRL * GMT)1301 GMT_LOCAL double gmtplot_fancy_fat_pen_width (struct GMT_CTRL *GMT) {
1302 	/* Return the pen width of the black/white checker fancy frame in points */
1303 	return (fabs (0.01 * GMT->current.setting.map_frame_percent * GMT->current.setting.map_frame_width) * GMT->session.u2u[GMT_INCH][GMT_PT]);
1304 }
1305 
gmtplot_fancy_thin_pen_width(struct GMT_CTRL * GMT,double width)1306 GMT_LOCAL double gmtplot_fancy_thin_pen_width (struct GMT_CTRL *GMT, double width) {
1307 	/* Return the pen width of the enclosing checker fancy frame in points as 10% of fat pen width */
1308 	return (0.1 * width / ( GMT->current.setting.map_frame_percent * 0.01 ));
1309 }
1310 
gmtplot_fancy_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1311 GMT_LOCAL void gmtplot_fancy_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1312 	double fat_pen, thin_pen;
1313 	bool dual = false;
1314 	unsigned int cap = PSL->internal.line_cap;
1315 
1316 	if (!(GMT->current.setting.map_frame_type & GMT_IS_FANCY)) {	/* Draw plain boundary and return */
1317 		gmtplot_wesn_map_boundary (GMT, PSL, w, e, s, n);
1318 		return;
1319 	}
1320 
1321 	PSL_setcolor (PSL, GMT->current.setting.map_frame_pen.rgb, PSL_IS_STROKE);
1322 
1323 	fat_pen = gmtplot_fancy_fat_pen_width (GMT);
1324 	if (GMT->current.map.frame.axis[GMT_Y].item[GMT_TICK_LOWER].active) {	/* Need two-layer frame */
1325 		fat_pen *= 0.5;
1326 		dual = true;
1327 	}
1328 	thin_pen = gmtplot_fancy_thin_pen_width (GMT, fat_pen);
1329 
1330 	/* Draw frame checkers */
1331 	/* This needs to be done with BUTT cap since checker segments are drawn as heavy lines */
1332 
1333 	PSL_setlinewidth (PSL, fat_pen);
1334 	PSL_setlinecap (PSL, PSL_BUTT_CAP);
1335 
1336 	gmtplot_fancy_frame_straightlat_checkers (GMT, PSL, w, e, s, n, dual);
1337 	gmtplot_fancy_frame_straightlon_checkers (GMT, PSL, w, e, s, n, dual);
1338 
1339 	/* Draw the outline on top of the checkers */
1340 	/* Reset line cap, etc. */
1341 
1342 	PSL_setlinecap (PSL, cap);
1343 	PSL_setcolor (PSL, GMT->current.setting.map_frame_pen.rgb, PSL_IS_STROKE);
1344 	PSL_setlinewidth (PSL, thin_pen);
1345 
1346 	gmtplot_fancy_frame_straight_outline (GMT, PSL, w, s, e, s, S_SIDE, dual);
1347 	gmtplot_fancy_frame_straight_outline (GMT, PSL, e, s, e, n, E_SIDE, dual);
1348 	gmtplot_fancy_frame_straight_outline (GMT, PSL, e, n, w, n, N_SIDE, dual);
1349 	gmtplot_fancy_frame_straight_outline (GMT, PSL, w, n, w, s, W_SIDE, dual);
1350 
1351 	gmtplot_rounded_framecorners (GMT, PSL, w, e, s, n, dual);
1352 }
1353 
gmtplot_rect_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double x0,double y0,double x1,double y1)1354 GMT_LOCAL void gmtplot_rect_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double x0, double y0, double x1, double y1) {
1355 	unsigned int cap = PSL->internal.line_cap;
1356 
1357 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
1358 	/* Temporarily change to square cap so rectangular frames have neat corners */
1359 	PSL_setlinecap (PSL, PSL_SQUARE_CAP);
1360 
1361 	if (GMT->current.map.frame.side[W_SIDE] & GMT_AXIS_DRAW) PSL_plotsegment (PSL, x0, y0, x0, y1);	/* West */
1362 	if (GMT->current.map.frame.side[E_SIDE] & GMT_AXIS_DRAW) PSL_plotsegment (PSL, x1, y0, x1, y1);	/* East */
1363 	if (GMT->current.map.frame.side[S_SIDE] & GMT_AXIS_DRAW) PSL_plotsegment (PSL, x0, y0, x1, y0);	/* South */
1364 	if (GMT->current.map.frame.side[N_SIDE] & GMT_AXIS_DRAW) PSL_plotsegment (PSL, x0, y1, x1, y1);	/* North */
1365 	PSL_setlinecap (PSL, cap);	/* Reset back to default */
1366 }
1367 
1368 /*	GMT_POLAR (S or N) PROJECTION MAP BOUNDARY	*/
1369 
gmtplot_polar_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1370 GMT_LOCAL void gmtplot_polar_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1371 	bool dual = false;
1372 	unsigned int cap = PSL->internal.line_cap;
1373 	double thin_pen, fat_pen, lon_range = e - w;
1374 	double x0, x1, x2, y0, y1, y2, start, stop;
1375 
1376 	if (GMT->common.R.oblique) { /* Draw rectangular boundary and return */
1377 		gmtplot_rect_map_boundary (GMT, PSL, 0.0, 0.0, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
1378 		return;
1379 	}
1380 
1381 	if (!GMT->current.proj.north_pole && gmt_M_is_Spole (s)) /* Cannot have southern boundary */
1382 		GMT->current.map.frame.side[S_SIDE] = GMT_AXIS_NONE;
1383 	if (GMT->current.proj.north_pole && gmt_M_is_Npole (n)) /* Cannot have northern boundary */
1384 		GMT->current.map.frame.side[N_SIDE] = GMT_AXIS_NONE;
1385 	if (gmt_M_360_range (w, e) || doubleAlmostEqualZero (e, w))
1386 		GMT->current.map.frame.side[E_SIDE] = GMT->current.map.frame.side[W_SIDE] = GMT_AXIS_NONE;
1387 
1388 	if (!(GMT->current.setting.map_frame_type & GMT_IS_FANCY)) {	/* Draw plain boundary and return */
1389 		gmtplot_wesn_map_boundary (GMT, PSL, w, e, s, n);
1390 		return;
1391 	}
1392 
1393 	/* Here draw fancy map boundary */
1394 
1395 	fat_pen = gmtplot_fancy_fat_pen_width (GMT);
1396 	if (GMT->current.map.frame.axis[GMT_Y].item[GMT_TICK_LOWER].active) {	/* Need two-layer frame */
1397 		fat_pen *= 0.5;
1398 		dual = true;
1399 	}
1400 	thin_pen = gmtplot_fancy_thin_pen_width (GMT, fat_pen);
1401 
1402 	/* Draw frame checkers */
1403 	/* This needs to be done with BUTT cap since checker segments are drawn as heavy lines */
1404 
1405 	PSL_setlinewidth (PSL, fat_pen);
1406 	PSL_setlinecap (PSL, PSL_BUTT_CAP);
1407 
1408 	gmtplot_fancy_frame_straightlat_checkers (GMT, PSL, w, e, s, n, dual);
1409 	gmtplot_fancy_frame_curvedlon_checkers (GMT, PSL, w, e, s, n, dual);
1410 
1411 	/* Draw the outline on top of the checkers */
1412 	/* Reset line cap, etc. */
1413 
1414 	PSL_setlinecap (PSL, cap);
1415 	PSL_setcolor (PSL, GMT->current.setting.map_frame_pen.rgb, PSL_IS_STROKE);
1416 	PSL_setlinewidth (PSL, thin_pen);
1417 
1418 	gmtplot_fancy_frame_curved_outline (GMT, PSL, w, s, e, s, S_SIDE, dual);
1419 	gmtplot_fancy_frame_straight_outline (GMT, PSL, e, s, e, n, E_SIDE, dual);
1420 	gmtplot_fancy_frame_curved_outline (GMT, PSL, w, n, e, n, N_SIDE, dual);
1421 	gmtplot_fancy_frame_straight_outline (GMT, PSL, w, n, w, s, W_SIDE, dual);
1422 
1423 	gmtplot_rounded_framecorners (GMT, PSL, w, e, s, n, dual);
1424 
1425 	if (GMT->current.proj.north_pole && gmt_M_is_Npole (n) && !(doubleAlmostEqual (lon_range, 180.0) || doubleAlmostEqual (lon_range, 360.0))) {
1426 		/* Connect the outer straight borders with an arc using map frame pen */
1427 		double radius = fat_pen * GMT->session.u2u[GMT_PT][GMT_INCH];	/* Get radius in inches */
1428 		PSL_setlinewidth (PSL, thin_pen);
1429 		gmt_geo_to_xy (GMT, w, n, &x0, &y0);
1430 		gmt_geo_to_xy (GMT, w, s, &x1, &y1);
1431 		stop = d_atan2d (y1 - y0, x1 - x0) - 90;
1432 		gmt_geo_to_xy (GMT, e, s, &x2, &y2);
1433 		start = d_atan2d (y2 - y0, x2 - x0) + 90;
1434 		if (stop < start) stop += 360.0;
1435 		if (stop > start) PSL_plotarc (PSL, x0, y0, radius, start, stop, PSL_MOVE|PSL_STROKE);
1436 	}
1437 	else if (!GMT->current.proj.north_pole && gmt_M_is_Spole (s) && !(doubleAlmostEqual (lon_range, 180.0) || doubleAlmostEqual (lon_range, 360.0))) {
1438 		/* Connect the outer straight borders with an arc using map frame pen */
1439 		double radius = fat_pen * GMT->session.u2u[GMT_PT][GMT_INCH];	/* Get radius in inches */
1440 		PSL_setlinewidth (PSL, thin_pen);
1441 		gmt_geo_to_xy (GMT, e, s, &x0, &y0);
1442 		gmt_geo_to_xy (GMT, e, n, &x1, &y1);
1443 		stop = d_atan2d (y1 - y0, x1 - x0) - 90;
1444 		gmt_geo_to_xy (GMT, w, n, &x2, &y2);
1445 		start = d_atan2d (y2 - y0, x2 - x0) + 90;
1446 		if (stop < start) stop += 360.0;
1447 		if (stop > start) PSL_plotarc (PSL, x0, y0, radius, start, stop, PSL_MOVE|PSL_STROKE);
1448 	}
1449 }
1450 
1451 /*	CONIC PROJECTION MAP BOUNDARY	*/
1452 
gmtplot_conic_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1453 GMT_LOCAL void gmtplot_conic_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1454 	bool dual = false;
1455 	unsigned int cap = PSL->internal.line_cap;
1456 	double thin_pen, fat_pen;
1457 
1458 	if (GMT->common.R.oblique) { /* Draw rectangular boundary and return */
1459 		gmtplot_rect_map_boundary (GMT, PSL, 0.0, 0.0, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
1460 		return;
1461 	}
1462 
1463 	if (!(GMT->current.setting.map_frame_type & GMT_IS_FANCY)) {	/* Draw plain boundary and return */
1464 		gmtplot_wesn_map_boundary (GMT, PSL, w, e, s, n);
1465 		return;
1466 	}
1467 
1468 	/* Here draw fancy map boundary */
1469 
1470 	if (!GMT->current.proj.north_pole && gmt_M_is_Spole (s)) /* Cannot have southern boundary */
1471 		GMT->current.map.frame.side[S_SIDE] = GMT_AXIS_NONE;
1472 	if (GMT->current.proj.north_pole && gmt_M_is_Npole (n)) /* Cannot have northern boundary */
1473 		GMT->current.map.frame.side[N_SIDE] = GMT_AXIS_NONE;
1474 
1475 	fat_pen = gmtplot_fancy_fat_pen_width (GMT);
1476 	if (GMT->current.map.frame.axis[GMT_Y].item[GMT_TICK_LOWER].active) {	/* Need two-layer frame */
1477 		fat_pen *= 0.5;
1478 		dual = true;
1479 	}
1480 	thin_pen = gmtplot_fancy_thin_pen_width (GMT, fat_pen);
1481 
1482 	/* Draw frame checkers */
1483 	/* This needs to be done with BUTT cap since checker segments are drawn as heavy lines */
1484 
1485 	PSL_setlinewidth (PSL, fat_pen);
1486 	PSL_setlinecap (PSL, PSL_BUTT_CAP);
1487 
1488 	gmtplot_fancy_frame_straightlat_checkers (GMT, PSL, w, e, s, n, dual);
1489 	gmtplot_fancy_frame_curvedlon_checkers (GMT, PSL, w, e, s, n, dual);
1490 
1491 	/* Draw the outline on top of the checkers */
1492 	/* Reset line cap, etc. */
1493 
1494 	PSL_setlinecap (PSL, cap);
1495 	PSL_setcolor (PSL, GMT->current.setting.map_frame_pen.rgb, PSL_IS_STROKE);
1496 	PSL_setlinewidth (PSL, thin_pen);
1497 
1498 	gmtplot_fancy_frame_curved_outline (GMT, PSL, w, s, e, s, S_SIDE, dual);
1499 	gmtplot_fancy_frame_straight_outline (GMT, PSL, e, s, e, n, E_SIDE, dual);
1500 	gmtplot_fancy_frame_curved_outline (GMT, PSL, w, n, e, n, N_SIDE, dual);
1501 	gmtplot_fancy_frame_straight_outline (GMT, PSL, w, n, w, s, W_SIDE, dual);
1502 
1503 	gmtplot_rounded_framecorners (GMT, PSL, w, e, s, n, dual);
1504 }
1505 
1506 /*	OBLIQUE MERCATOR PROJECTION MAP FUNCTIONS	*/
1507 
gmtplot_oblmrc_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1508 GMT_LOCAL void gmtplot_oblmrc_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1509 	gmt_M_unused(w); gmt_M_unused(e); gmt_M_unused(s); gmt_M_unused(n);
1510 	gmtplot_rect_map_boundary (GMT, PSL, 0.0, 0.0, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
1511 }
1512 
1513 /*	MOLLWEIDE and HAMMER-AITOFF EQUAL AREA PROJECTION MAP FUNCTIONS	*/
1514 
gmtplot_ellipse_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1515 GMT_LOCAL void gmtplot_ellipse_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1516 	if (GMT->common.R.oblique) { /* Draw rectangular boundary and return */
1517 		gmtplot_rect_map_boundary (GMT, PSL, 0.0, 0.0, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
1518 		return;
1519 	}
1520 	gmtplot_wesn_map_boundary (GMT, PSL, w, e, s, n);	/* Draw outline first, then turn off non-existent sides */
1521 	if (gmt_M_is_Spole (GMT->common.R.wesn[YLO])) /* Cannot have southern boundary */
1522 		GMT->current.map.frame.side[S_SIDE] = GMT_AXIS_NONE;
1523 	if (gmt_M_is_Npole (GMT->common.R.wesn[YHI])) /* Cannot have northern boundary */
1524 		GMT->current.map.frame.side[N_SIDE] = GMT_AXIS_NONE;
1525 }
1526 
gmtplot_basic_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1527 GMT_LOCAL void gmtplot_basic_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1528 	if (GMT->common.R.oblique) { /* Draw rectangular boundary and return */
1529 		gmtplot_rect_map_boundary (GMT, PSL, 0.0, 0.0, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
1530 		return;
1531 	}
1532 	gmtplot_wesn_map_boundary (GMT, PSL, w, e, s, n);
1533 }
1534 
1535 /*
1536  *	GENERIC MAP PLOTTING FUNCTIONS
1537  */
1538 
gmtplot_genper_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1539 GMT_LOCAL int gmtplot_genper_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1540 	uint64_t nr;
1541 	gmt_M_unused(w); gmt_M_unused(e); gmt_M_unused(s); gmt_M_unused(n);
1542 
1543 	if (GMT->common.R.oblique) {	/* Draw rectangular boundary and return */
1544 		gmtplot_rect_map_boundary (GMT, PSL, 0.0, 0.0, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
1545 		return 0;
1546 	}
1547 
1548 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
1549 
1550 	nr = GMT->current.map.n_lon_nodes + GMT->current.map.n_lat_nodes;
1551 	if (nr >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
1552 
1553 	if (GMT->current.proj.g_debug > 1) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper_map_boundary nr = %" PRIu64 "\n", nr);
1554 
1555 	gmtlib_genper_map_clip_path (GMT, nr, GMT->current.plot.x, GMT->current.plot.y);
1556 
1557 	PSL_plotline (PSL, GMT->current.plot.x, GMT->current.plot.y, (int)nr, PSL_MOVE|PSL_STROKE);
1558 
1559 	return 0;
1560 }
1561 
gmtplot_circle_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1562 GMT_LOCAL void gmtplot_circle_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1563 	gmt_M_unused(w); gmt_M_unused(e); gmt_M_unused(s); gmt_M_unused(n);
1564 	if (GMT->common.R.oblique) { /* Draw rectangular boundary and return */
1565 		gmtplot_rect_map_boundary (GMT, PSL, 0.0, 0.0, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
1566 		return;
1567 	}
1568 
1569 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
1570 
1571 	PSL_plotarc (PSL, GMT->current.proj.r, GMT->current.proj.r, GMT->current.proj.r, 0.0, 360.0, PSL_MOVE|PSL_STROKE);
1572 }
1573 
gmtplot_theta_r_map_boundary(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1574 GMT_LOCAL void gmtplot_theta_r_map_boundary (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1575 	bool circles = false;
1576 	unsigned int flag = PSL_CLOSE;
1577 	uint64_t i, k = 0, nr, n_max_path;
1578 	double a, da;
1579 
1580 	/* This boundary may be a circle, a donut, a wedge, or a donut with a missing sector.  In all cases we wish
1581 	 * to draw the outline in one go to ensure the corners are joined properly and not leaving small notches. */
1582 
1583 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
1584 
1585 	if (GMT->current.proj.flip) {	/* Flipped, so N is the inside circle; check if radius matches n */
1586 		if (doubleAlmostEqual (n, GMT->current.proj.flip_radius) && gmt_M_is_zero (GMT->current.proj.radial_offset))
1587 			GMT->current.map.frame.side[N_SIDE] = GMT_AXIS_NONE;	/* No donuts, please */
1588 	}
1589 	else {	/* No flip, so s is inside circle. Check if s is zero and there is not an offset */
1590 		if (gmt_M_is_zero (s) && gmt_M_is_zero (GMT->current.proj.radial_offset))
1591 			GMT->current.map.frame.side[S_SIDE] = GMT_AXIS_NONE;		/* No donuts, please */
1592 	}
1593 	if (gmt_M_360_range (w, e) || doubleAlmostEqualZero (e, w)) {	/* Draw a full 360 circle so no E/W sides will be drawn */
1594 		GMT->current.map.frame.side[E_SIDE] = GMT->current.map.frame.side[W_SIDE] = GMT_AXIS_NONE;
1595 		circles = true;
1596 	}
1597 
1598 	nr = GMT->current.map.n_lon_nodes;	/* Points needed to draw a full circle */
1599 	n_max_path = 2 * (nr + 2);	/* Max length of boundary */
1600 	while (n_max_path > GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);	/* Ensure we have enough plot memory */
1601 	da = fabs (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / (nr - 1);	/* Steps in azimuth along the curved boundary */
1602 	if (GMT->current.map.frame.side[N_SIDE] & GMT_AXIS_DRAW) {	/* Must draw the N circular boundary from W (XLO) to E (XHI), all at YHI */
1603 		for (i = 0; i < nr; i++, k++) {
1604 			a = GMT->common.R.wesn[XLO] + i * da;
1605 			gmt_geo_to_xy (GMT, a, GMT->common.R.wesn[YHI], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);
1606 		}
1607 		if (circles) {	/* Nothing to connect to, so plot this circle we have as is */
1608 			PSL_plotline (PSL, GMT->current.plot.x, GMT->current.plot.y, (int)nr, PSL_MOVE|PSL_STROKE|PSL_CLOSE);
1609 			k = 0;	/* Start all over in case another circle is needed */
1610 		}
1611 	}
1612 	/* Now at E (XHI, YHI).  If we need to add a radial E boundary then add it now to the array, ending at XHI, YLO */
1613 	if (GMT->current.map.frame.side[E_SIDE] & GMT_AXIS_DRAW) {
1614 		gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);	k++;
1615 		gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);	k++;
1616 	}
1617 	/* Now at E (XHI, YLO).  Do we hook in the other partial circle? */
1618 	if (GMT->current.map.frame.side[S_SIDE] & GMT_AXIS_DRAW) {	/* Must draw the S circular boundary from E (XHI) backwards to W (XLO), all at YLO */
1619 		for (i = 0; i < nr; i++, k++) {
1620 			a = GMT->common.R.wesn[XHI] - i * da;
1621 			gmt_geo_to_xy (GMT, a, GMT->common.R.wesn[YLO], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);
1622 		}
1623 		if (circles) {	/* Nothing to connect to, so plot this circle as is */
1624 			PSL_plotline (PSL, GMT->current.plot.x, GMT->current.plot.y, (int)nr, PSL_MOVE|PSL_STROKE);
1625 			k = 0;	/* Start all over */
1626 		}
1627 	}
1628 	else if (k) {   /* Must plot now */
1629 		PSL_plotline (PSL, GMT->current.plot.x, GMT->current.plot.y, (int)k, PSL_MOVE|PSL_STROKE);
1630 		k = 0;
1631 	}
1632 	/* Now at W (XLO, YLO).  If need to add a radial W boundary add it now to the array, ending at XLO, YHI (where we started) */
1633 	if (GMT->current.map.frame.side[W_SIDE] & GMT_AXIS_DRAW) {
1634 		gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);	k++;
1635 		gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);	k++;
1636 	}
1637 	for (i = S_SIDE; i <= W_SIDE; i++) if ((GMT->current.map.frame.side[i] & GMT_AXIS_DRAW) == 0) flag = 0;
1638 	if (k)	/* Finally draw the strange donut/pacman boundary as one piece */
1639 		PSL_plotline (PSL, GMT->current.plot.x, GMT->current.plot.y, (int)k, PSL_MOVE|PSL_STROKE|flag);
1640 }
1641 
gmtplot_map_tick(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double * xx,double * yy,unsigned int * sides,double * angles,unsigned int nx,unsigned int type,double len)1642 GMT_LOCAL void gmtplot_map_tick (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double *xx, double *yy, unsigned int *sides, double *angles, unsigned int nx, unsigned int type, double len) {
1643 	double angle, xl, yl, c, s, tick_length;
1644 	unsigned int i;
1645 	bool set_angle;
1646 
1647 	/* The set_angle bit trieds to handle the fact that the given angles from crossing may need an adjustment depending on
1648 	 * which side of a rectangular box it occurs.  There are exception for round maps, etc.  It is a bit nebulous and could
1649 	 * need a better explanation.  For instance, I commented out the Gnomonic case which is needed for annotations but not here, apparently */
1650 	set_angle = ((!GMT->common.R.oblique && !(gmt_M_is_azimuthal(GMT) || gmt_M_is_conical(GMT))) || GMT->common.R.oblique);
1651 	if (!GMT->common.R.oblique && (GMT->current.proj.projection_GMT == GMT_GENPER || GMT->current.proj.projection_GMT == GMT_POLYCONIC)) set_angle = true;
1652 
1653 	for (i = 0; i < nx; i++) {
1654 		if (!GMT->current.proj.edge[sides[i]]) continue;
1655 		if ((GMT->current.map.frame.side[sides[i]] & GMT_AXIS_TICK) == 0) continue;
1656 		if (!(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE) && ((type == 0 && (sides[i] % 2)) || (type == 1 && !(sides[i] % 2)))) continue;
1657 		angle = ((GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_NORMAL_TICKS) ? (sides[i] - 1) * 90.0 : angles[i]);
1658 		if (set_angle) {	/* Adjust angle to fit the range of angles relative to each side */
1659 			if (sides[i] == 0 && angle < 180.0) angle -= 180.0;
1660 			if (sides[i] == 1 && (angle > 90.0 && angle < 270.0)) angle -= 180.0;
1661 			if (sides[i] == 2 && angle > 180.0) angle -= 180.0;
1662 			if (sides[i] == 3 && (angle < 90.0 || angle > 270.0)) angle -= 180.0;
1663 		}
1664 		sincosd (angle, &s, &c);
1665 		tick_length = len;
1666 		if (GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_EXTEND_TICKS) {
1667 			if (sides[i] % 2) {
1668 				/* if (fabs (c) > cosd (GMT->current.setting.map_annot_min_angle)) continue; */
1669 				if (fabs (c) < sind (GMT->current.setting.map_annot_min_angle)) continue;
1670 				tick_length /= fabs(c);
1671 			}
1672 			else {
1673 				if (fabs (s) < sind (GMT->current.setting.map_annot_min_angle)) continue;
1674 				tick_length /= fabs(s);
1675 			}
1676 		}
1677 		xl = tick_length * c;
1678 		yl = tick_length * s;
1679 		PSL_plotsegment (PSL, xx[i], yy[i], xx[i]+xl, yy[i]+yl);
1680 	}
1681 }
1682 
gmtplot_map_lontick(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lon,double south,double north,double len)1683 GMT_LOCAL void gmtplot_map_lontick (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lon, double south, double north, double len) {
1684 	unsigned int i, nc;
1685 	struct GMT_XINGS *xings = NULL;
1686 
1687 	nc = gmtlib_map_loncross (GMT, lon, south, north, &xings);
1688 	for (i = 0; i < nc; i++)
1689 		gmtplot_map_tick (GMT, PSL, xings[i].xx, xings[i].yy, xings[i].sides, xings[i].angle, xings[i].nx, 0, len);
1690 	if (nc) gmt_M_free (GMT, xings);
1691 }
1692 
gmtplot_map_lattick(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lat,double west,double east,double len)1693 GMT_LOCAL void gmtplot_map_lattick (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lat, double west, double east, double len) {
1694 	unsigned int i, nc;
1695 
1696 	struct GMT_XINGS *xings = NULL;
1697 
1698 	nc = gmtlib_map_latcross (GMT, lat, west, east, &xings);
1699 	for (i = 0; i < nc; i++)
1700 		gmtplot_map_tick (GMT, PSL, xings[i].xx, xings[i].yy, xings[i].sides, xings[i].angle, xings[i].nx, 1, len);
1701 	if (nc) gmt_M_free (GMT, xings);
1702 }
1703 
gmtplot_annot_too_crowded(struct GMT_CTRL * GMT,double x,double y,unsigned int side)1704 GMT_LOCAL bool gmtplot_annot_too_crowded (struct GMT_CTRL *GMT, double x, double y, unsigned int side) {
1705 	/* Checks if the proposed annotation is too close to a previously plotted annotation */
1706 	unsigned int i;
1707 	double d_min;
1708 
1709 	if (GMT->current.setting.map_annot_min_spacing <= 0.0) return (false);
1710 
1711 	for (i = 0, d_min = DBL_MAX; i < GMT_n_annotations[side]; i++)
1712 		d_min = MIN (d_min, hypot (GMT_x_annotation[side][i] - x, GMT_y_annotation[side][i] - y));
1713 	if (d_min < GMT->current.setting.map_annot_min_spacing) {
1714 		GMT_n_annotations_skip[side]++;
1715 		return (true);
1716 	}
1717 
1718 	/* OK to plot and add to list */
1719 
1720 	if (GMT_n_annotations[side] == GMT_alloc_annotations[side]) gmt_M_malloc2 (GMT, GMT_x_annotation[side], GMT_y_annotation[side], GMT_n_annotations[side], &(GMT_alloc_annotations[side]), double);
1721 	GMT_x_annotation[side][GMT_n_annotations[side]] = x, GMT_y_annotation[side][GMT_n_annotations[side]] = y, GMT_n_annotations[side]++;
1722 
1723 	return (false);
1724 }
1725 
1726 /*! . */
gmtplot_get_annot_offset(struct GMT_CTRL * GMT,bool * flip,unsigned int level)1727 GMT_LOCAL double gmtplot_get_annot_offset (struct GMT_CTRL *GMT, bool *flip, unsigned int level) {
1728 	/* Return offset in inches for text annotation.  If annotation
1729 	 * is to be placed 'inside' the map, set flip to true */
1730 
1731 	double a = GMT->current.setting.map_annot_offset[level];
1732 	if (GMT->current.setting.map_frame_type & GMT_IS_INSIDE) {	/* Inside annotation */
1733 		a = -fabs (a);
1734 		a -= fabs (GMT->current.setting.map_tick_length[GMT_PRIMARY]);
1735 		*flip = true;
1736 	}
1737 	else if (a >= 0.0) {	/* Outside annotation */
1738 		double dist = GMT->current.setting.map_tick_length[GMT_PRIMARY];	/* Length of tickmark (could be negative) */
1739 		/* For fancy frame we must consider that the frame width might exceed the ticklength */
1740 		if (GMT->current.setting.map_frame_type & GMT_IS_FANCY && GMT->current.setting.map_frame_width > dist) dist = GMT->current.setting.map_frame_width;
1741 		if (dist > 0.0) a += dist;
1742 		*flip = false;
1743 	}
1744 	else {		/* Inside annotation via negative tick length */
1745 		if (GMT->current.setting.map_tick_length[GMT_PRIMARY] < 0.0) a += GMT->current.setting.map_tick_length[GMT_PRIMARY];
1746 		*flip = true;
1747 	}
1748 
1749 	return (a);
1750 }
1751 
gmtplot_curved_boundary_offset(struct GMT_CTRL * GMT,double lon,double lat,int type,unsigned int level)1752 GMT_LOCAL double gmtplot_curved_boundary_offset (struct GMT_CTRL *GMT, double lon, double lat, int type, unsigned int level) {
1753 	/* For global curved maps like Mollweide etc, the parallel labeling occurs against an increasingly
1754 	 * sloping map border as we approach the poles.  Here we try to compensate for that by adding a bit
1755 	 * of extra annotation offset boost that depends on the latitude and size of annotation font */
1756 	double lat1, lat2, dlat, x1, x2, y1, y2, h, boost;
1757 	if (type == 0) return 0.0;	/* No boost for any longitude annotations at top/bottom of these global projections */
1758 	if (!gmt_M_is_misc (GMT)) return 0.0;	/* Not one of the global misc projections */
1759 	if (GMT->common.R.oblique) return 0.0;	/* Not a w/e/s/n curved frame */
1760 
1761 	/* OK, here we have Hammer, Robinson, etc. */
1762 
1763 	dlat = (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / 180.0;	/* So 1 degree for a global map */
1764 	if (doubleAlmostEqual (lat, -90)) lat1 = lat, lat2 = lat + dlat;	/* South pole point */
1765 	else if (doubleAlmostEqual (lat, 90)) lat1 = lat - dlat, lat2 = lat;	/* North pole point */
1766 	else lat1 = lat - dlat,	lat2 = lat + dlat;
1767 	gmt_geo_to_xy (GMT, lon, lat1, &x1, &y1);
1768 	gmt_geo_to_xy (GMT, lon, lat2, &x2, &y2);
1769 	h = GMT->current.setting.font_annot[level].size * GMT->session.u2u[GMT_PT][GMT_INCH] * GMT_LET_HEIGHT;	/* Approximate height of annotations */
1770 	boost = 0.375 * h * fabs (x2 - x1) / (y2 - y1);	/* Started with 0.5, 0.25 was too small so split the difference */
1771 	return boost;
1772 }
1773 
gmtplot_skip_end_annotation(struct GMT_CTRL * GMT,unsigned int axis,double x,double length)1774 GMT_LOCAL bool gmtplot_skip_end_annotation (struct GMT_CTRL *GMT, unsigned int axis, double x, double length) {
1775 	/* Returns true if this is an annotation at the end of the axis and we are either in GMT_IS_INSIDE mode or user added modifier +e[l|u] to the axis annotation settings */
1776 	if (GMT->current.map.frame.axis[axis].skip[0] && fabs (x) < GMT_CONV4_LIMIT) return true;   /* Skip annotation if it falls ~exactly at the start of the axis */
1777 	if (GMT->current.map.frame.axis[axis].skip[1] && fabs (x - length) < GMT_CONV4_LIMIT) return true;   /* Skip annotation if t falls ~exactly at the end of the axis */
1778 	if (GMT->current.setting.map_frame_type & GMT_IS_INSIDE && (fabs (x) < GMT_CONV4_LIMIT || fabs (x - length) < GMT_CONV4_LIMIT)) return true;   /* Skip annotation on edges when MAP_FRAME_TYPE = inside */
1779 	return false;	/* No, let's plot it */
1780 }
1781 
gmtplot_map_symbol(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,struct GMT_XINGS * xings,char * label,unsigned int type,bool annot,unsigned int level,unsigned int form)1782 GMT_LOCAL void gmtplot_map_symbol (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, struct GMT_XINGS *xings, char *label, unsigned int type, bool annot, unsigned int level, unsigned int form) {
1783 	/* type = 0 for lon and 1 for lat */
1784 
1785 	double line_angle, text_angle, div, tick_length, o_len, len, ca, sa, boost;
1786 	double *xx = xings->xx, *yy = xings->yy, *line_angles = xings->angle;
1787 	unsigned int i, annot_type, justify, *sides = xings->sides, nx = xings->nx;
1788 	bool flip;
1789 	len = gmtplot_get_annot_offset (GMT, &flip, level);	/* Get annotation offset, and flip justification if "inside" */
1790 	annot_type = 2 << type;		/* 2 = NS, 4 = EW */
1791 
1792 	for (i = 0; i < nx; i++) {
1793 		if (!(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE) && ((type == 0 && (sides[i] % 2)) || (type == 1 && !(sides[i] % 2)))) continue;
1794 
1795 		if (gmtlib_prepare_label (GMT, line_angles[i], sides[i], xx[i], yy[i], type, &line_angle, &text_angle, &justify)) continue;
1796 
1797 		boost = gmtplot_curved_boundary_offset (GMT, xings->lon[i], xings->lat[i], type, level);
1798 		sincosd (line_angle, &sa, &ca);
1799 		tick_length = GMT->current.setting.map_tick_length[GMT_PRIMARY];
1800 		o_len = len;
1801 		if (!flip && GMT->current.setting.map_annot_oblique & annot_type) o_len = tick_length;
1802 		if (GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_EXTEND_TICKS) {
1803 			div = ((sides[i] % 2) ? fabs (ca) : fabs (sa));
1804 			o_len /= div;
1805 		}
1806 		else
1807 			o_len += copysign (boost, o_len);
1808 		xx[i] += o_len * ca;
1809 		yy[i] += o_len * sa;
1810 		if (!flip && (GMT->current.setting.map_annot_oblique & annot_type) && GMT->current.setting.map_annot_offset[level] > 0.0) {
1811 			if (sides[i] % 2)
1812 				xx[i] += (sides[i] == 1) ? GMT->current.setting.map_annot_offset[level] : -GMT->current.setting.map_annot_offset[level];
1813 			else
1814 				yy[i] += (sides[i] == 2) ? GMT->current.setting.map_annot_offset[level] : -GMT->current.setting.map_annot_offset[level];
1815 		}
1816 
1817 		if (annot) {
1818 			double length = (type == GMT_X) ? GMT->current.map.width : GMT->current.map.height;
1819 			double pos = (type == GMT_X) ? xx[i] : yy[i];
1820 			if (gmtplot_annot_too_crowded (GMT, xx[i], yy[i], sides[i])) continue;
1821 			if (gmtplot_skip_end_annotation (GMT, type, pos, length)) continue;	/* Don't want annotations exactly at one or both ends of the axis */
1822 			if (GMT->current.proj.three_D && GMT->current.proj.z_project.cos_az > 0) {	/* Rotate annotation when seen "from North" */
1823 				if (!flip) justify = gmt_flip_justify (GMT, justify);
1824 				text_angle += 180.0;
1825 			}
1826 			else
1827 				if (flip) justify = gmt_flip_justify (GMT, justify);
1828 			PSL_plottext (PSL, xx[i], yy[i], GMT->current.setting.font_annot[level].size, label, text_angle, justify, form);
1829 		}
1830 	}
1831 }
1832 
gmtplot_map_symbol_ew(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lat,char * label,double west,double east,bool annot,unsigned int level,unsigned int form)1833 GMT_LOCAL void gmtplot_map_symbol_ew (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lat, char *label, double west, double east, bool annot, unsigned int level, unsigned int form) {
1834 	unsigned int i, nc;
1835 	struct GMT_XINGS *xings = NULL;
1836 
1837 	nc = gmtlib_map_latcross (GMT, lat, west, east, &xings);
1838 	for (i = 0; i < nc; i++)
1839 		//gmtplot_map_symbol (GMT, PSL, xings[i].xx, xings[i].yy, xings[i].sides, xings[i].angle, label, xings[i].nx, 1, annot, level, form);
1840 		gmtplot_map_symbol (GMT, PSL, &(xings[i]), label, 1, annot, level, form);
1841 	if (nc) gmt_M_free (GMT, xings);
1842 }
1843 
gmtplot_map_symbol_ns(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double lon,char * label,double south,double north,bool annot,unsigned int level,unsigned int form)1844 GMT_LOCAL void gmtplot_map_symbol_ns (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double lon, char *label, double south, double north, bool annot, unsigned int level, unsigned int form) {
1845 	unsigned int i, k, nc;
1846 	struct GMT_XINGS *xings = NULL;
1847 	bool flip = (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_LON && gmt_M_type (GMT, GMT_IN, GMT_Y) != GMT_IS_LAT && GMT->current.proj.scale[GMT_Y] < 0.0);
1848 	/* flip deals with the problem when x is lon and geographic annotation machinery is used but y is Cartesian and upside down */
1849 	nc = gmtlib_map_loncross (GMT, lon, south, north, &xings);
1850 	for (i = 0; i < nc; i++) {
1851 		if (flip) for (k = 0; k < xings[i].nx; k++) {	/* Must turn sides 0 and 2 into sides 2 and 0 */
1852 			if ((xings[i].sides[k] % 2) == 0) xings[i].sides[k] = 2 - xings[i].sides[k];	/* Flip up and down sides */
1853 		}
1854 		//gmtplot_map_symbol (GMT, PSL, xings[i].xx, xings[i].yy, xings[i].sides, xings[i].angle, label, xings[i].nx, 0, annot, level, form);
1855 		gmtplot_map_symbol (GMT, PSL, &(xings[i]), label, 0, annot, level, form);
1856 	}
1857 	if (nc) gmt_M_free (GMT, xings);
1858 }
1859 
gmtplot_z_gridlines(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double zmin,double zmax,int plane,unsigned int mode3d,int quadrant)1860 GMT_LOCAL void gmtplot_z_gridlines (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double zmin, double zmax, int plane, unsigned int mode3d, int quadrant) {
1861 	unsigned int k, i, nz, n, item[2] = {GMT_GRID_UPPER, GMT_GRID_LOWER};
1862 	int qplane = 1 - plane;
1863 	double dz, zz, dd, min, max, z0, z1, xx, dummy, value, *z = NULL, *d = NULL;
1864 	char *plane_name[2] = {"y-z", "x-z"};
1865 
1866 	if (qplane == GMT_X) {
1867 		value = (quadrant == 4) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO];
1868 		min = GMT->current.proj.rect[XLO];	max = GMT->current.proj.rect[XHI];
1869 	}
1870 	else {
1871 		value = (quadrant == 3) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO];
1872 		min = GMT->current.proj.rect[YLO];	max = GMT->current.proj.rect[YHI];
1873 	}
1874 
1875 	for (k = 0; k < 2; k++) {
1876 		if (fabs (GMT->current.setting.map_grid_cross_size[k]) > 0.0) continue;
1877 
1878 		dz = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Z].type, &GMT->current.map.frame.axis[GMT_Z].item[item[k]]);	/* Gridline spacing in z */
1879 
1880 		if (!GMT->current.map.frame.axis[GMT_Z].item[item[k]].active || fabs(dz) == 0.0) continue;
1881 
1882 		PSL_comment (PSL, "%s gridlines %s\n", plane_name[plane], k ? "(secondary)" : "(primary)");
1883 
1884 		gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[k]);
1885 
1886 		nz = gmtlib_coordinate_array (GMT, zmin, zmax, &GMT->current.map.frame.axis[GMT_Z].item[item[k]], &z, NULL);
1887 		for (i = 0; i < nz; i++) {	/* Here z acts as y and x|y acts as x */
1888 			/* Draw one horizontal line */
1889 			zz = gmt_z_to_zz (GMT, z[i]);
1890 			PSL_plotsegment (PSL, min, zz, max, zz);
1891 		}
1892 		z0 = gmt_z_to_zz (GMT, zmin);	/* These are the projected min and max z values, i.e., the ends of vertical gridlines on the back walls */
1893 		z1 = gmt_z_to_zz (GMT, zmax);
1894 
1895 		dd = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[qplane].type, &GMT->current.map.frame.axis[qplane].item[item[k]]);
1896 		if (!GMT->current.map.frame.axis[qplane].item[item[k]].active || fabs(dd) == 0.0) continue;
1897 		n = gmtlib_coordinate_array (GMT, GMT->common.R.wesn[2*qplane], GMT->common.R.wesn[2*qplane+1], &GMT->current.map.frame.axis[qplane].item[item[k]], &d, NULL);
1898 		for (i = 0; i < n; i++) {	/* Here z acts as y and x|y acts as x */
1899 			/* Draw one horizontal line */
1900 			if (qplane == GMT_X)	/* The "x" are projection x or longitude */
1901 				gmt_geo_to_xy (GMT, d[i], value, &xx, &dummy);
1902 			else	/* The "x" are projection y or latitude */
1903 				gmt_geo_to_xy (GMT, value, d[i], &dummy, &xx);
1904 
1905 			PSL_plotsegment (PSL, xx, z0, xx, z1);
1906 		}
1907 		gmt_M_free (GMT, d);
1908 
1909 		PSL_setdash (PSL, NULL, 0);
1910 	}
1911 	if (!GMT->current.map.frame.draw_wall && z && (mode3d & GMT_3D_BOX) == 0) {
1912 		PSL_plotsegment (PSL, min, z0, min, z1);
1913 		PSL_plotsegment (PSL, max, z0, max, z1);
1914 	}
1915 	gmt_M_free (GMT, z);
1916 }
1917 
gmtplot_save_current_gridlines(struct GMT_CTRL * GMT)1918 GMT_LOCAL int gmtplot_save_current_gridlines (struct GMT_CTRL *GMT) {
1919 	/* If only primary gridlines are drawn, we save information to gmt.history */
1920 
1921 	if (!(GMT->current.map.frame.axis[GMT_X].item[GMT_GRID_UPPER].active || GMT->current.map.frame.axis[GMT_Y].item[GMT_GRID_UPPER].active)) return (GMT_NOERROR);	/* Primary gridlines not selected, so bail */
1922 	if (GMT->current.map.frame.axis[GMT_X].item[GMT_GRID_LOWER].active || GMT->current.map.frame.axis[GMT_Y].item[GMT_GRID_LOWER].active) return (GMT_NOERROR);		/* Secondary gridlines selected, so bail */
1923 
1924 	GMT->current.plot.gridline_spacing[GMT_X] = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_GRID_UPPER]);
1925 	GMT->current.plot.gridline_spacing[GMT_Y] = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[GMT_GRID_UPPER]);
1926 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Save current gridline information to gmt.history\n");
1927 	return (GMT_NOERROR);
1928 }
1929 
gmtplot_get_current_gridlines(struct GMT_CTRL * GMT,double * dx,double * dy)1930 GMT_LOCAL int gmtplot_get_current_gridlines (struct GMT_CTRL *GMT, double *dx, double *dy) {
1931 	/* Obtain the previous gridline intervals, if nonzero */
1932 
1933 	if (gmt_M_is_zero (GMT->current.plot.gridline_spacing[GMT_X]) && gmt_M_is_zero (GMT->current.plot.gridline_spacing[GMT_Y])) return (GMT_NOERROR);	/* No gridlines drawn yet */
1934 	*dx = GMT->current.plot.gridline_spacing[GMT_X];
1935 	*dy = GMT->current.plot.gridline_spacing[GMT_Y];
1936 	return (GMT_NOERROR);
1937 }
1938 
gmtplot_map_gridlines(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)1939 GMT_LOCAL void gmtplot_map_gridlines (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
1940 	unsigned int k, i, np, item[2] = {GMT_GRID_UPPER, GMT_GRID_LOWER};
1941 	double dx, dy, *v = NULL;
1942 	bool reset = false;
1943 	struct GMT_PLOT_AXIS *A[2] = {NULL, NULL};
1944 
1945 	for (k = i = 0; k < 2; k++) {	/* First check if any gridlines are requested */
1946 		if (fabs (GMT->current.setting.map_grid_cross_size[k]) > 0.0) continue;
1947 		if (GMT->current.setting.map_grid_cross_type[k] > GMT_CROSS_NORMAL) continue;
1948 		if (!(GMT->current.map.frame.axis[GMT_X].item[item[k]].active || GMT->current.map.frame.axis[GMT_Y].item[item[k]].active)) continue;
1949 		i++;
1950 	}
1951 	if (i == 0) return;	/* No gridlines requested */
1952 
1953 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Entering gmtplot_map_gridlines\n");
1954 	reset = gmtlib_genper_reset (GMT, reset);
1955 
1956 	if (gmtplot_save_current_gridlines (GMT)) return;	/* Save a file with primary only gridline interval used under modern mode only; return if error */
1957 
1958 	for (k = 0; k < 2; k++) {
1959 		if (fabs (GMT->current.setting.map_grid_cross_size[k]) > 0.0) continue;	/* Must have a size of zero to be a grid line, else it is a cross or tick */
1960 
1961 		A[GMT_X] = &GMT->current.map.frame.axis[GMT_X];	/* Short-hand for x-axis */
1962 		A[GMT_Y] = &GMT->current.map.frame.axis[GMT_Y];	/* Short-hand for y-axis */
1963 		dx = gmtlib_get_map_interval (GMT, A[GMT_X]->type, &A[GMT_X]->item[item[k]]);	/* x grid spacing; will be 0 if custom intervals */
1964 		dy = gmtlib_get_map_interval (GMT, A[GMT_Y]->type, &A[GMT_Y]->item[item[k]]);	/* y grid spacing; will be 0 if custom intervals */
1965 
1966 		if (!(A[GMT_X]->item[item[k]].active || A[GMT_Y]->item[item[k]].active)) continue;	/* Neither is active */
1967 
1968 		PSL_comment (PSL, "%s\n", k ? "Map gridlines (secondary)" : "Map gridlines (primary)");
1969 
1970 		gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[k]);
1971 
1972 		if (A[GMT_X]->item[k].special && (np = gmtlib_load_custom_annot (GMT, &GMT->current.map.frame.axis[GMT_X], 'g', &v, NULL))) {
1973 			gmtplot_x_grid (GMT, PSL, s, n, v, np);
1974 			gmt_M_free (GMT, v);
1975 		}
1976 		else if (!A[GMT_X]->item[item[k]].active || fabs(dx) == 0.0) { /* Nothing */ }
1977 		else if (GMT->current.proj.xyz_projection[GMT_X] == GMT_TIME)
1978 			plot_timex_grid (GMT, PSL, w, e, s, n, item[k]);
1979 		else if (GMT->current.proj.xyz_projection[GMT_X] == GMT_LOG10)
1980 			gmtplot_logx_grid (GMT, PSL, w, e, s, n, dx);
1981 		else if (GMT->current.proj.xyz_projection[GMT_X] == GMT_POW)
1982 			gmtplot_powx_grid (GMT, PSL, w, e, s, n, dx);
1983 		else if (GMT->current.map.frame.obl_grid)	/* Draw oblique grid lines that go S to N */
1984 			gmtplot_linearx_oblgrid (GMT, PSL, w, e, s, n, dx);
1985 		else	/* Draw grid lines that go S to N */
1986 			gmt_linearx_grid (GMT, PSL, w, e, s, n, dx);
1987 
1988 		if (A[GMT_Y]->item[k].special && (np = gmtlib_load_custom_annot (GMT, &GMT->current.map.frame.axis[GMT_Y], 'g', &v, NULL))) {
1989 			gmtplot_y_grid (GMT, PSL, w, e, v, np);
1990 			gmt_M_free (GMT, v);
1991 		}
1992 		else if (!A[GMT_Y]->item[item[k]].active || fabs(dy) == 0.0) { /* Nothing */ }
1993 		else if (GMT->current.proj.xyz_projection[GMT_Y] == GMT_TIME)
1994 			gmtplot_timey_grid (GMT, PSL, w, e, s, n, item[k]);
1995 		else if (GMT->current.proj.xyz_projection[GMT_Y] == GMT_LOG10)
1996 			gmtplot_logy_grid (GMT, PSL, w, e, s, n, dy);
1997 		else if (GMT->current.proj.xyz_projection[GMT_Y] == GMT_POW)
1998 			gmtplot_powy_grid (GMT, PSL, w, e, s, n, dy);
1999 		else if (GMT->current.map.frame.obl_grid)	/* Draw oblique grid lines that go S to N */
2000 			gmtplot_lineary_oblgrid (GMT, PSL, w, e, s, n, dx);
2001 		else	/* Draw grid lines that go E to W */
2002 			gmtplot_lineary_grid (GMT, PSL, w, e, s, n, dy);
2003 
2004 		PSL_setdash (PSL, NULL, 0);
2005 	}
2006 	gmtlib_genper_reset (GMT, reset);
2007 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Exiting gmtplot_map_gridlines\n");
2008 }
2009 
gmtplot_cross_angle(struct GMT_CTRL * GMT,double lon,double lat,double dx,double dy,unsigned int type)2010 GMT_LOCAL double gmtplot_cross_angle (struct GMT_CTRL *GMT, double lon, double lat, double dx, double dy, unsigned int type) {
2011 	/* (lon,alt) is the point and we want a tangent angle there in radians */
2012 	if (gmt_M_is_geographic (GMT, GMT_IN)) {	/* General case with arbitrary orientations */
2013 		double x0, y0, x1, y1;
2014 		if (type == GMT_X) {	/* Compute angle from d/dx */
2015 			gmt_geo_to_xy (GMT, lon - dx, lat, &x0, &y0);
2016 			gmt_geo_to_xy (GMT, lon + dx, lat, &x1, &y1);
2017 		}
2018 		else {	/* Get d/dy */
2019 			gmt_geo_to_xy (GMT, lon, ((lat - dy) < -90.0) ? -90.0 : lat - dy, &x0, &y0);
2020 			gmt_geo_to_xy (GMT, lon, ((lat + dy) >  90.0) ?  90.0 : lat + dy, &x1, &y1);
2021 		}
2022 		return (d_atan2 (y1-y0, x1-x0));
2023 	}
2024 	/* Cartesian is always x horizontal, y 90 degrees up */
2025 	return ((type == GMT_X) ? 0.0 : M_PI_2);
2026 }
2027 
gmtplot_map_gridcross(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)2028 GMT_LOCAL void gmtplot_map_gridcross (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
2029 	unsigned int i, j, k, nx, ny, item[2] = {GMT_GRID_UPPER, GMT_GRID_LOWER};
2030 	double x0, y0, xa, xb, ya, yb, xi, yj, *x = NULL, *y = NULL;
2031 	double angle, Ca, Sa, L, dx, dy;
2032 	struct GMT_PLOT_AXIS *A[2] = {NULL, NULL};
2033 
2034 	for (k = i = 0; k < 2; k++)
2035 		if (GMT->current.setting.map_grid_cross_size[k] > 0.0 && GMT->current.setting.map_grid_cross_type[k] == GMT_CROSS_NORMAL) i++;
2036 
2037 	if (i == 0) return;	/* No grid crosses requested */
2038 
2039 	gmt_map_clip_on (GMT, GMT->session.no_rgb, 3);
2040 
2041 	for (k = 0; k < 2; k++) {
2042 		if (gmt_M_is_zero (GMT->current.setting.map_grid_cross_size[k])) continue;
2043 
2044 		PSL_comment (PSL, "%s\n", k ? "Map gridcrosses (secondary)" : "Map gridcrosses (primary)");
2045 
2046 		gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[k]);
2047 
2048 		A[GMT_X] = &GMT->current.map.frame.axis[GMT_X];	/* Short-hand for x-axis */
2049 		A[GMT_Y] = &GMT->current.map.frame.axis[GMT_Y];	/* Short-hand for y-axis */
2050 		nx = gmtlib_coordinate_array (GMT, w, e, &A[GMT_X]->item[item[k]], &x, NULL);
2051 		ny = gmtlib_coordinate_array (GMT, s, n, &A[GMT_Y]->item[item[k]], &y, NULL);
2052 		dy = (ny > 1) ? y[1] - y[0] : GMT->current.map.dlat;
2053 		dx = (nx > 1) ? x[1] - x[0] : GMT->current.map.dlon;
2054 
2055 		L = 0.5 * GMT->current.setting.map_grid_cross_size[k];
2056 
2057 		for (j = 0; j < ny; j++) {
2058 			yj = y[j];
2059 			for (i = 0; i < nx; i++) {
2060 				if (gmt_M_pole_is_point(GMT) && doubleAlmostEqual (fabs (yj), 90.0)) {	/* Only place one grid cross at the poles for maps where the poles are points */
2061 					xi = GMT->current.proj.central_meridian;
2062 					i = nx;	/* This ends the loop for this particular latitude */
2063 				}
2064 				else
2065 					xi = x[i];
2066 
2067 				if (gmt_map_outside (GMT, xi, yj)) continue;	/* Outside map */
2068 
2069 				gmt_geo_to_xy (GMT, xi, yj, &x0, &y0);	/* Grid crossing center */
2070 				angle = gmtplot_cross_angle (GMT, xi, yj, dx, dy, GMT_X);
2071 				sincos (angle, &Sa, &Ca);
2072 				xa = x0 - L * Ca;
2073 				xb = x0 + L * Ca;
2074 				ya = y0 - L * Sa;
2075 				yb = y0 + L * Sa;
2076 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2077 
2078 				angle += M_PI_2;	/* Since crosses are orthogonal */
2079 				sincos (angle, &Sa, &Ca);
2080 				xa = x0 - L * Ca;
2081 				xb = x0 + L * Ca;
2082 				ya = y0 - L * Sa;
2083 				yb = y0 + L * Sa;
2084 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2085 			}
2086 		}
2087 		if (nx) gmt_M_free (GMT, x);
2088 		if (ny) gmt_M_free (GMT, y);
2089 
2090 		PSL_setdash (PSL, NULL, 0);
2091 
2092 	}
2093 	gmt_map_clip_off (GMT);
2094 }
2095 
gmtplot_set_gridcross_limbs(struct GMT_CTRL * GMT,unsigned int axis,unsigned int kind,double value,unsigned int * B,unsigned int * E)2096 GMT_LOCAL void gmtplot_set_gridcross_limbs (struct GMT_CTRL *GMT, unsigned int axis, unsigned int kind, double value, unsigned int *B, unsigned int *E) {
2097 	*B = *E = 0;	/* Default is symmetric tick */
2098 	if (GMT->current.setting.map_grid_cross_type[kind] == GMT_CROSS_SYMM) return;	/* Symmetric tick */
2099 	if (gmt_M_is_zero (value)) return;	/* Symmetrical as well for zero */
2100 	if (gmt_M_type (GMT, GMT_IN, axis) == GMT_IS_LON) {	/* Worry about longitudes */
2101 		if (doubleAlmostEqualZero (fabs (value), 180.0)) return;	/* Symmetrical at both 0 and 180 longitude */
2102 		if (value > 0.0 && value < 180.0) *B = 1; else *E = 1;	/* One-sided */
2103 	}
2104 	else {
2105 		if (value > 0.0) *B = 1; else *E = 1;	/* One-sided */
2106 	}
2107 }
2108 
gmtplot_map_gridticks(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)2109 GMT_LOCAL void gmtplot_map_gridticks (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
2110 	/* Draw symmetric or asymmetric grid ticks along the courser gridlines for parallels and meridians */
2111 	bool single, point_point = false;
2112 	unsigned int i, j, k, nx, ny, G_nx, G_ny, G_i, G_j, B = 0, E = 0, item[2] = {GMT_GRID_UPPER, GMT_GRID_LOWER};
2113 	double x0, y0, xa, xb, ya, yb, xi, yj, *x = NULL, *y = NULL, *G_x = NULL, *G_y = NULL;
2114 	double angle, Ca, Sa, L, sgn[2] = {1.0, 0.0}, G_dx, G_dy, dx, dy, ys, yn;
2115 
2116 	for (k = i = 0; k < 2; k++)
2117 		if (GMT->current.setting.map_grid_cross_type[k] > GMT_CROSS_NORMAL) i++;
2118 
2119 	if (i == 0) return;	/* No gridline ticks requested */
2120 
2121 	if (gmtplot_get_current_gridlines (GMT, &G_dx, &G_dy)) {
2122 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Gridline tick embellishments specified but no gridlines have been laid down first\n");
2123 		return;
2124 	}
2125 
2126 	if (gmt_M_pole_is_point (GMT)) {	/* Might have two separate domains of gridlines */
2127 		point_point = true;
2128 		if (GMT->current.proj.projection_GMT == GMT_POLAR) {	/* Different for polar graphs since "lat" = 0 is at the center */
2129 			ys =  90.0 - GMT->current.setting.map_polar_cap[0];
2130 			yn = n;
2131 		}
2132 		else {
2133 			ys = MAX (s, -GMT->current.setting.map_polar_cap[0]);
2134 			yn = MIN (n,  GMT->current.setting.map_polar_cap[0]);
2135 		}
2136 	}
2137 	else {	/* No polar cap to worry about */
2138 		ys = s;
2139 		yn = n;
2140 	}
2141 	/* Get the course gridline spacings */
2142 	G_nx = gmtlib_linear_array (GMT, w, e, G_dx, 0.0, &G_x);
2143 	G_ny = gmtlib_linear_array (GMT, ys, yn, G_dy, 0.0, &G_y);
2144 
2145 	gmt_map_clip_on (GMT, GMT->session.no_rgb, 3);
2146 
2147 	for (k = 0; k < 2; k++) {
2148 		if (gmt_M_is_zero (GMT->current.setting.map_grid_cross_size[k])) continue;
2149 
2150 		PSL_comment (PSL, "%s\n", k ? "Map gridticks (secondary)" : "Map gridticks (primary)");
2151 
2152 		gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[k]);
2153 
2154 		/* Get the detailed gridline tick spacings */
2155 		nx = gmtlib_coordinate_array (GMT, w, e, &GMT->current.map.frame.axis[GMT_X].item[item[k]], &x, NULL);
2156 		ny = gmtlib_coordinate_array (GMT, ys, yn, &GMT->current.map.frame.axis[GMT_Y].item[item[k]], &y, NULL);
2157 		/* Get a small increment in x and y for computing local angle */
2158 		dy = (ny > 1) ? y[1] - y[0] : GMT->current.map.dlat;
2159 		dx = (nx > 1) ? x[1] - x[0] : GMT->current.map.dlon;
2160 
2161 		L = 0.5 * fabs (GMT->current.setting.map_grid_cross_size[k]);
2162 
2163 		for (G_j = 0; G_j < G_ny; G_j++) {	/* For each gridline parallel */
2164 			yj = G_y[G_j];	/* Current parallel */
2165 			single = (gmt_M_pole_is_point(GMT) && doubleAlmostEqualZero (fabs (yj), 90.0));	/* Only place one grid tick at the poles for maps where the poles are points */
2166 			if (gmt_M_pole_is_point(GMT) && doubleAlmostEqualZero (fabs (yj), 90.0)) continue;	/* No grid tick at single pole points */
2167 			gmtplot_set_gridcross_limbs (GMT, GMT_Y, k, yj, &B, &E);
2168 			for (i = 0; i < nx; i++) {	/* Going along this parallel to place ticks */
2169 				if (single) {
2170 					xi = GMT->current.proj.central_meridian;
2171 					i = nx;	/* This ends the loop for this particular parallel */
2172 				}
2173 				else
2174 					xi = x[i];	/* Current longitude */
2175 				if (gmt_M_is_zero (fmod (xi, G_dx))) continue;	/* Not draw on top of gridlines */
2176 				if (gmt_map_outside (GMT, xi, yj)) continue;	/* Outside map */
2177 				gmt_geo_to_xy (GMT, xi, yj, &x0, &y0);
2178 				angle = gmtplot_cross_angle (GMT, xi, yj, dx, dy, GMT_Y);
2179 				sincos (angle, &Sa, &Ca);
2180 				xa = x0 - L * Ca * sgn[B];
2181 				xb = x0 + L * Ca * sgn[E];
2182 				ya = y0 - L * Sa * sgn[B];
2183 				yb = y0 + L * Sa * sgn[E];
2184 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2185 			}
2186 		}
2187 
2188 		for (G_i = 0; G_i < G_nx; G_i++) {	/* For each gridline meridian */
2189 			xi = G_x[G_i];	/* Current meridian */
2190 			gmtplot_set_gridcross_limbs (GMT, GMT_X, k, xi, &B, &E);
2191 			for (j = 0; j < ny; j++) {
2192 				yj = y[j];
2193 				if (point_point && doubleAlmostEqualZero (fabs (yj), GMT->current.setting.map_polar_cap[0])) continue;	/* No latitude ticks long polar circles */
2194 				if (gmt_map_outside (GMT, xi, yj)) continue;	/* Outside map */
2195 				if (gmt_M_pole_is_point(GMT) && doubleAlmostEqualZero (fabs (yj), 90.0)) continue; 	/* No grid tick at single pole points */
2196 				if (gmt_M_is_zero (fmod (yj, G_dy))) continue;	/* Not draw on top of gridlines */
2197 				gmt_geo_to_xy (GMT, xi, yj, &x0, &y0);
2198 				angle = gmtplot_cross_angle (GMT, xi, yj, dx, dy, GMT_X);
2199 				sincos (angle, &Sa, &Ca);
2200 				xa = x0 - L * Ca * sgn[B];
2201 				xb = x0 + L * Ca * sgn[E];
2202 				ya = y0 - L * Sa * sgn[B];
2203 				yb = y0 + L * Sa * sgn[E];
2204 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2205 			}
2206 		}
2207 
2208 		if (point_point && k == GMT_SECONDARY && MAX (fabs(s), fabs(n)) > GMT->current.setting.map_polar_cap[0]) {
2209 			/* At least one of the southern or northern polar cap parallels are plotted */
2210 			unsigned int nx_p, ny_p, cap, m;
2211 			double limit[2] = {s, n}, sign[2] = {-1.0, +1.0}, *xp = NULL, *yp = NULL;
2212 
2213 			/* Do coarser meridional lines inside polar cap */
2214 			nx_p = gmtlib_linear_array (GMT, w, e, GMT->current.setting.map_polar_cap[1], GMT->current.map.frame.axis[GMT_X].phase, &xp);
2215 			for (cap = 0; cap < 2; cap++) {	/* For south and north cap */
2216 				if (GMT->current.setting.map_polar_cap[0] < fabs (limit[cap])) {	/* Tick this polar cap */
2217 					yj = sign[cap] * GMT->current.setting.map_polar_cap[0];
2218 					gmtplot_set_gridcross_limbs (GMT, GMT_Y, k, yj, &B, &E);
2219 					for (i = 0; i < nx; i++) {	/* Going along this parallel to place ticks */
2220 						xi = x[i];	/* Current longitude */
2221 						if (gmt_M_is_zero (fmod (xi, GMT->current.setting.map_polar_cap[1]))) continue;	/* Not draw on top of coarse gridlines */
2222 						if (gmt_map_outside (GMT, xi, yj)) continue;	/* Outside map */
2223 						gmt_geo_to_xy (GMT, xi, yj, &x0, &y0);
2224 						angle = gmtplot_cross_angle (GMT, xi, yj, dx, dy, GMT_Y);
2225 						sincos (angle, &Sa, &Ca);
2226 						xa = x0 - L * Ca * sgn[B];
2227 						xb = x0 + L * Ca * sgn[E];
2228 						ya = y0 - L * Sa * sgn[B];
2229 						yb = y0 + L * Sa * sgn[E];
2230 						PSL_plotsegment (PSL, xa, ya, xb, yb);
2231 					}
2232 					if (cap == 0) /* South cap */
2233 						ny_p = gmtlib_coordinate_array (GMT, -90.0, ys, &GMT->current.map.frame.axis[GMT_Y].item[item[k]], &yp, NULL);
2234 					else
2235 						ny_p = gmtlib_coordinate_array (GMT, yn, 90.0, &GMT->current.map.frame.axis[GMT_Y].item[item[k]], &yp, NULL);
2236 					for (m = 0; m < nx_p; m++) {	/* For the coarse meridional polar cap lines */
2237 						xi = xp[m];	/* Current meridian */
2238 						gmtplot_set_gridcross_limbs (GMT, GMT_X, k, xi, &B, &E);
2239 						for (j = 0; j < ny_p; j++) {
2240 							yj = yp[j];
2241 							if (point_point && doubleAlmostEqualZero (fabs (yj), GMT->current.setting.map_polar_cap[0])) continue;	/* No latitude ticks long polar circles */
2242 							if (gmt_map_outside (GMT, xi, yj)) continue;	/* Outside map */
2243 							if (gmt_M_pole_is_point(GMT) && doubleAlmostEqualZero (fabs (yj), 90.0)) continue; 	/* No grid tick at single pole points */
2244 							gmt_geo_to_xy (GMT, xi, yj, &x0, &y0);
2245 							angle = gmtplot_cross_angle (GMT, xi, yj, dx, dy, GMT_X);
2246 							sincos (angle, &Sa, &Ca);
2247 							xa = x0 - L * Ca * sgn[B];
2248 							xb = x0 + L * Ca * sgn[E];
2249 							ya = y0 - L * Sa * sgn[B];
2250 							yb = y0 + L * Sa * sgn[E];
2251 							PSL_plotsegment (PSL, xa, ya, xb, yb);
2252 						}
2253 					}
2254 					if (ny_p) gmt_M_free (GMT, yp);
2255 				}
2256 			}
2257 			if (nx_p) gmt_M_free (GMT, xp);
2258 		}
2259 
2260 		if (nx) gmt_M_free (GMT, x);
2261 		if (ny) gmt_M_free (GMT, y);
2262 
2263 		PSL_setdash (PSL, NULL, 0);
2264 	}
2265 	if (G_nx) gmt_M_free (GMT, G_x);
2266 	if (G_ny) gmt_M_free (GMT, G_y);
2267 
2268 	gmt_map_clip_off (GMT);
2269 }
2270 
gmtplot_skip_polar_apex_annotation(struct GMT_CTRL * GMT,unsigned int i,double * val,unsigned int ny)2271 GMT_LOCAL bool gmtplot_skip_polar_apex_annotation (struct GMT_CTRL *GMT, unsigned int i, double *val, unsigned int ny) {
2272 	/* Determine if the W and E annotations on a polar basemap can be placed when the radius from the center to
2273 	 * the annotation location is zero.  This depends on w-e range and a few special cases */
2274 	double annot_height, annot_offset, alpha, beta;
2275 	if (GMT->current.proj.projection_GMT != GMT_POLAR) return false;	/* No action unless the polar -JP|p projection */
2276 	if (!(GMT->current.map.frame.side[W_SIDE] == GMT_AXIS_ALL && GMT->current.map.frame.side[E_SIDE] == GMT_AXIS_ALL)) return false;	/* Requires -BWE to have overprinting issues */
2277 	if (GMT->current.proj.flip) {	/* Here the north value is at the potential apex */
2278 		//if (i < (ny-1) || !doubleAlmostEqualZero (GMT->common.R.wesn[YHI], val[i])) return false;	/* Not apex label */
2279 		if (i < (ny-1) || !doubleAlmostEqualZero (GMT->current.proj.flip_radius, val[i])) return false;	/* Not apex label */
2280 	}
2281 	else {	/* Here radius 0 is at the potential apex */
2282 		if (i > 0 || !gmt_M_is_zero (val[i])) return false;	/*  Not apex label  */
2283 	}
2284 	/* OK, here the label for W or E is right at the apex.  Only plot if overprinting is unlikely */
2285 	if (doubleAlmostEqualZero (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO], 180.0)) return false;	/* No apex when angular range is exactly 180 */
2286 	/* Determine maximum apex angle that still avoids overlap - this depends on fontsize and ticklength */
2287 	annot_height  = GMT_LET_HEIGHT * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH;	/* Approximate height of annotation in inches */
2288 	annot_offset = MAX (0.0, GMT->current.setting.map_annot_offset[GMT_PRIMARY]) + MAX (0.0, GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER]);	/* Add up distance from axis to annotation */
2289 	beta = 0.5 * (180 + GMT->common.R.wesn[XLO] - GMT->common.R.wesn[XHI]);	/* half-angle between the two gridline directions at the apex at the W and E sides */
2290 	alpha = (gmt_M_is_zero (annot_offset)) ? 90.0 : R2D * atan (0.5 * annot_height / annot_offset);	/* Angle between gridline direction and line from apex to nearest textbox corner */
2291 	if (alpha > beta) return true;	/* Apex angle not large enough to avoid overprinting */
2292 	//if (range > 135.0) return true;	/* Apex angle not large enough to avoid overprinting */
2293 	return false;	/* OK to place the label */
2294 }
2295 
gmtplot_map_tickitem(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,unsigned int item)2296 GMT_LOCAL void gmtplot_map_tickitem (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, unsigned int item) {
2297 	unsigned int i, nx = 0, ny = 0;
2298 	bool do_x, do_y;
2299 	double dx, dy, *val = NULL, len, shift = 0.0;
2300 
2301 	if (! (GMT->current.map.frame.axis[GMT_X].item[item].active || GMT->current.map.frame.axis[GMT_Y].item[item].active)) return;
2302 
2303 	dx = (GMT->current.map.frame.axis[GMT_X].file_custom) ? 1.0 : gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[item]);
2304 	dy = (GMT->current.map.frame.axis[GMT_Y].file_custom) ? 1.0 : gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[item]);
2305 
2306 	if (dx <= 0.0 && dy <= 0.0) return;
2307 
2308 	do_x = dx > 0.0 && GMT->current.map.frame.axis[GMT_X].item[item].active && (item == GMT_ANNOT_UPPER ||
2309 		(item == GMT_TICK_UPPER && dx != gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_ANNOT_UPPER])) ||
2310 		(item == GMT_TICK_LOWER && dx != gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_ANNOT_LOWER])));
2311 	do_y = dy > 0.0 && GMT->current.map.frame.axis[GMT_Y].item[item].active && (item == GMT_ANNOT_UPPER ||
2312 		(item == GMT_TICK_UPPER && dy != gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_UPPER])) ||
2313 		(item == GMT_TICK_LOWER && dy != gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_LOWER])));
2314 	len = GMT->current.setting.map_tick_length[item];
2315 	if (GMT->current.setting.map_frame_type & GMT_IS_INSIDE) len = -fabs (len);	/* Negative to become inside */
2316 
2317 	GMT->current.map.on_border_is_outside = true;	/* Temporarily, points on the border are outside */
2318 
2319 	if (do_x) {	/* Draw grid lines that go E to W */
2320 		if (GMT->current.map.frame.axis[GMT_X].file_custom)
2321 			nx = gmtlib_coordinate_array (GMT, w, e, &GMT->current.map.frame.axis[GMT_X].item[item], &val, NULL);
2322 		else
2323 			nx = gmtlib_linear_array (GMT, w, e, dx, GMT->current.map.frame.axis[GMT_X].phase, &val);
2324 		for (i = 0; i < nx; i++)  {
2325 			shift = gmtplot_shift_gridline (GMT, val[i], GMT_X);
2326 			gmtplot_map_lontick (GMT, PSL, val[i] + shift, s, n, len);
2327 		}
2328 		if (nx) gmt_M_free (GMT, val);
2329 	}
2330 
2331 	if (do_y) {	/* Draw grid lines that go S to N */
2332 		if (GMT->current.proj.z_down) {
2333 			if (GMT->current.map.frame.axis[GMT_Y].file_custom)
2334 				ny = gmtlib_coordinate_array (GMT, 0.0, GMT->current.proj.z_radius-s, &GMT->current.map.frame.axis[GMT_Y].item[item], &val, NULL);
2335 			else if (GMT->current.proj.z_down == GMT_ZDOWN_Z) /* z = n - r */
2336 				ny = gmtlib_linear_array (GMT, 0.0, GMT->current.proj.z_radius-s, dy, GMT->current.map.frame.axis[GMT_Y].phase, &val);
2337 			else if (GMT->current.proj.z_down == GMT_ZDOWN_ZP) /* r = rp - z */
2338 				ny = gmtlib_linear_array (GMT, GMT->current.proj.z_radius-n, GMT->current.proj.z_radius-s, dy, GMT->current.map.frame.axis[GMT_Y].phase, &val);
2339 			for (i = 0; i < ny; i++) {
2340 				if (GMT->current.proj.z_down == GMT_ZDOWN_ZP)
2341 					val[i] = GMT->current.proj.z_radius - val[i];	/* These are the z values needed for positioning */
2342 				else
2343 					val[i] = GMT->common.R.wesn[YHI] - val[i];	/* These are the radial values needed for positioning */
2344 			}
2345 		}
2346 		else {
2347 			if (GMT->current.map.frame.axis[GMT_Y].file_custom)
2348 				ny = gmtlib_coordinate_array (GMT, s, n, &GMT->current.map.frame.axis[GMT_Y].item[item], &val, NULL);
2349 			else
2350 				ny = gmtlib_linear_array (GMT, s, n, dy, GMT->current.map.frame.axis[GMT_Y].phase, &val);
2351 		}
2352 		for (i = 0; i < ny; i++) {
2353 			if (gmtplot_skip_polar_apex_annotation (GMT, i, val, ny)) continue;
2354 			shift = gmtplot_shift_gridline (GMT, val[i], GMT_Y);
2355 			gmtplot_map_lattick (GMT, PSL, val[i] + shift, w, e, len);
2356 		}
2357 		if (ny) gmt_M_free (GMT, val);
2358 	}
2359 
2360 	GMT->current.map.on_border_is_outside = false;	/* Reset back to default */
2361 }
2362 
gmtplot_map_tickmarks(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)2363 GMT_LOCAL void gmtplot_map_tickmarks (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
2364 	/* Tickmarks at annotation interval has already been done except when annotations were not desired */
2365 
2366 	if (!(gmt_M_is_geographic (GMT, GMT_IN) || GMT->current.proj.projection_GMT == GMT_POLAR)) return;	/* Tickmarks already done by linear axis */
2367 
2368 	PSL_comment (PSL, "Map tickmarks\n");
2369 
2370 	gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
2371 	gmtplot_map_tickitem (GMT, PSL, w, e, s, n, GMT_ANNOT_UPPER);
2372 	if (!(GMT->current.setting.map_frame_type & GMT_IS_FANCY)) {	/* Draw plain boundary and return */
2373 		gmtplot_map_tickitem (GMT, PSL, w, e, s, n, GMT_TICK_UPPER);
2374 		gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_SECONDARY]);
2375 		gmtplot_map_tickitem (GMT, PSL, w, e, s, n, GMT_TICK_LOWER);
2376 	}
2377 
2378 	PSL_setdash (PSL, NULL, 0);
2379 }
2380 
gmtlib_set_do_seconds(struct GMT_CTRL * GMT,double inc)2381 bool gmtlib_set_do_seconds (struct GMT_CTRL *GMT, double inc) {
2382 	/* Determines if seconds are to be labelled based on size of increment */
2383 	if (GMT->current.plot.calclock.geo.order[2] == -1) return (false);			/* Seconds not requested by format */
2384 	if (GMT->current.plot.calclock.geo.n_sec_decimals > 0) return (true);			/* If asked for ss.xxx annotations */
2385 	if (fabs (60.0 * fmod (fmod (inc, 1.0) * 60.0, 1.0)) >= 1.0) return (true);	/* Multiples of >= 1 sec intervals */
2386 	return (false);
2387 }
2388 
gmtplot_label_trim(char * label,int stage)2389 GMT_LOCAL void gmtplot_label_trim (char *label, int stage) {
2390 	/* Used to shorten secondary annotations by eliminating the leading digits. E.g. if the
2391 	 * primary annotation is 30 degrees and the secondary is 30:05, 30:10, etc then we remove
2392 	 * the leading 1 (degrees) or 2 (degrees and minutes) part of the annotation.
2393 	 * This can only work if we are annotating with the ddd:mm:sss[.xx] format as for ddd.xxxxx
2394 	 * we cannot do so.  So if primary is 30:20 and secondary is 30:20:02, 30:20:04 etc we
2395 	 * end upremoving the leading 30:20 (stage = 2).
2396 	 */
2397 	size_t i;
2398 	if (!label) return;	/* No label given */
2399 	if (!stage) return;	/* Not asked to do anything */
2400 	/* Must remove leading stuff (e.g., ddd<degree_sign>) for 2ndary annotations */
2401 	for (i = 0; stage && label[i]; i++)
2402 		if (!strchr ("0123456789-+", label[i])) stage--;
2403 	while (label[i])
2404 		label[stage++] = label[i++];	/* Copy over the later part of the label to the beginning */
2405 	label[stage] = '\0';
2406 }
2407 
gmtplot_radial_annot_setup(struct GMT_CTRL * GMT,double angle,unsigned int * justify,double * text_angle,double * line_angle,double * dc,double * ds)2408 GMT_LOCAL void gmtplot_radial_annot_setup (struct GMT_CTRL *GMT, double angle, unsigned int *justify, double *text_angle, double *line_angle, double *dc, double *ds) {
2409 	if (angle < 0.0) angle += 360.0;
2410 	if (GMT->current.proj.got_azimuths)
2411 		angle = 90.0 - angle;
2412 	if (GMT->current.proj.projection_GMT == GMT_POLAR)	/* Shift by 90 so it works like N polar */
2413 		gmtlib_polar_prepare_label (GMT, angle-90-GMT->current.proj.p_base_angle, E_SIDE, line_angle, text_angle, justify);
2414 	else
2415 		gmtlib_polar_prepare_label (GMT, angle, E_SIDE, line_angle, text_angle, justify);
2416 
2417 	if (GMT->current.proj.projection_GMT != GMT_POLAR && !GMT->current.proj.north_pole) {
2418 		/* Adjust for the fact that for S polar maps, things are a bit backwards */
2419 		*text_angle = -*text_angle;
2420 		*justify = (*justify == PSL_ML) ? PSL_MR : PSL_ML;
2421 		*line_angle = 190.0 - *line_angle;
2422 	}
2423 	if (GMT->current.proj.projection_GMT != GMT_POLAR) {
2424 		*line_angle += 180.0;
2425 		*justify = (*justify == PSL_ML) ? PSL_BR : PSL_TL;
2426 	}
2427 	else
2428 		*justify = (*justify == PSL_ML) ? PSL_TL : PSL_BR;
2429 	*line_angle -= 45;
2430 	/* Shift annotation by the amount implied by GMT_ANNOT_OFFSET */
2431 	sincosd (*line_angle, ds, dc);
2432 	*ds *= MAX (GMT->current.setting.map_annot_offset[GMT_PRIMARY], 0.0);
2433 	*dc *= MAX (GMT->current.setting.map_annot_offset[GMT_PRIMARY], 0.0);
2434 
2435 	//fprintf (stderr, "Just = %d Angle = %g Line = %g dx = %g dy = %g\n", *justify, *text_angle, *line_angle, *dc, *ds);
2436 }
2437 
gmtplot_consider_internal_annotations(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)2438 GMT_LOCAL void gmtplot_consider_internal_annotations (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
2439 	/* Special annotation routine for annotating latitude (or radius) along a specified meridian.  This allows us to annotate
2440 	 * maps that do not have the axes we wish to annotate, such as annotating latitudes for a 0/360 azimuthal map. */
2441 	unsigned int i, nx = 0, ny = 0, first = 0, form, lonlat, justify;
2442 	bool do_minutes, do_seconds, do_grid = false;
2443 	char label[GMT_LEN256] = {""}, format[GMT_LEN64] = {""}, **label_c = NULL;
2444 	double *val = NULL, *tval = NULL, dx, dy, x0, y0, x1, y1, L, ds, dc, line_angle, text_angle, angle, dyg, dxg, xa, xb, ya, yb, Sa, Ca;
2445 
2446 	if (GMT->current.map.frame.internal_annot == 0) return;	/* Not requested */
2447 
2448 	form = gmt_setfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]);
2449 	PSL_comment (PSL, "Map annotations (Internal)\n");
2450 	PSL_settextmode (PSL, PSL_TXTMODE_MINUS);	/* Replace hyphens with minus signs */
2451 
2452 	if (GMT->current.map.frame.internal_annot == 1) {	/* Placement of latitude or radial annotations along selected meridian */
2453 		dy = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_UPPER]);
2454 		if (gmt_M_y_is_lat (GMT, GMT_IN)) {
2455 			do_minutes = (fabs (fmod (dy, 1.0)) > GMT_CONV4_LIMIT);
2456 			do_seconds = gmtlib_set_do_seconds (GMT, dy);
2457 			lonlat = 1;
2458 		}
2459 		else {	/* Also, we know that GMT->current.setting.format_geo_out = -1 in this case */
2460 			do_minutes = do_seconds = 0;
2461 			lonlat = 2;
2462 		}
2463 		if (GMT->current.plot.r_theta_annot) {	/* Make format for radial term */
2464 			char tmp[GMT_LEN64] = {""};
2465 			strcpy (format, GMT->current.setting.format_float_map);
2466 			gmt_get_format (GMT, dy, NULL, NULL, tmp);
2467 			strncpy (GMT->current.setting.format_float_map, tmp, GMT_LEN64-1);
2468 		}
2469 
2470 		if (GMT->current.proj.z_down) {	/* Want to annotate depth rather than radius */
2471 			if (GMT->current.map.frame.axis[GMT_Y].file_custom)
2472 				ny = gmtlib_coordinate_array (GMT, 0.0, GMT->current.proj.z_radius-s, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_UPPER], &tval, &label_c);
2473 			else if (GMT->current.proj.z_down == GMT_ZDOWN_Z) /* z = n - r */
2474 				ny = gmtlib_linear_array (GMT, 0.0, GMT->current.proj.z_radius-s, dy, GMT->current.map.frame.axis[GMT_Y].phase, &tval);
2475 			else if (GMT->current.proj.z_down == GMT_ZDOWN_ZP) /* z = n - r */
2476 				ny = gmtlib_linear_array (GMT, GMT->current.proj.z_radius-n, GMT->current.proj.z_radius-s, dy, GMT->current.map.frame.axis[GMT_Y].phase, &tval);
2477 			val = gmt_M_memory (GMT, NULL, ny, double);
2478 			for (i = 0; i < ny; i++) {
2479 				if (GMT->current.proj.z_down == GMT_ZDOWN_ZP)
2480 					val[i] = GMT->current.proj.z_radius - tval[i];	/* These are the z values needed for positioning */
2481 				else
2482 					val[i] = GMT->common.R.wesn[YHI] - tval[i];	/* These are the radial values needed for positioning */
2483 			}
2484 		}
2485 		else {				/* Annotate radius */
2486 			if (GMT->current.map.frame.axis[GMT_Y].file_custom)
2487 				ny = gmtlib_coordinate_array (GMT, s, n, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_UPPER], &val, &label_c);
2488 			else
2489 				ny = gmtlib_linear_array (GMT, s, n, dy, GMT->current.map.frame.axis[GMT_Y].phase, &val);
2490 			tval = val;	/* Here they are the same thing */
2491 		}
2492 
2493 		/* Lot of exceptions to check for that affects if first or last annotatino should be skipped */
2494 		if (GMT->current.proj.z_down == GMT_ZDOWN_Z && ny && tval[ny-1] < GMT->current.proj.z_radius)
2495 			ny--;
2496 		else if (GMT->current.proj.z_down == GMT_ZDOWN_ZP && ny) {
2497 			if ((tval[0] - GMT->current.proj.flip_radius + n) < 0.5*dy)
2498 			first = 1;
2499 		}
2500 		else if (GMT->current.proj.projection_GMT == GMT_POLAR && ny && GMT->current.proj.flip && GMT->current.proj.radial_offset > 0.0)
2501 			ny--;
2502 		else if (GMT->current.proj.projection_GMT == GMT_POLAR && ny && (GMT->current.proj.radial_offset > 0.0 || (!GMT->current.proj.flip && tval[0] > 0.0)))
2503 			first = 1;
2504 		else if (GMT->current.proj.got_elevations && n < 90.0)
2505 			ny--;
2506 		else if (GMT->current.proj.flip && ny && tval[ny-1] < GMT->current.proj.flip_radius)
2507 			ny--;
2508 
2509 		gmtplot_radial_annot_setup (GMT, GMT->current.map.frame.internal_arg, &justify, &text_angle, &line_angle, &dc, &ds);
2510 		/* Shall we place grid crosses or have user selected gridlines? */
2511 		if ((dyg = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[GMT_GRID_UPPER])) > 0.0) {
2512 			if ((dx = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_GRID_UPPER])) > 0.0) {
2513 				if (fabs (fmod (GMT->current.map.frame.internal_arg, dx)) > GMT_CONV4_LIMIT)
2514 					do_grid = true;
2515 				else if (dyg > dy)
2516 					do_grid = true;
2517 			}
2518 			else
2519 				do_grid = true;
2520 		}
2521 		else
2522 			do_grid = true;
2523 
2524 		if (do_grid) {
2525 			/* Either pick current grid-cross size or use half the annotation size as backup */
2526 			L = 0.5 * fabs ((GMT->current.setting.map_grid_cross_size[GMT_PRIMARY] > 0.0) ? GMT->current.setting.map_grid_cross_size[GMT_PRIMARY] : 0.5 * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH);
2527 			gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[GMT_PRIMARY]);
2528 		}
2529 
2530 		for (i = first; i < ny; i++) {
2531 			if (label_c && label_c[i] && label_c[i][0])
2532 				strncpy (label, label_c[i], GMT_LEN256-1);
2533 			else
2534 				gmtlib_get_annot_label (GMT, tval[i], label, do_minutes, do_seconds, 1, lonlat, GMT->current.map.is_world);
2535 			gmt_geo_to_xy (GMT, GMT->current.map.frame.internal_arg, val[i], &x0, &y0);
2536 			PSL_plottext (PSL, x0+dc, y0+ds, GMT->current.setting.font_annot[GMT_PRIMARY].size, label, text_angle, justify, form);
2537 			if (do_grid) {
2538 				gmt_geo_to_xy (GMT, GMT->current.map.frame.internal_arg, tval[i] - copysign (GMT->current.map.dlat, tval[i]), &x1, &y1);
2539 				angle = d_atan2 (y1-y0, x1-x0);
2540 				sincos (angle, &Sa, &Ca);
2541 				xa = x0 - L * Ca;	xb = x0 + L * Ca;
2542 				ya = y0 - L * Sa;	yb = y0 + L * Sa;
2543 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2544 				angle += M_PI_2;
2545 				sincos (angle, &Sa, &Ca);
2546 				xa = x0 - L * Ca;	xb = x0 + L * Ca;
2547 				ya = y0 - L * Sa;	yb = y0 + L * Sa;
2548 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2549 			}
2550 		}
2551 		if (ny) gmt_M_free (GMT, val);
2552 		if (label_c) {
2553 			for (i = 0; i < ny; i++) gmt_M_str_free (label_c[i]);
2554 			gmt_M_free (GMT, label_c);
2555 		}
2556 		if (GMT->current.proj.z_down) gmt_M_free (GMT, tval);
2557 		if (GMT->current.plot.r_theta_annot)	/* Restore the format */
2558 			strcpy (GMT->current.setting.format_float_map, format);
2559 	}
2560 
2561 	if (GMT->current.map.frame.internal_annot == 2) {	/* Placement of longitude annotations along selected parallel */
2562 		dx = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_ANNOT_UPPER]);
2563 		do_minutes = (fabs (fmod (dx, 1.0)) > GMT_CONV4_LIMIT);
2564 		do_seconds = gmtlib_set_do_seconds (GMT, dx);
2565 
2566 		if (GMT->current.map.frame.axis[GMT_X].file_custom)
2567 			nx = gmtlib_coordinate_array (GMT, w, e, &GMT->current.map.frame.axis[GMT_X].item[GMT_ANNOT_UPPER], &val, &label_c);
2568 		else
2569 			nx = gmtlib_linear_array (GMT, w, e, dx, GMT->current.map.frame.axis[GMT_X].phase, &val);
2570 		//if (nx && doubleAlmostEqualZero (val[0], w)) first = 1;
2571 		if (nx && doubleAlmostEqualZero (val[nx-1], e)) nx--;
2572 
2573 		if ((dyg = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_GRID_UPPER])) > 0.0) {
2574 			if ((dxg = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_GRID_UPPER])) > 0.0) {
2575 				if (fabs (fmod (GMT->current.map.frame.internal_arg, dxg)) > GMT_CONV4_LIMIT)
2576 					do_grid = true;
2577 				else if (dxg > dx)
2578 					do_grid = true;
2579 			}
2580 			else
2581 				do_grid = true;
2582 		}
2583 		else
2584 			do_grid = true;
2585 
2586 		if (do_grid) {
2587 			/* Either pick current grid-cross size or use half the annotation size as backup */
2588 			L = 0.5 * fabs ((GMT->current.setting.map_grid_cross_size[GMT_PRIMARY] > 0.0) ? GMT->current.setting.map_grid_cross_size[GMT_PRIMARY] : 0.5 * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH);
2589 			gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[GMT_PRIMARY]);
2590 		}
2591 
2592 		for (i = first; i < nx; i++) {
2593 			if (label_c && label_c[i] && label_c[i][0])
2594 				strncpy (label, label_c[i], GMT_LEN256-1);
2595 			else
2596 				gmtlib_get_annot_label (GMT, val[i], label, do_minutes, do_seconds, 1, 0, GMT->current.map.is_world);
2597 			gmt_geo_to_xy (GMT, val[i], GMT->current.map.frame.internal_arg, &x0, &y0);
2598 			gmt_geo_to_xy (GMT, val[i] + GMT->current.map.dlon, GMT->current.map.frame.internal_arg, &x1, &y1);
2599 			angle = d_atan2 (y1-y0, x1-x0);	/* Angle at longitude annotation */
2600 			sincos (angle+M_PI_4, &dc, &ds);	/* Add 45 to get distance to BL corner of boundingbox for annotation */
2601 			ds *= MAX (GMT->current.setting.map_annot_offset[GMT_PRIMARY], 0.0);
2602 			dc *= MAX (GMT->current.setting.map_annot_offset[GMT_PRIMARY], 0.0);
2603 			PSL_plottext (PSL, x0+dc, y0+ds, GMT->current.setting.font_annot[GMT_PRIMARY].size, label, R2D*angle, PSL_BL, form);
2604 			if (do_grid) {
2605 				sincos (angle, &Sa, &Ca);
2606 				xa = x0 - L * Ca;	xb = x0 + L * Ca;
2607 				ya = y0 - L * Sa;	yb = y0 + L * Sa;
2608 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2609 				angle += M_PI_2;
2610 				sincos (angle, &Sa, &Ca);
2611 				xa = x0 - L * Ca;	xb = x0 + L * Ca;
2612 				ya = y0 - L * Sa;	yb = y0 + L * Sa;
2613 				PSL_plotsegment (PSL, xa, ya, xb, yb);
2614 			}
2615 		}
2616 		if (nx) gmt_M_free (GMT, val);
2617 		if (label_c) {
2618 			for (i = 0; i < nx; i++) gmt_M_str_free (label_c[i]);
2619 			gmt_M_free (GMT, label_c);
2620 		}
2621 	}
2622 	if (val) gmt_M_free (GMT, val);
2623 	PSL_settextmode (PSL, PSL_TXTMODE_HYPHEN);	/* Back to leave as is */
2624 }
2625 
gmtplot_map_label(struct GMT_CTRL * GMT,double x,double y,char * label,double angle,int just,unsigned int axis,bool below)2626 GMT_LOCAL void gmtplot_map_label (struct GMT_CTRL *GMT, double x, double y, char *label, double angle, int just, unsigned int axis, bool below) {
2627 	/* Function to use to set axis labels for Cartesian basemaps and colorbars */
2628 	struct PSL_CTRL *PSL= GMT->PSL;
2629 	bool pos_set = (gmt_M_is_zero (x) && gmt_M_is_zero (y));	/* If the current point has already been placed */
2630 
2631 	if (gmt_text_is_latex (GMT, label)) {	/* Detected LaTeX commands, i.e., "....@[LaTeX...@[ ..." or  "....<math>LaTeX...</math> ..." */
2632 		bool set_L_off = (axis == GMT_X && !below);	/* May need to ensure extra offset for title */
2633 		double w, h;
2634 		unsigned char *eps = NULL;
2635 		struct imageinfo header;
2636 
2637 		if ((eps = gmtplot_latex_eps (GMT, &GMT->current.setting.font_label, label, &header)) == NULL) {
2638 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmtplot_map_label: Conversion of LaTeX label to EPS failed\n");
2639 			return;	/* Done */
2640 		}
2641 		/* Scale up EPS dimensions by the ratio of label font size to LaTeX default size of 10p */
2642 		w = (header.width / 72.0)  * (GMT->current.setting.font_label.size / 10.0);
2643 		h = (header.height / 72.0) * (GMT->current.setting.font_label.size / 10.0);
2644 		/* Place EPS file as label, then free eps */
2645 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtplot_map_label: Conversion of LaTeX label gave dimensions %g x %g\n", w, h);
2646 		PSL_command (PSL, "V\n");	/* Keep the relative changes inside a save/restore block */
2647 		/* If we plot label below the axis then we must adjust for the fact that the base y-coordinate is based on label font height of "M",
2648 		 * but now we have an EPS image of given (and presumably larger) height.  So we adjust by the difference in those two values */
2649 		if (pos_set) {	/* Translate origin to currentpoint since already set by calling function, and possibly rotate by +/- 90 degrees */
2650 			double sgn[2] = {-1.0, 1.0};
2651 			if (fabs (angle) > 0.0) PSL_command (PSL, "currentpoint T %g R\n", angle); else PSL_command (PSL, "currentpoint T\n");
2652 			if (below) PSL_command (PSL, "0 %d PSL_LH sub neg M currentpoint T\n", (int)lrint (h * PSL->internal.y2iy));
2653 			/* I am somehow missing the label offset for y-axes so I need to account for it here in order to get correct result */
2654 			if (axis == GMT_Y) PSL_command (PSL, "0 %d M currentpoint T\n", (int)lrint (sgn[below]*GMT->current.setting.map_label_offset[GMT_Y] * PSL->internal.y2iy));
2655 		}
2656 		// PSL_command (PSL, "V currentpoint -2000 0 G 4000 0 D S U\n");	/* Debug line for base of label; keep for future debugging */
2657 		PSL_plotlatexeps (PSL, x, y, w, h, PSL_BC, eps, GMT->current.setting.font_label.fill.rgb, &header);	/* Place the EPS plot */
2658 		PSL_command (PSL, "U\n");	/* Close up the block */
2659 		if (set_L_off)	/* Must reset the value of PSL_LH to the EPS image height so the title baseline can be adjusted */
2660 			PSL_command (PSL, "/PSL_LH %d def\n", (int)lrint (h * PSL->internal.y2iy));
2661 		PSL_free (eps);
2662 		return;	/* Done on this end */
2663 	}
2664 	else {	/* Regular text label */
2665 		int form = gmt_setfont (GMT, &GMT->current.setting.font_label);
2666 		double s = (pos_set) ? -1.0 : +1.0;
2667 		PSL_plottext (PSL, x, y, s * GMT->current.setting.font_label.size, label, angle, just, form);
2668 	}
2669 }
2670 
gmtplot_skip_pole_lat_annotation(struct GMT_CTRL * GMT,double lat)2671 bool gmtplot_skip_pole_lat_annotation (struct GMT_CTRL *GMT, double lat) {
2672 	/* Here, latitude is -90 or +90 and the question is do we annotation that latitude? */
2673 	if (GMT->current.proj.projection_GMT == GMT_ECKERT4   && fabs (lat) > 85.0) return true;	/* Slope too gentle near poles to adjust via boost */
2674 	if (GMT->current.proj.projection_GMT == GMT_HAMMER    && fabs (lat) > 85.0) return true;	/* Slope too gentle near poles to adjust via boost */
2675 	if (GMT->current.proj.projection_GMT == GMT_MOLLWEIDE && fabs (lat) > 88.0) return true;	/* Slope too gentle near poles to adjust via boost */
2676 	if (fabs (lat) < 90.0) return false;	/* Not at the poles so OK to annotate */
2677 	if (GMT->current.proj.polar) return true;	/* Cannot since inside map */
2678 	return false;
2679 }
2680 
gmtplot_revise_angles_just(struct GMT_CTRL * GMT,double line_angle,unsigned side,unsigned int def_just[],double * t_angle,unsigned int * just)2681 static char *gmtplot_revise_angles_just (struct GMT_CTRL *GMT, double line_angle, unsigned side, unsigned int def_just[], double *t_angle, unsigned int *just) {
2682 	/* Given the side (W or E) and angle of the boundary, determine the justification of the text and possibly flip it 180 */
2683 	static char *flip[2] = {"", "neg "};
2684 	unsigned int k = GMT->current.proj.got_azimuths ? 1 : 0, pside;
2685 	if (side == W_SIDE) side = 0; else side = 1;	/* Turn W_SIDE to 0 and E_SIDE to 1 for use with flip and def_just arrays */
2686 	pside = GMT->current.proj.got_azimuths ? 1-side : side;	/* Since azimuths go the other way */
2687 	if ((line_angle < -90.00 || line_angle > 90.0)) {	/* Must flip the text angle 180 degrees and also flip the justification */
2688 		*t_angle = 180.0;
2689 		*just = def_just[pside];
2690 	}
2691 	else {	/* Normally the annotation is just plotted along the radial which is the x-axis after the rotation */
2692 		*t_angle = 0.0;
2693 		*just = def_just[1-pside];
2694 	}
2695 	if (side == 1) k = (k + 1) % 2;	/* We want positive direction to be outwards */
2696 	// fprintf (stderr, "Side %c Input: line-angle = %g, just = %d  Output: t-angle = %g, just = %d neg = %s\n", name[side], line_angle, def_just[0], *t_angle, *just, flip[k]);
2697 	return flip[k];
2698 }
2699 
gmtplot_map_annotate(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n)2700 GMT_LOCAL void gmtplot_map_annotate (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n) {
2701 	unsigned int i, k, nx = 0, ny = 0, last, form, remove[2] = {0,0}, trim, add, x_kind = 0;
2702 	bool do_minutes, do_seconds, done_Greenwich, done_Dateline, check_edges;
2703 	bool full_lat_range, proj_A, proj_B, annot_0_and_360 = false, dual[2], is_dual, annot, is_world_save, lon_wrap_save;
2704 	char label[GMT_LEN256] = {""};
2705 	char **label_c = NULL;
2706 	double *val = NULL, dx[2], dy[2], w2, s2, del, shift = 0.0;
2707 
2708 	if (!(gmt_M_x_is_lon (GMT, GMT_IN) || gmt_M_y_is_lat (GMT, GMT_IN) || GMT->current.proj.projection_GMT == GMT_POLAR)) return;	/* Annotations and header already done by gmtplot_linear_map_boundary */
2709 
2710 	is_world_save = GMT->current.map.is_world;
2711 	lon_wrap_save = GMT->current.map.lon_wrap;
2712 
2713 	if (GMT->current.proj.projection_GMT == GMT_POLAR) {	/* r-theta requires more info to know what theta is */
2714 		if (GMT->current.proj.angle_kind == GMT_IS_LON) x_kind = 0;
2715 		else if (GMT->current.proj.angle_kind == GMT_IS_LAT) x_kind = 3;
2716 		else x_kind = 4;
2717 	}
2718 
2719 	if (GMT->current.map.frame.header[0] && !GMT->current.map.frame.plotted_header) {	/* Make plot header for geographic maps */
2720 		if (gmt_M_is_geographic (GMT, GMT_IN) || GMT->current.map.frame.side[N_SIDE] & GMT_AXIS_ANNOT) {
2721 			PSL_setfont (PSL, GMT->current.setting.font_annot[GMT_PRIMARY].id);
2722 			PSL_command (PSL, "/PSL_H_y %d ", PSL_IZ (PSL, GMT->current.setting.map_tick_length[GMT_PRIMARY] + GMT->current.setting.map_annot_offset[GMT_PRIMARY] + GMT->current.setting.map_title_offset));
2723 			if (GMT->current.setting.map_degree_symbol == gmt_none)
2724 				PSL_deftextdim (PSL, "-h", GMT->current.setting.font_annot[GMT_PRIMARY].size, "100");
2725 			else {
2726 				snprintf (label, GMT_LEN16, "100%c",  (int)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol]);
2727 				PSL_deftextdim (PSL, "-h", GMT->current.setting.font_annot[GMT_PRIMARY].size, label);
2728 			}
2729 			PSL_command (PSL, "add def\n");
2730 		}
2731 		else
2732 			PSL_defunits (PSL, "PSL_H_y", GMT->current.setting.map_title_offset + GMT->current.setting.map_tick_length[GMT_PRIMARY]);
2733 
2734 		PSL_command (PSL, "%d %d PSL_H_y add M\n", PSL_IZ (PSL, GMT->current.proj.rect[XHI] * 0.5), PSL_IZ (PSL, GMT->current.proj.rect[YHI]));
2735 		gmt_map_title (GMT, 0.0, 0.0);
2736 	}
2737 
2738 	gmtplot_consider_internal_annotations (GMT, PSL, w, e, s, n);	/* Handle any special case of internal annotations */
2739 
2740 	if (GMT->current.proj.edge[S_SIDE] || GMT->current.proj.edge[N_SIDE]) {
2741 		dx[0] = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_ANNOT_UPPER]);
2742 		dx[1] = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_X].type, &GMT->current.map.frame.axis[GMT_X].item[GMT_ANNOT_LOWER]);
2743 		/* Determine if we should annotate both 0 and 360 degrees */
2744 
2745 		full_lat_range = (fabs (180.0 - fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO])) < GMT_CONV4_LIMIT);
2746 		proj_A = (GMT->current.proj.projection_GMT == GMT_MERCATOR || GMT->current.proj.projection_GMT == GMT_OBLIQUE_MERC ||
2747 			GMT->current.proj.projection_GMT == GMT_WINKEL || GMT->current.proj.projection_GMT == GMT_ECKERT4 || GMT->current.proj.projection_GMT == GMT_ECKERT6 ||
2748 			GMT->current.proj.projection_GMT == GMT_ROBINSON || GMT->current.proj.projection_GMT == GMT_CYL_EQ || GMT->current.proj.projection_GMT == GMT_CYL_STEREO ||
2749 			GMT->current.proj.projection_GMT == GMT_CYL_EQDIST || GMT->current.proj.projection_GMT == GMT_MILLER || GMT->current.proj.projection_GMT == GMT_LINEAR);
2750 		proj_B = (GMT->current.proj.projection_GMT == GMT_HAMMER || GMT->current.proj.projection_GMT == GMT_MOLLWEIDE ||
2751 			GMT->current.proj.projection_GMT == GMT_SINUSOIDAL);
2752 		if (gmt_M_is_conical (GMT) && gmt_M_360_range (w, e))	/* Special case since 360 longitudes do not form a circle but a pacman shape */
2753 			annot_0_and_360 = true;
2754 		else
2755 			annot_0_and_360 = (is_world_save && (proj_A || (!full_lat_range && proj_B)));
2756 	}
2757 	else
2758 		dx[0] = dx[1] = 0.0;
2759 	if (GMT->current.proj.edge[E_SIDE] || GMT->current.proj.edge[W_SIDE]) {
2760 		dy[0] = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_UPPER]);
2761 		dy[1] = gmtlib_get_map_interval (GMT, GMT->current.map.frame.axis[GMT_Y].type, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_LOWER]);
2762 	}
2763 	else
2764 		dy[0] = dy[1] = 0.0;
2765 
2766 	if (GMT->current.map.frame.axis[GMT_X].file_custom) dx[0] = 1.0;	/* To pass checks below */
2767 	if (GMT->current.map.frame.axis[GMT_Y].file_custom) dy[0] = 1.0;	/* To pass checks below */
2768 
2769 	if (dx[0] <= 0.0 && dy[0] <= 0.0) return;
2770 
2771 	dual[GMT_X] = (dx[1] > 0.0);
2772 	dual[GMT_Y] = (dy[1] > 0.0);
2773 	is_dual = (dual[GMT_X] | dual[GMT_Y]);
2774 	check_edges = (!GMT->common.R.oblique && (GMT->current.setting.map_frame_type & GMT_IS_INSIDE));
2775 
2776 	PSL_comment (PSL, "Map annotations (external)\n");
2777 	PSL_settextmode (PSL, PSL_TXTMODE_MINUS);	/* Replace hyphens with minus signs */
2778 
2779 	form = gmt_setfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]);
2780 
2781 	GMT->current.map.on_border_is_outside = true;	/* Temporarily, points on the border are outside */
2782 	if (!GMT->common.R.oblique) {
2783 		GMT->current.map.is_world = false;
2784 		if (!(GMT->current.proj.projection_GMT == GMT_GENPER || GMT->current.proj.projection_GMT == GMT_GNOMONIC)) GMT->current.map.lon_wrap = false;
2785 	}
2786 
2787 	w2 = (dx[1] > 0.0) ? floor (w / dx[1]) * dx[1] : 0.0;
2788 	s2 = (dy[1] > 0.0) ? floor (s / dy[1]) * dy[1] : 0.0;
2789 
2790 	if (dual[GMT_X]) remove[GMT_X] = (dx[0] < (1.0/60.0)) ? 2 : 1;
2791 	if (dual[GMT_Y]) remove[GMT_Y] = (dy[0] < (1.0/60.0)) ? 2 : 1;
2792 	add = (is_dual) ? 1 : 0;
2793 	for (k = 0; k < 1 + add; k++) {
2794 		if (dx[k] > 0.0 && (gmt_M_x_is_lon (GMT, GMT_IN) || GMT->current.proj.projection_GMT == GMT_POLAR)) {	/* Annotate the S and N boundaries */
2795 			done_Greenwich = done_Dateline = false;
2796 			do_minutes = (fabs (fmod (dx[k], 1.0)) > GMT_CONV4_LIMIT);
2797 			do_seconds = gmtlib_set_do_seconds (GMT, dx[k]);
2798 
2799 			if (GMT->current.map.frame.axis[GMT_X].file_custom)
2800 				nx = gmtlib_coordinate_array (GMT, w, e, &GMT->current.map.frame.axis[GMT_X].item[GMT_ANNOT_UPPER], &val, &label_c);
2801 			else
2802 				nx = gmtlib_linear_array (GMT, w, e, dx[k], GMT->current.map.frame.axis[GMT_X].phase, &val);
2803 			last = nx - 1;
2804 			for (i = 0; i < nx; i++) {	/* Worry that we do not try to plot 0 and 360 OR -180 and +180 on top of each other */
2805 				if (check_edges && ((i == 0 && val[i] == w) || (i == last && val[i] == e)))
2806 					continue;	/* To avoid/limit clipping of annotations */
2807 				if (gmt_M_is_zero (val[i]))
2808 					done_Greenwich = true;		/* OK, want to plot 0 */
2809 				if (doubleAlmostEqual (val[i], -180.0))
2810 					done_Dateline = true;	/* OK, want to plot -180 */
2811 				/* Only annotate val[i] if
2812 				 *	(1) projection is such that 0/360 or -180/180 are in different x/y locations, OR
2813 				 *	(2) Plot 360 if 0 hasn't been plotted, OR
2814 				 *	(3) plot +180 if -180 hasn't been plotted
2815 				 */
2816 				annot = annot_0_and_360 || !((done_Greenwich && doubleAlmostEqual (val[i], 360.0)) || (done_Dateline && doubleAlmostEqual (val[i], 180.0)));
2817 				trim = 0;
2818 				if (dual[GMT_X] && k == 0) {
2819 					del = fmod (val[i] - w2, dx[1]);
2820 					if (gmt_M_is_zero (del) || doubleAlmostEqual (del, dx[1]))
2821 						annot = false;
2822 					else
2823 						trim = remove[GMT_X];
2824 				}
2825 				if (label_c && label_c[i] && label_c[i][0])
2826 					strncpy (label, label_c[i], GMT_LEN256-1);
2827 				else
2828 					gmtlib_get_annot_label (GMT, val[i], label, do_minutes, do_seconds, !trim, x_kind, is_world_save);
2829 				gmtplot_label_trim (label, trim);
2830 				shift = gmtplot_shift_gridline (GMT, val[i], GMT_X);
2831 				gmtplot_map_symbol_ns (GMT, PSL, val[i]+shift, label, s, n, annot, k, form);
2832 			}
2833 			if (nx) gmt_M_free (GMT, val);
2834 			if (label_c) {
2835 				for (i = 0; i < nx; i++) gmt_M_str_free (label_c[i]);
2836 				gmt_M_free (GMT, label_c);
2837 			}
2838 		}
2839 
2840 		if (dy[k] > 0.0 && (gmt_M_y_is_lat (GMT, GMT_IN) || GMT->current.proj.projection_GMT == GMT_POLAR)) {	/* Annotate W and E boundaries */
2841 			unsigned int lonlat;
2842 			double *tval = NULL;
2843 			char format[GMT_LEN64] = {""};
2844 
2845 			if (gmt_M_y_is_lat (GMT, GMT_IN)) {
2846 				do_minutes = (fabs (fmod (dy[k], 1.0)) > GMT_CONV4_LIMIT);
2847 				do_seconds = gmtlib_set_do_seconds (GMT, dy[k]);
2848 				lonlat = 1;
2849 			}
2850 			else {	/* Also, we know that GMT->current.setting.format_geo_out = -1 in this case */
2851 				do_minutes = do_seconds = 0;
2852 				lonlat = 2;
2853 			}
2854 			if (GMT->current.plot.r_theta_annot) {	/* Make format for radial term */
2855 				char tmp[GMT_LEN64] = {""};
2856 				strcpy (format, GMT->current.setting.format_float_map);
2857 				gmt_get_format (GMT, dy[k], NULL, NULL, tmp);
2858 				strncpy (GMT->current.setting.format_float_map, tmp, GMT_LEN64-1);
2859 			}
2860 
2861 			if (GMT->current.proj.z_down) {	/* Want to annotate depth rather than radius */
2862 				if (GMT->current.map.frame.axis[GMT_Y].file_custom)
2863 					ny = gmtlib_coordinate_array (GMT, 0.0, GMT->current.proj.z_radius-s, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_UPPER], &tval, &label_c);
2864 				else if (GMT->current.proj.z_down == GMT_ZDOWN_Z) /* z = n - r */
2865 					ny = gmtlib_linear_array (GMT, 0.0, GMT->current.proj.z_radius-s, dy[k], GMT->current.map.frame.axis[GMT_Y].phase, &tval);
2866 				else if (GMT->current.proj.z_down == GMT_ZDOWN_ZP) /* z = n - r */
2867 					ny = gmtlib_linear_array (GMT, GMT->current.proj.z_radius-n, GMT->current.proj.z_radius-s, dy[k], GMT->current.map.frame.axis[GMT_Y].phase, &tval);
2868 				val = gmt_M_memory (GMT, NULL, ny, double);
2869 				for (i = 0; i < ny; i++) {
2870 					if (GMT->current.proj.z_down == GMT_ZDOWN_ZP)
2871 						val[i] = GMT->current.proj.z_radius - tval[i];	/* These are the z values needed for positioning */
2872 					else
2873 						val[i] = GMT->common.R.wesn[YHI] - tval[i];	/* These are the radial values needed for positioning */
2874 				}
2875 			}
2876 			else {				/* Annotate radius */
2877 				if (GMT->current.map.frame.axis[GMT_Y].file_custom)
2878 					ny = gmtlib_coordinate_array (GMT, s, n, &GMT->current.map.frame.axis[GMT_Y].item[GMT_ANNOT_UPPER], &val, &label_c);
2879 				else
2880 					ny = gmtlib_linear_array (GMT, s, n, dy[k], GMT->current.map.frame.axis[GMT_Y].phase, &val);
2881 				tval = val;	/* Here they are the same thing */
2882 			}
2883 			PSL_command (PSL, "/PSL_AH1 0\n");	/* Initiate annotation width  */
2884 			last = ny - 1;
2885 			for (i = 0; i < ny; i++) {
2886 				if (gmtplot_skip_pole_lat_annotation (GMT, val[i]))
2887 					continue;	/* Cannot place S or N 90 degree annotation for this projection */
2888 				if (gmtplot_skip_polar_apex_annotation (GMT, i, val, ny)) continue;
2889 				annot = true, trim = 0;
2890 				if (check_edges && ((i == 0 && val[i] == s) || (i == last && val[i] == n)))
2891 					continue;	/* To avoid/limit clipping of annotations */
2892 
2893 				if (dual[GMT_Y] && k == 0) {
2894 					del = fmod (val[i] - s2, dy[1]);
2895 					if (gmt_M_is_zero (del) || doubleAlmostEqual (del, dy[1]))
2896 						annot = false;
2897 					else
2898 						trim = remove[GMT_Y];
2899 				}
2900 				if (label_c && label_c[i] && label_c[i][0])
2901 					strncpy (label, label_c[i], GMT_LEN256-1);
2902 				else
2903 					gmtlib_get_annot_label (GMT, tval[i], label, do_minutes, do_seconds, !trim, lonlat, is_world_save);
2904 				gmtplot_label_trim (label, trim);
2905 				shift = gmtplot_shift_gridline (GMT, val[i], GMT_Y);
2906 				gmtplot_map_symbol_ew (GMT, PSL, val[i]+shift, label, w, e, annot, k, form);
2907 				PSL_deftextdim (PSL, "-w", GMT->current.setting.font_annot[GMT_PRIMARY].size, label);
2908 				PSL_command (PSL, "mx\n");		/* Update the longest annotation stored in PSL_AH1 */
2909 			}
2910 			PSL_command (PSL, "def\n");	/* Finalize the definition of longest annotation found */
2911 			if (ny) gmt_M_free (GMT, val);
2912 			if (label_c) {
2913 				for (i = 0; i < ny; i++) gmt_M_str_free (label_c[i]);
2914 				gmt_M_free (GMT, label_c);
2915 			}
2916 			if (GMT->current.proj.z_down) gmt_M_free (GMT, tval);
2917 			if (GMT->current.plot.r_theta_annot)	/* Restore the format */
2918 				strcpy (GMT->current.setting.format_float_map, format);
2919 		}
2920 	}
2921 
2922 	if (GMT->current.proj.projection_GMT == GMT_POLAR && GMT->current.map.frame.axis[GMT_Y].label[0] && !gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) {
2923 		/* May need to label radial axis */
2924 		struct GMT_PLOT_AXIS *A = &GMT->current.map.frame.axis[GMT_Y];
2925 		double x0, x1, y0, y1, xm, ym, line_angle, t_angle;
2926 		double off = GMT->current.setting.map_label_offset[GMT_Y] + MAX (0.0, GMT->current.setting.map_annot_offset[GMT_PRIMARY]) + MAX (0.0, GMT->current.setting.map_tick_length[GMT_PRIMARY]);
2927 		unsigned int just, we_side[2] = {W_SIDE, E_SIDE}, def_just[2] = {PSL_TC, PSL_BC};
2928 		char *neg;
2929 		if (GMT->current.map.frame.side[we_side[0]] & GMT_AXIS_ANNOT) {
2930 			/* Compute x, y, angles etc */
2931 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], &x0, &y0);
2932 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &x1, &y1);
2933 			xm = 0.5 * (x0 + x1);	ym = 0.5 * (y0 + y1);	line_angle = d_atan2d (y1 - y0, x1 - x0);
2934 			neg = gmtplot_revise_angles_just (GMT, line_angle, we_side[0], def_just, &t_angle, &just);
2935 			/* Center origin on mid-axis and rotate plot x-axis to coincide with line_angle direction */
2936 			PSL_setorigin (PSL, xm, ym, line_angle, PSL_FWD);
2937 			PSL_setcurrentpoint (PSL, 0.0, 0.0);	/* Make the center point of axis the current point */
2938 			PSL_command (PSL, "0 PSL_AH1 %d add %sG\n", PSL_IZ (PSL, off), neg);	/* Move outwards in the x-direction by the annotation width and offset */
2939 			gmtplot_map_label (GMT, 0.0, 0.0, A->label, t_angle, just, GMT_Y, true);	/* Place the label */
2940 			PSL_setorigin (PSL, -xm, -ym, -line_angle, PSL_INV);	/* Undo the coordinate system change */
2941 		}
2942 		if (GMT->current.map.frame.side[we_side[1]] & GMT_AXIS_ANNOT) {
2943 			/* Compute x, y, angles etc */
2944 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &x0, &y0);
2945 			gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &x1, &y1);
2946 			xm = 0.5 * (x0 + x1);	ym = 0.5 * (y0 + y1);	line_angle = d_atan2d (y1 - y0, x1 - x0);
2947 			neg = gmtplot_revise_angles_just (GMT, line_angle, we_side[1], def_just,  &t_angle, &just);
2948 			/* Center origin on mid-axis and rotate plot x-axis to coincide with line_angle direction */
2949 			PSL_setorigin (PSL, xm, ym, line_angle, PSL_FWD);
2950 			PSL_setcurrentpoint (PSL, 0.0, 0.0);	/* Make the center point of axis the current point */
2951 			PSL_command (PSL, "0 PSL_AH1 %d add %sG\n", PSL_IZ (PSL, off), neg);	/* Move outwards in the x-direction by the annotation width and offset */
2952 			gmtplot_map_label (GMT, 0.0, 0.0, A->label, t_angle, just, GMT_Y, false);
2953 			PSL_setorigin (PSL, -xm, -ym, -line_angle, PSL_INV);	/* Undo the coordinate system change */
2954 		}
2955 	}
2956 
2957 	GMT->current.map.on_border_is_outside = false;	/* Reset back to default */
2958 	GMT->current.map.is_world = is_world_save;
2959 	GMT->current.map.lon_wrap = lon_wrap_save;
2960 	PSL_settextmode (PSL, PSL_TXTMODE_HYPHEN);	/* Back to leave as is */
2961 }
2962 
gmtplot_map_boundary(struct GMT_CTRL * GMT)2963 GMT_LOCAL void gmtplot_map_boundary (struct GMT_CTRL *GMT) {
2964 	double w, e, s, n;
2965 	struct PSL_CTRL *PSL= GMT->PSL;
2966 
2967 	if (!GMT->current.map.frame.draw && GMT->current.proj.projection_GMT != GMT_LINEAR) return;	/* We have a separate check in linear_map_boundary */
2968 	if (GMT->current.map.frame.no_frame) return;	/* Specifically did not want frame */
2969 
2970 	if (GMT->current.map.frame.order == GMT_BASEMAP_BEFORE && GMT->current.map.frame.basemap_flag & GMT_BASEMAP_FRAME_AFTER) return;	/* Wrong order */
2971 	if (GMT->current.map.frame.order == GMT_BASEMAP_AFTER  && !(GMT->current.map.frame.basemap_flag & GMT_BASEMAP_FRAME_AFTER)) return;	/* Wrong order */
2972 
2973 	w = GMT->common.R.wesn[XLO], e = GMT->common.R.wesn[XHI], s = GMT->common.R.wesn[YLO], n = GMT->common.R.wesn[YHI];
2974 
2975 	PSL_comment (PSL, "Start of map frame\n");
2976 
2977 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
2978 	PSL_setcolor (PSL, GMT->current.setting.map_frame_pen.rgb, PSL_IS_STROKE);
2979 
2980 	switch (GMT->current.proj.projection_GMT) {
2981 		case GMT_LINEAR:
2982 			if (gmt_M_is_geographic (GMT, GMT_IN))	/* xy is lonlat */
2983 				gmtplot_fancy_map_boundary (GMT, PSL, w, e, s, n);
2984 			else
2985 				gmtplot_linear_map_boundary (GMT, PSL, w, e, s, n);
2986 			break;
2987 		case GMT_POLAR:
2988 			gmtplot_theta_r_map_boundary (GMT, PSL, w, e, s, n);
2989 			break;
2990 		case GMT_MERCATOR:
2991 		case GMT_CYL_EQ:
2992 		case GMT_CYL_EQDIST:
2993 		case GMT_CYL_STEREO:
2994 		case GMT_MILLER:
2995 			gmtplot_fancy_map_boundary (GMT, PSL, w, e, s, n);
2996 			break;
2997 		case GMT_ALBERS:
2998 		case GMT_ECONIC:
2999 		case GMT_LAMBERT:
3000 		case GMT_POLYCONIC:
3001 			gmtplot_conic_map_boundary (GMT, PSL, w, e, s, n);
3002 			break;
3003 		case GMT_OBLIQUE_MERC:
3004 			gmtplot_oblmrc_map_boundary (GMT, PSL, w, e, s, n);
3005 			break;
3006 		case GMT_GENPER:
3007 			gmtplot_genper_map_boundary (GMT, PSL, w, e, s, n);
3008 			break;
3009 		case GMT_STEREO:
3010 		case GMT_ORTHO:
3011 		case GMT_LAMB_AZ_EQ:
3012 		case GMT_AZ_EQDIST:
3013 		case GMT_GNOMONIC:
3014 			if (GMT->current.proj.polar)
3015 				gmtplot_polar_map_boundary (GMT, PSL, w, e, s, n);
3016 			else
3017 				gmtplot_circle_map_boundary (GMT, PSL, w, e, s, n);
3018 			break;
3019 		case GMT_HAMMER:
3020 		case GMT_MOLLWEIDE:
3021 		case GMT_SINUSOIDAL:
3022 			gmtplot_ellipse_map_boundary (GMT, PSL, w, e, s, n);
3023 			break;
3024 		case GMT_TM:
3025 		case GMT_UTM:
3026 		case GMT_CASSINI:
3027 		case GMT_WINKEL:
3028 		case GMT_ECKERT4:
3029 		case GMT_ECKERT6:
3030 		case GMT_ROBINSON:
3031 		case GMT_VANGRINTEN:
3032 			gmtplot_basic_map_boundary (GMT, PSL, w, e, s, n);
3033 			break;
3034 	}
3035 	PSL_comment (PSL, "End of map frame\n");
3036 }
3037 
3038 /* gmt_map_basemap will create a basemap for the given area.
3039  * Scaling and wesn are assumed to be passed through the GMT->current.proj-structure (see GMT_project.h)
3040  * Tickmark info are passed through the GMT->current.map.frame-structure
3041  */
3042 
gmtplot_is_fancy_boundary(struct GMT_CTRL * GMT)3043 GMT_LOCAL bool gmtplot_is_fancy_boundary (struct GMT_CTRL *GMT) {
3044 	switch (GMT->current.proj.projection_GMT) {
3045 		case GMT_LINEAR:
3046 			return (gmt_M_is_geographic (GMT, GMT_IN));
3047 			break;
3048 		case GMT_MERCATOR:
3049 		case GMT_CYL_EQ:
3050 		case GMT_CYL_EQDIST:
3051 		case GMT_CYL_STEREO:
3052 		case GMT_MILLER:
3053 			return (true);
3054 			break;
3055 		case GMT_ALBERS:
3056 		case GMT_ECONIC:
3057 		case GMT_LAMBERT:
3058 			return (!GMT->common.R.oblique);
3059 			break;
3060 		case GMT_STEREO:
3061 		case GMT_ORTHO:
3062 		case GMT_GENPER:
3063 		case GMT_LAMB_AZ_EQ:
3064 		case GMT_AZ_EQDIST:
3065 		case GMT_GNOMONIC:
3066 		case GMT_VANGRINTEN:
3067 			return (GMT->current.proj.polar && !GMT->common.R.oblique);
3068 			break;
3069 		case GMT_POLAR:
3070 		case GMT_OBLIQUE_MERC:
3071 		case GMT_HAMMER:
3072 		case GMT_MOLLWEIDE:
3073 		case GMT_SINUSOIDAL:
3074 		case GMT_TM:
3075 		case GMT_UTM:
3076 		case GMT_CASSINI:
3077 		case GMT_WINKEL:
3078 		case GMT_ECKERT4:
3079 		case GMT_ECKERT6:
3080 		case GMT_ROBINSON:
3081 			return (false);
3082 			break;
3083 		default:
3084 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in gmtplot_is_fancy_boundary - notify developers\n");
3085 			return (false);
3086 	}
3087 }
3088 
gmtplot_vertical_wall(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,int quadrant,double * nesw,bool back,unsigned int mode3d)3089 GMT_LOCAL void gmtplot_vertical_wall (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, int quadrant, double *nesw, bool back, unsigned int mode3d) {
3090 	/* Draws the two vertical walls in the back of the 3-D plot */
3091 	int plane = (quadrant + 1) % 2;	/* Black magic that gives us GMT_X (for yz plane) or GMT_Y (for xz plane) */
3092 	double xx[4], yy[4];
3093 	gmt_plane_perspective (GMT, plane, nesw[quadrant % 4]);
3094 	/* Assign the coordinates of the four corners of the wall (hence assumed to be rectangular) */
3095 	xx[0] = xx[1] = nesw[(quadrant+1)%4];	xx[2] = xx[3] = nesw[(quadrant+3)%4];
3096 	yy[0] = yy[3] = GMT->current.proj.zmin;	yy[1] = yy[2] = GMT->current.proj.zmax;
3097 	if (mode3d & GMT_3D_WALL && back) {	/* Called for one of the back walls and we wish to draw it */
3098 		int wplane = 1 - plane;	/* Get integer for the correct plain side */
3099 		if (GMT->current.map.frame.paint[wplane]) {	/* First paint the back wall, with no outline drawn */
3100 			PSL_setfill (PSL, GMT->current.map.frame.fill[wplane].rgb, 0);
3101 			PSL_plotbox (PSL, nesw[(quadrant+1)%4], GMT->current.proj.zmin, nesw[(quadrant+3)%4], GMT->current.proj.zmax);
3102 		}
3103 		if (back)	/* Gridlines was requested, so draw those now */
3104 			gmtplot_z_gridlines (GMT, PSL, GMT->common.R.wesn[ZLO], GMT->common.R.wesn[ZHI], plane, mode3d, quadrant);
3105 
3106 		if (GMT->current.map.frame.draw_wall) {	/* We wanted the outline of the back-wall drawn, do now to overwrite any perimeter gridlines */
3107 			gmt_setpen (GMT, &GMT->current.map.frame.pen);
3108 			PSL_plotline (PSL, xx, yy, 4, PSL_MOVE|PSL_STROKE);
3109 		}
3110 	}
3111 	else if (back)	/* Get here if no back-walls were filled our outlined */
3112 		gmtplot_z_gridlines (GMT, PSL, GMT->common.R.wesn[ZLO], GMT->common.R.wesn[ZHI], plane, mode3d, quadrant);
3113 }
3114 
gmtplot_cube_box(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,int quadrant,double * nesw)3115 GMT_LOCAL void gmtplot_cube_box (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, int quadrant, double *nesw) {
3116 	/* Draws the 3-D cube lines */
3117 	int plane = (quadrant + 1) % 2;	/* Black magic that gives us GMT_X (for yz plane) or GMT_Y (for xz plane) */
3118 	double xx[4], yy[4];
3119 	gmt_plane_perspective (GMT, plane, nesw[quadrant % 4]);
3120 	/* Assign the coordinates of the four corners of the wall (hence assumed to be rectangular) */
3121 	xx[0] = xx[1] = nesw[(quadrant+1)%4];	xx[2] = xx[3] = nesw[(quadrant+3)%4];
3122 	yy[0] = yy[3] = GMT->current.proj.zmin;	yy[1] = yy[2] = GMT->current.proj.zmax;
3123 	gmt_setpen (GMT, &GMT->current.map.frame.pen);
3124 	PSL_plotline (PSL, xx, yy, 4, PSL_MOVE|PSL_STROKE);
3125 }
3126 
gmtplot_timestamp(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double x,double y,unsigned int justify,char * U_label)3127 GMT_LOCAL void gmtplot_timestamp (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double x, double y, unsigned int justify, char *U_label) {
3128 	/* x, y = location of the time stamp box
3129 	 * justify indicates the corner of the box that (x,y) refers to, see below
3130 	 * U_label = label to be plotted to the right of the box
3131 	 *
3132 	 *   9        10       11
3133 	 *   |------------------|
3134 	 *   5         6        7
3135 	 *   |------------------|
3136 	 *   1         2        3
3137 	 */
3138 
3139 	time_t right_now;
3140 	char label[GMT_LEN512] = {""}, text[GMT_LEN256] = {""};
3141 	double dim[3] = {0.365, 0.15, 0.032};	/* Predefined dimensions in inches */
3142 	double unset_rgb[4] = {-1.0, -1.0, -1.0, 0.0};
3143 
3144 	/* Plot time string in format defined by format_time_stamp */
3145 
3146 	right_now = time ((time_t *)0);
3147 	strftime (text, sizeof(text), GMT->current.setting.format_time_stamp, localtime (&right_now));
3148 	snprintf (label, GMT_LEN256, "  %s  ", text);
3149 
3150 	PSL_command (PSL, "%% Begin GMT time-stamp\nV\n");
3151 	PSL_setorigin (PSL, x, y, 0.0, PSL_FWD);
3152 	PSL_setlinewidth (PSL, 0.25);
3153 	PSL_setfont (PSL, GMT->current.setting.font_logo.id);
3154 	PSL_defunits (PSL, "PSL_g_w", dim[0]);	/* Size of the black [GMT] box */
3155 	PSL_defunits (PSL, "PSL_g_h", dim[1]);
3156 	PSL_deftextdim (PSL, "PSL_b", 8.0, label);	/* Size of the white [timestamp] box (use only length) */
3157 
3158 	/* When justification is not BL (justify == 1), add some PostScript code to move to the
3159 		location where the lower left corner of the time stamp box is to be drawn */
3160 
3161 	switch ((justify + 3) % 4) {
3162 		case 1:	/* Center */
3163 			PSL_command (PSL, "PSL_g_w PSL_b_w add 2 div neg 0 T\n"); break;
3164 		case 2:	/* Right justify */
3165 			PSL_command (PSL, "PSL_g_w PSL_b_w add neg 0 T\n"); break;
3166 	}
3167 	switch (justify / 4) {
3168 		case 1: /* Middle */
3169 			PSL_command (PSL, "0 PSL_g_h 2 div neg T\n"); break;
3170 		case 2: /* Top justify */
3171 			PSL_command (PSL, "0 PSL_g_h neg T\n"); break;
3172 	}
3173 
3174 	/* Now draw black box with GMT logo, and white box with time stamp */
3175 
3176 	PSL_setfill (PSL, GMT->current.setting.map_default_pen.rgb, 1);
3177 	PSL_plotsymbol (PSL, 0.5*dim[0], 0.5*dim[1], dim, PSL_RECT);
3178 	PSL_plotcolorimage (PSL, 0.0, 0.0, dim[0], dim[1], PSL_BL, GMT_glyph, 220, 90, 1);
3179 	PSL_setfill (PSL, GMT->PSL->init.page_rgb, 1);
3180 	PSL_command (PSL, "PSL_g_h PSL_b_w PSL_g_w 0 Sb\n");
3181 	PSL_plottext (PSL, dim[0], dim[2], 8.0, label, 0.0, PSL_BL, 0);
3182 
3183 	/* Optionally, add additional label to the right of the box */
3184 
3185 	if (U_label && U_label[0]) {
3186 		snprintf (label, GMT_LEN512, "   %s", U_label);
3187 		PSL_plottext (PSL, 0.0, 0.0, -7.0, label, 0.0, PSL_BL, 0);
3188 	}
3189 
3190 	PSL_command (PSL, "U\n%% End GMT time-stamp\n");
3191 
3192 	/* Reset fill style to empty and no outline and reset linewidth */
3193 	PSL_setfill (PSL, unset_rgb, 0);
3194 	PSL->current.linewidth = GMT_NOTSET;
3195 }
3196 
gmtplot_echo_command(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,struct GMT_OPTION * options)3197 GMT_LOCAL void gmtplot_echo_command (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, struct GMT_OPTION *options) {
3198 	/* This routine will echo the command and its arguments to the
3199 	 * PostScript output file so that the user can see what scales
3200 	 * etc was used to produce this plot.  Any options with arguments
3201 	 * containing spaces will be enclosed in single quotes.
3202 	 */
3203 	size_t length = 0;
3204 	char outstring[GMT_LEN1024] = {""}, not_used[GMT_LEN32] = {""};
3205 	struct GMT_OPTION *opt = NULL;
3206 
3207 	if (GMT->current.setting.run_mode == GMT_MODERN)
3208 		PSL_command (PSL, "\n%% PostScript produced by:\n%%@GMT: gmt %s", gmt_current_name (GMT->init.module_name, not_used));
3209 	else
3210 		PSL_command (PSL, "\n%% PostScript produced by:\n%%@GMT: gmt %s", GMT->init.module_name);
3211 	for (opt = options; opt; opt = opt->next) {
3212 		if (length >= GMT_LEN512) {
3213 			PSL_command (PSL, "%s \\\n%%@GMT:+", outstring);
3214 			length = 0;
3215 			gmt_M_memset (outstring, GMT_LEN1024, char);
3216 		}
3217 		strcat (outstring, " ");	length++;
3218 		if (!(opt->option == GMT_OPT_INFILE || opt->option == GMT_OPT_OUTFILE)) {
3219 			if (strchr (opt->arg, ' ')) outstring[length++] = '\'';
3220 			outstring[length++] = '-';
3221 			outstring[length++] = opt->option;
3222 		}
3223 		if ((strlen (opt->arg) + length) < GMT_LEN1024) strcat (outstring, opt->arg);
3224 		length += strlen (opt->arg);
3225 		if (strchr (opt->arg, ' ')) outstring[length++] = '\'';
3226 	}
3227 	PSL_command (PSL, "%s\n", outstring);
3228 }
3229 
gmtplot_NaN_pen_up(double x[],double y[],unsigned int pen[],uint64_t n)3230 GMT_LOCAL void gmtplot_NaN_pen_up (double x[], double y[], unsigned int pen[], uint64_t n) {
3231 	/* Ensure that if there are NaNs we set pen = PSL_MOVE */
3232 
3233 	uint64_t i, n1;
3234 
3235 	for (i = 0, n1 = n - 1; i < n; i++) {
3236 		if (gmt_M_is_dnan (x[i]) || gmt_M_is_dnan (y[i])) {
3237 			pen[i] = PSL_MOVE;
3238 			if (i < n1) pen[i+1] = PSL_MOVE;	/* Since the next point must become the new anchor */
3239 		}
3240 	}
3241 }
3242 
gmtplot_northstar(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double x0,double y0,double r)3243 GMT_LOCAL void gmtplot_northstar (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double x0, double y0, double r) {
3244 	/* Draw a fancy 5-pointed North star */
3245 	unsigned int a;
3246 	double r2, x[4], y[4], dir, dir2, s, c;
3247 
3248 	r2 = r * 0.3;
3249 	for (a = 0; a <= 360; a += 72) {	/* Azimuth of the 5 points on the star */
3250 		/* Solid half */
3251 		x[0] = x[3] = x0, y[0] = y[3] = y0;
3252 		dir = 90.0 - (double)a;
3253 		sincosd (dir, &s, &c);
3254 		x[1] = x0 + r * c;
3255 		y[1] = y0 + r * s;
3256 		dir2 = dir - 36.0;
3257 		sincosd (dir2, &s, &c);
3258 		x[2] = x0 + r2 * c;
3259 		y[2] = y0 + r2 * s;
3260 		PSL_setfill (PSL, GMT->current.setting.map_default_pen.rgb, 1);
3261 		PSL_plotpolygon (PSL, x, y, 4);
3262 		/* Hollow half */
3263 		x[0] = x[3] = x0, y[0] = y[3] = y0;
3264 		sincosd (dir, &s, &c);
3265 		x[1] = x0 + r * c;
3266 		y[1] = y0 + r * s;
3267 		dir2 = dir + 36.0;
3268 		sincosd (dir2, &s, &c);
3269 		x[2] = x0 + r2 * c;
3270 		y[2] = y0 + r2 * s;
3271 		PSL_setfill (PSL, GMT->PSL->init.page_rgb, 1);
3272 		PSL_plotpolygon (PSL, x, y, 4);
3273 	}
3274 }
3275 
3276 #define M_VW	0.005
3277 #define M_HL	0.075
3278 #define M_HW	0.025
3279 
3280 #define DIST_TO_2ND_POINT 1.0
3281 
3282 #define F_VW	0.01
3283 #define F_HL	0.15
3284 #define F_HW	0.05
3285 
gmtplot_draw_mag_rose(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,struct GMT_MAP_ROSE * mr)3286 GMT_LOCAL void gmtplot_draw_mag_rose (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, struct GMT_MAP_ROSE *mr) {
3287 	/* Magnetic compass rose */
3288 	unsigned int i, k, level, just, ljust[4] = {PSL_TC, PSL_ML, PSL_BC, PSL_MR}, n_tick = 0, form;
3289 	double ew_angle, angle, R[2], tlen[3], L, s, c, lon, lat, x[5], y[5], xp[5], yp[5];
3290 	double offset, t_angle, scale[2], base, v_angle, *val = NULL, dim[PSL_MAX_DIMS];
3291 	char label[GMT_LEN16], *type[2] = {"inner", "outer"};
3292 	struct GMT_FILL f;
3293 
3294 	gmt_xy_to_geo (GMT, &lon, &lat, mr->refpoint->x, mr->refpoint->y);
3295 
3296 	/* Initialize fill structure */
3297 	gmt_init_fill (GMT, &f, GMT->current.setting.color_patch[GMT_BGD][0], GMT->current.setting.color_patch[GMT_BGD][1], GMT->current.setting.color_patch[GMT_BGD][2]);
3298 	ew_angle = gmt_azim_to_angle (GMT, lon, lat, DIST_TO_2ND_POINT, 90.0);	/* Get angle of E-W direction at this location */
3299 
3300 	R[GMT_ROSE_PRIMARY] = 0.75 * 0.5 * mr->size;
3301 	R[GMT_ROSE_SECONDARY] = 0.5 * mr->size;
3302 	tlen[0] = GMT->current.setting.map_tick_length[GMT_TICK_UPPER];
3303 	tlen[1] = GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER];
3304 	tlen[2] = 1.5 * GMT->current.setting.map_tick_length[GMT_ANNOT_UPPER];
3305 	scale[GMT_ROSE_PRIMARY] = 0.85;
3306 	scale[GMT_ROSE_SECONDARY] = 1.0;
3307 	GMT->current.plot.r_theta_annot = false;	/* Just in case it was turned on in gmt_map.c */
3308 
3309 	PSL_settextmode (PSL, PSL_TXTMODE_MINUS);	/* Replace hyphens with minus signs */
3310 
3311 	for (level = 0; level < 2; level++) {	/* Inner (0) and outer (1) angles */
3312 		if (level == GMT_ROSE_PRIMARY && mr->kind != 2) continue;	/* Sorry, not magnetic directions */
3313 		if (mr->draw_circle[level]) {
3314 			gmt_setfill (GMT, NULL, 1);
3315 			PSL_comment (PSL, "Draw magnetic rose %s circle\n", type[level]);
3316 			gmt_setpen (GMT, &mr->pen[level]);
3317 			s = 2.0 * R[level];
3318 			PSL_plotsymbol (PSL, mr->refpoint->x, mr->refpoint->y, &s, PSL_CIRCLE);
3319 		}
3320 		offset = (level == GMT_ROSE_PRIMARY) ? mr->declination : 0.0;
3321 		gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[level]);
3322 		n_tick = gmtlib_linear_array (GMT, 0.0, 360.0, mr->g_int[level], 0.0, &val);
3323 		PSL_comment (PSL, "Draw %d tickmarks for magnetic rose %s circle\n", n_tick, type[level]);
3324 		for (i = 0; i < n_tick - 1; i++) {	/* Increments of fine tickmarks (-1 to avoid repeating 360) */
3325 			angle = 90.0 - (offset + val[i]);	/* Since val is azimuth */
3326 			k = (gmt_M_is_zero (fmod (val[i], mr->a_int[level]))) ? 2 : ((gmt_M_is_zero (fmod (val[i], mr->f_int[level]))) ? 1 : 0);
3327 			sincosd (ew_angle + angle, &s, &c);
3328 			x[0] = mr->refpoint->x + R[level] * c, y[0] = mr->refpoint->y + R[level] * s;
3329 			x[1] = mr->refpoint->x + (R[level] - scale[level]*tlen[k]) * c, y[1] = mr->refpoint->y + (R[level] - scale[level]*tlen[k]) * s;
3330 			PSL_plotsegment (PSL, x[0], y[0], x[1], y[1]);
3331 		}
3332 		gmt_M_free (GMT, val);
3333 
3334 		form = gmt_setfont (GMT, &GMT->current.setting.font_annot[level]);
3335 		n_tick = gmtlib_linear_array (GMT, 0.0, 360.0, mr->a_int[level], 0.0, &val);
3336 		PSL_comment (PSL, "Draw %d tickmarks and annotations for magnetic rose %s circle\n", n_tick, type[level]);
3337 		for (i = 0; i < n_tick - 1; i++) {	/* Increments of annotations (-1 to avoid repeating 360) */
3338 			angle = 90.0 - (offset + val[i]);	/* Since val is azimuth */
3339 			sincosd (ew_angle + angle, &s, &c);
3340 			x[0] = mr->refpoint->x + (R[level] + GMT->current.setting.map_annot_offset[level]) * c, y[0] =
3341 					 mr->refpoint->y + (R[level] + GMT->current.setting.map_annot_offset[level]) * s;
3342 			if (GMT->current.setting.map_degree_symbol == gmt_none)
3343 				snprintf (label, GMT_LEN16, "%ld", lrint (val[i]));
3344 			else
3345 				snprintf (label, GMT_LEN16, "%ld%c", lrint (val[i]), (int)GMT->current.setting.ps_encoding.code[GMT->current.setting.map_degree_symbol]);
3346 			t_angle = fmod ((double)(ew_angle - val[i] - offset) + 360.0, 360.0);	/* Now in 0-360 range */
3347 			if (t_angle > 180.0) t_angle -= 180.0;	/* Now in -180/180 range */
3348 			if (t_angle > 90.0 || t_angle < -90.0) t_angle -= copysign (180.0, t_angle);
3349 			just = (y[0] <= mr->refpoint->y) ? PSL_TC : PSL_BC;
3350 			v_angle = val[i] - ew_angle;
3351 			if (level == GMT_ROSE_SECONDARY && doubleAlmostEqual (v_angle, 90.0))
3352 				t_angle = -90.0, just = PSL_BC;
3353 			if (level == GMT_ROSE_SECONDARY && doubleAlmostEqual (v_angle, 270.0))
3354 				t_angle = 90.0, just = PSL_BC;
3355 			PSL_plottext (PSL, x[0], y[0], GMT->current.setting.font_annot[level].size, label, t_angle, just, form);
3356 		}
3357 		gmt_M_free (GMT, val);
3358 	}
3359 
3360 	/* Draw extra tick for the 4 main compass directions */
3361 	gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_SECONDARY]);
3362 	base = R[GMT_ROSE_SECONDARY] + GMT->current.setting.map_annot_offset[GMT_SECONDARY] + GMT->current.setting.font_annot[GMT_ROSE_SECONDARY].size / PSL_POINTS_PER_INCH;
3363 	PSL_comment (PSL, "Draw 4 tickmarks and annotations for cardinal directions of magnetic rose\n", n_tick);
3364 	for (i = 0, k = 1; i < 360; i += 90, k++) {	/* 90-degree increments of tickmarks */
3365 		angle = (double)i;
3366 		sincosd (ew_angle + angle, &s, &c);
3367 		x[0] = mr->refpoint->x + R[GMT_ROSE_SECONDARY] * c, y[0] = mr->refpoint->y + R[GMT_ROSE_SECONDARY] * s;
3368 		x[1] = mr->refpoint->x + (R[GMT_ROSE_SECONDARY] + tlen[0]) * c, y[1] = mr->refpoint->y + (R[GMT_ROSE_SECONDARY] + tlen[0]) * s;
3369 		PSL_plotsegment (PSL, x[0], y[0], x[1], y[1]);
3370 		if (k == 4) k = 0;
3371 		if (!mr->label[k][0]) continue;	/* No label desired */
3372 		x[0] = mr->refpoint->x + base * c, y[0] = mr->refpoint->y + base * s;
3373 		x[1] = mr->refpoint->x + (base + 2.0 * tlen[2]) * c, y[1] = mr->refpoint->y + (base + 2.0 * tlen[2]) * s;
3374 		PSL_plotsegment (PSL, x[0], y[0], x[1], y[1]);
3375 		if (k == 2 && mr->label[2][0] == '*') {	/* Wanted '*' instead of N */
3376 			x[0] = mr->refpoint->x + (base + 2.0*tlen[2] + GMT->current.setting.map_title_offset + 0.025*mr->size) * c, y[0] = mr->refpoint->y + (base + 2.0*tlen[2] + GMT->current.setting.map_title_offset + 0.025*mr->size) * s;
3377 			gmtplot_northstar (GMT, PSL, x[0], y[0], 0.1*mr->size);
3378 		}
3379 		else {
3380 			x[0] = mr->refpoint->x + (base + 2.0*tlen[2] + GMT->current.setting.map_title_offset) * c, y[0] = mr->refpoint->y + (base + 2.0*tlen[2] + GMT->current.setting.map_title_offset) * s;
3381 			form = gmt_setfont (GMT, &GMT->current.setting.font_title);
3382 			PSL_plottext (PSL, x[0], y[0], GMT->current.setting.font_title.size, mr->label[k], ew_angle, ljust[k], form);
3383 			gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_SECONDARY]);
3384 		}
3385 	}
3386 
3387 	gmt_M_memset (dim, PSL_MAX_DIMS, double);
3388 	dim[11] = 0.5 * GMT->current.setting.map_default_pen.width;
3389 	PSL_defpen (PSL, "PSL_vecheadpen", GMT->current.setting.map_default_pen.width, GMT->current.setting.map_default_pen.style, GMT->current.setting.map_default_pen.offset, GMT->current.setting.map_default_pen.rgb);
3390 	if (mr->kind == 2) {	/* Compass needle and label */
3391 		char tmpstring[GMT_LEN64] = {""};
3392 		PSL_comment (PSL, "Draw magnetic rose declination arrow and optional label\n");
3393 		sincosd (ew_angle + (90.0 - mr->declination), &s, &c);
3394 		L = R[GMT_ROSE_PRIMARY] - 2.0 * tlen[2];
3395 		x[0] = mr->refpoint->x - L * c, y[0] = mr->refpoint->y - L * s;
3396 		x[1] = mr->refpoint->x + L * c, y[1] = mr->refpoint->y + L * s;
3397 		dim[PSL_VEC_XTIP]        = x[1];
3398 		dim[PSL_VEC_YTIP]        = y[1];
3399 		dim[PSL_VEC_TAIL_WIDTH]  = M_VW * mr->size;
3400 		dim[PSL_VEC_HEAD_LENGTH] = M_HL * mr->size;
3401 		dim[PSL_VEC_HEAD_WIDTH]  = M_HW * mr->size,
3402 		dim[PSL_VEC_HEAD_SHAPE]  = GMT->current.setting.map_vector_shape;
3403 		dim[PSL_VEC_STATUS]      = PSL_VEC_END | PSL_VEC_FILL;
3404 		gmt_setpen (GMT, &GMT->current.setting.map_default_pen);
3405 		gmt_setfill (GMT, &f, 1);
3406 		PSL_plotsymbol (PSL, x[0], y[0], dim, PSL_VECTOR);
3407 		t_angle = fmod (ew_angle + 90.0 - mr->declination + 360.0, 360.0);	/* Now in 0-360 range */
3408 		if (fabs (t_angle) > 90.0) t_angle -= copysign (180.0, t_angle);
3409 		sincosd (t_angle, &s, &c);
3410 		x[0] = mr->refpoint->x - 2.0 * M_VW * mr->size * s, y[0] = mr->refpoint->y + 2.0 * M_VW * mr->size * c;
3411 		if (strcmp (mr->dlabel, "-")) {	/* Want declination labeling unless when giving "-" */
3412 			if (mr->dlabel[0] == 0) {	/* Want default label */
3413 				gmtlib_get_annot_label (GMT, mr->declination, tmpstring, true, false, true, 0, GMT->current.map.is_world);
3414 				snprintf (mr->dlabel, GMT_LEN256, "@~d@~ = %s", tmpstring);
3415 			}
3416 			form = gmt_setfont (GMT, &GMT->current.setting.font_label);
3417 			PSL_plottext (PSL, x[0], y[0], GMT->current.setting.font_label.size, mr->dlabel, t_angle, PSL_BC, form);
3418 		}
3419 	}
3420 	else {			/* Just geographic directions and a centered arrow */
3421 		L = mr->size - 4.0*tlen[2];
3422 		x[0] = x[1] = x[4] = 0.0,	x[2] = -0.25 * mr->size,		x[3] = -x[2];
3423 		y[0] = -0.5 * L,		y[1] = -y[0], y[2] = y[3] = 0.0,	y[4] = y[1] + GMT->current.setting.map_annot_offset[GMT_PRIMARY];
3424 		gmtlib_rotate2D (GMT, x, y, 5, mr->refpoint->x, mr->refpoint->y, ew_angle, xp, yp);	/* Coordinate transformation and placement of the 4 labels */
3425 		dim[PSL_VEC_XTIP]        = xp[1];
3426 		dim[PSL_VEC_YTIP]        = yp[1];
3427 		dim[PSL_VEC_TAIL_WIDTH]  = F_VW * mr->size;
3428 		dim[PSL_VEC_HEAD_LENGTH] = F_HL * mr->size;dim[PSL_VEC_HEAD_WIDTH] = F_HW * mr->size;
3429 		dim[PSL_VEC_HEAD_SHAPE]  = GMT->current.setting.map_vector_shape;
3430 		dim[PSL_VEC_STATUS]      = PSL_VEC_END | PSL_VEC_FILL;
3431 		gmt_setfill (GMT, &f, 1);
3432 		PSL_defpen (PSL, "PSL_vecheadpen", GMT->current.setting.map_frame_pen.width, "", 0, f.rgb);
3433 		PSL_plotsymbol (PSL, xp[0], yp[0], dim, PSL_VECTOR);
3434 		s = 0.25 * mr->size;
3435 		gmt_init_fill (GMT, &f, -1.0, -1.0, -1.0);
3436 		gmt_setfill (GMT, &f, 1);
3437 		PSL_plotsymbol (PSL, mr->refpoint->x, mr->refpoint->y, &s, PSL_CIRCLE);
3438 		PSL_plotsegment (PSL, xp[2], yp[2], xp[3], yp[3]);
3439 	}
3440 
3441 	PSL_settextmode (PSL, PSL_TXTMODE_HYPHEN);	/* Back to leave as is */
3442 }
3443 
3444 /* These are used to scale the plain arrow given rose size */
3445 
3446 #define ROSE_LENGTH_SCL1	(0.5 * M_SQRT2)
3447 #define ROSE_LENGTH_SCL2	0.5
3448 #define ROSE_WIDTH_SCL1		0.2
3449 #define ROSE_WIDTH_SCL2		0.2
3450 #define ROSE_WIDTH_SCL3		0.2
3451 
gmtplot_draw_dir_rose(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,struct GMT_MAP_ROSE * mr)3452 GMT_LOCAL void gmtplot_draw_dir_rose (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, struct GMT_MAP_ROSE *mr) {
3453 	unsigned int i, kind, form, just[4] = {PSL_TC, PSL_ML, PSL_BC, PSL_MR};
3454 	int k;
3455 	double angle, L[4], R[4], x[PSL_MAX_DIMS], y[8], xp[8], yp[8], tx[3], ty[3];
3456 	double lon, lat, s, c, rot[4] = {0.0, 45.0, 22.5, -22.5};
3457 	struct GMT_FILL f;
3458 
3459 	/* Initialize fill structure */
3460 	gmt_init_fill (GMT, &f, GMT->current.setting.color_patch[GMT_BGD][0], GMT->current.setting.color_patch[GMT_BGD][1], GMT->current.setting.color_patch[GMT_BGD][2]);
3461 
3462 	gmt_xy_to_geo (GMT, &lon, &lat, mr->refpoint->x, mr->refpoint->y);
3463 	angle = gmt_azim_to_angle (GMT, lon, lat, DIST_TO_2ND_POINT, 90.0);	/* Get angle of E-W direction at this location */
3464 
3465 	gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
3466 
3467 	if (mr->type == GMT_ROSE_DIR_FANCY) {	/* Fancy scale */
3468 		PSL_comment (PSL, "Draw fancy directional rose of level %d\n", mr->kind);
3469 		mr->size *= 0.5;	/* Got diameter, use radius for calculations */
3470 		L[0] = mr->size;
3471 		L[1] = ROSE_LENGTH_SCL1 * mr->size;
3472 		L[2] = L[3] = ROSE_LENGTH_SCL2 * mr->size;
3473 		R[0] = ROSE_WIDTH_SCL1 * mr->size;
3474 		R[1] = ROSE_WIDTH_SCL2 * mr->size;
3475 		R[2] = R[3] = ROSE_WIDTH_SCL3 * mr->size;
3476 		mr->kind--;	/* Turn 1-3 into 0-2 */
3477 		if (mr->kind == 2) mr->kind = 3;	/* Trick so that we can draw 8 rather than 4 points */
3478 		for (k = kind = mr->kind; k >= 0; k--, kind--) {
3479 			/* Do 4 blades 90 degrees apart, aligned with main axes & relative to (0,0) */
3480 			x[0] = L[kind], x[1] = x[7] = 0.5 * M_SQRT2 * R[kind], x[2] = x[6] = 0.0;
3481 			y[0] = y[4] = 0.0, y[1] = y[3] = 0.5 * M_SQRT2 * R[kind], y[2] = L[kind];
3482 			x[3] = x[5] = -x[1], x[4] = -x[0];
3483 			y[5] = y[7] = -y[1], y[6] = -y[2];
3484 			gmtlib_rotate2D (GMT, x, y, 8, mr->refpoint->x, mr->refpoint->y, rot[kind] + angle, xp, yp);	/* Coordinate transformation and placement of the 4 labels */
3485 			PSL_setfill (PSL, GMT->PSL->init.page_rgb, 1);
3486 			PSL_plotpolygon (PSL, xp, yp, 8);	/* Outline of 4-pointed star */
3487 			tx[0] = mr->refpoint->x, ty[0] = mr->refpoint->y;
3488 			/* Fill positive halves of the 4-pointed blades */
3489 			PSL_setfill (PSL, GMT->current.setting.map_default_pen.rgb, 1);
3490 			tx[1] = xp[0], ty[1] = yp[0], tx[2] = xp[7], ty[2] = yp[7];
3491 			PSL_plotpolygon (PSL, tx, ty, 3);	/* East */
3492 			tx[1] = xp[1], ty[1] = yp[1], tx[2] = xp[2], ty[2] = yp[2];
3493 			PSL_plotpolygon (PSL, tx, ty, 3);	/* North */
3494 			tx[1] = xp[3], ty[1] = yp[3], tx[2] = xp[4], ty[2] = yp[4];
3495 			PSL_plotpolygon (PSL, tx, ty, 3);	/* West */
3496 			tx[1] = xp[5], ty[1] = yp[5], tx[2] = xp[6], ty[2] = yp[6];
3497 			PSL_plotpolygon (PSL, tx, ty, 3);	/* South */
3498 		}
3499 		sincosd (angle, &s, &c);
3500 		x[0] = x[2] = 0.0, x[1] = L[0] + GMT->current.setting.map_title_offset; x[3] = -x[1];
3501 		y[1] = y[3] = 0.0, y[2] = L[0] + GMT->current.setting.map_title_offset; y[0] = -y[2];
3502 		gmtlib_rotate2D (GMT, x, y, 4, mr->refpoint->x, mr->refpoint->y, angle, xp, yp);	/* Coordinate transformation and placement of the 4 labels */
3503 		form = gmt_setfont (GMT, &GMT->current.setting.font_title);
3504 		for (i = 0; i < 4; i++) PSL_plottext (PSL, xp[i], yp[i], GMT->current.setting.font_title.size, mr->label[i], angle, just[i], form);
3505 	}
3506 	else {			/* Plain North arrow w/circle */
3507 		PSL_comment (PSL, "Draw plain directional rose\n");
3508 		sincosd (angle, &s, &c);
3509 		gmt_M_memset (x, PSL_MAX_DIMS, double);
3510 		x[0] = x[1] = x[4] = 0.0, x[2] = -0.25 * mr->size, x[3] = -x[2];
3511 		y[0] = -0.5 * mr->size, y[1] = -y[0], y[2] = y[3] = 0.0; y[4] = y[1] + GMT->current.setting.map_annot_offset[GMT_PRIMARY];
3512 		gmtlib_rotate2D (GMT, x, y, 5, mr->refpoint->x, mr->refpoint->y, angle, xp, yp);	/* Coordinate transformation and placement of the 4 labels */
3513 		x[0] = xp[1], x[1] = yp[1];
3514 		x[2] = F_VW * mr->size, x[3] = F_HL * mr->size, x[4] = F_HW * mr->size;
3515 		x[5] = GMT->current.setting.map_vector_shape, x[6] = PSL_VEC_END | PSL_VEC_FILL;
3516 		gmt_setfill (GMT, &f, 1);
3517 		PSL_defpen (PSL, "PSL_vecheadpen", GMT->current.setting.map_frame_pen.width, "", 0, f.rgb);
3518 		PSL_plotsymbol (PSL, xp[0], yp[0], x, PSL_VECTOR);
3519 		s = 0.25 * mr->size;
3520 		gmt_init_fill (GMT, &f, -1.0, -1.0, -1.0);
3521 		gmt_setfill (GMT, &f, 1);
3522 		PSL_plotsymbol (PSL, mr->refpoint->x, mr->refpoint->y, &s, PSL_CIRCLE);
3523 		PSL_plotsegment (PSL, xp[2], yp[2], xp[3], yp[3]);
3524 		if (mr->label[2][0]) {	/* Wanted the north label */
3525 			form = gmt_setfont (GMT, &GMT->current.setting.font_title);
3526 			PSL_plottext (PSL, xp[4], yp[4], GMT->current.setting.font_title.size, mr->label[2], angle, PSL_BC, form);
3527 		}
3528 	}
3529 }
3530 
gmtplot_savepen(struct GMT_CTRL * GMT,struct GMT_PEN * pen)3531 GMT_LOCAL void gmtplot_savepen (struct GMT_CTRL *GMT, struct GMT_PEN *pen) {
3532 	/* gmt_getpen retrieves the current pen in PSL. */
3533 	struct PSL_CTRL *PSL = GMT->PSL;
3534 	if (!pen) return;
3535 	pen->width = PSL->current.linewidth;
3536 	pen->offset = PSL->current.offset;
3537 	if (PSL->current.style[0])
3538 		strncpy (pen->style, PSL->current.style, GMT_PEN_LEN-1);
3539 	else
3540 		memset (pen->style, 0, GMT_PEN_LEN);
3541 	gmt_M_rgb_copy (pen->rgb, PSL->current.rgb[PSL_IS_STROKE]);
3542 }
3543 
3544 enum plot_operand {
3545 	LEFT_OPERAND = 0,
3546 	RIGHT_OPERAND1 = 1,
3547 	RIGHT_OPERAND2 = 2
3548 };
3549 
gmtplot_custum_failed_bool_test_string(struct GMT_CTRL * GMT,struct GMT_CUSTOM_SYMBOL_ITEM * s,double size[],char * text,bool * retval)3550 GMT_LOCAL int gmtplot_custum_failed_bool_test_string (struct GMT_CTRL *GMT, struct GMT_CUSTOM_SYMBOL_ITEM *s, double size[], char *text, bool *retval) {
3551 	unsigned int k;
3552 	int type;
3553 	bool result, delete[2] = {false, false};
3554 	char *arg[2];
3555 	gmt_M_unused (size);	/* Numerical values not used here */
3556 	for (k = 0; k < 2; k++) {	/* Load up the left and right operands */
3557 		type = (s->var[k] >= GMT_VAR_WORD) ? GMT_VAR_WORD : s->var[k];	/* If word then (s->var[k] - GMT_VAR_WORD) is the word number (0, 1, ...) */
3558 		switch (type) {
3559 			case GMT_VAR_STRING:	/* trailing text comparison */
3560 				arg[k] = text;		break;
3561 			case GMT_VAR_WORD:		/* trailing word comparison */
3562 				arg[k] = gmt_get_word (text, " \t", s->var[0] - GMT_VAR_WORD);	delete[k] = true;	break;
3563 			case GMT_CONST_STRING:	/* Constant text comparison */
3564 				arg[k] = s->string;	break;
3565 			default:	/* Should not get here */
3566 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized text variable type (%d) passed to gmtplot_custum_failed_bool_test_string\n", s->var[k]);
3567 				return GMT_NOT_A_VALID_TYPE;		break;
3568 		}
3569 	}
3570 	switch (s->operator) {
3571 		case '<':	/* < */
3572 			result = (strcmp (arg[LEFT_OPERAND], arg[RIGHT_OPERAND1]) < 0);
3573 			break;
3574 		case 'L':	/* <= */
3575 			result = (strcmp (arg[LEFT_OPERAND], arg[RIGHT_OPERAND1]) <= 0);
3576 			break;
3577 		case '=':	/* == */
3578 			result = (strcmp (arg[LEFT_OPERAND], arg[RIGHT_OPERAND1]) == 0);
3579 			break;
3580 		case 'G':	/* >= */
3581 			result = (strcmp (arg[LEFT_OPERAND], arg[RIGHT_OPERAND1]) >= 0);
3582 			break;
3583 		case '>':	/* > */
3584 			result = (strcmp (arg[LEFT_OPERAND], arg[RIGHT_OPERAND1]) > 0);
3585 			break;
3586 		default:
3587 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized symbol macro text operator (%d = '%c') passed to gmt_draw_custom_symbol\n", s->operator, (char)s->operator);
3588 			return GMT_PARSE_ERROR;
3589 			break;
3590 	}
3591 	for (k = 0; k < 2; k++)
3592 		if (delete[k]) gmt_M_str_free (arg[k]);	/* Free word that we extracted from text */
3593 
3594 	if (s->negate) result = !result;	/* Negate the test since we used a ! operator , e.g., != */
3595 	*retval = !result;		/* Return the opposite of the test result */
3596 	return (GMT_NOERROR);
3597 }
3598 
gmtplot_custum_failed_bool_test(struct GMT_CTRL * GMT,struct GMT_CUSTOM_SYMBOL_ITEM * s,double size[],char * text,bool * retval)3599 GMT_LOCAL int gmtplot_custum_failed_bool_test (struct GMT_CTRL *GMT, struct GMT_CUSTOM_SYMBOL_ITEM *s, double size[], char *text, bool *retval) {
3600 	unsigned int k;
3601 	int error = GMT_NOERROR;
3602 	bool result;
3603 	double arg[3];
3604 
3605 	/* Determine if we have text comparisons to deal with, if so, call the string version of this function */
3606 
3607 	for (k = 0; k < 2; k++)
3608 		if (s->var[k] == GMT_VAR_STRING || s->var[k] >= GMT_VAR_WORD) {
3609 			error = gmtplot_custum_failed_bool_test_string (GMT, s, size, text, retval);
3610 			return error;
3611 		}
3612 
3613 	/* Here we have numerical comparisons only */
3614 
3615 	/* Perform the boolean comparison and return false if test is true */
3616 
3617 	for (k = 0; k < 3; k++) {	/* Load up the left and 1-2 right operands */
3618 		switch (s->var[k]) {
3619 			case GMT_VAR_SIZE:	/* Symbol size */
3620 				arg[k] = size[0];	break;
3621 			case GMT_VAR_IS_Y:	/* User y-coordinate */
3622 				arg[k] = GMT->current.io.curr_rec[GMT_Y];	break;
3623 			case GMT_VAR_IS_X:	/* User x-coordinate */
3624 				arg[k] = GMT->current.io.curr_rec[GMT_X];	break;
3625 			case GMT_CONST_VAR:	/* A numeric constant */
3626 				arg[k] = s->const_val[k];	break;
3627 			default:		/* One of the variables 1-n */
3628 				arg[k] = size[s->var[k]];	break;
3629 		}
3630 	}
3631 	switch (s->operator) {
3632 		case '<':	/* < */
3633 			result = (arg[LEFT_OPERAND] < arg[RIGHT_OPERAND1]);
3634 			break;
3635 		case 'L':	/* <= */
3636 			result = (arg[LEFT_OPERAND] <= arg[RIGHT_OPERAND1]);
3637 			break;
3638 		case '=':	/* == */
3639 			result = (arg[LEFT_OPERAND] == arg[RIGHT_OPERAND1]);
3640 			break;
3641 		case 'G':	/* >= */
3642 			result = (arg[LEFT_OPERAND] >= arg[RIGHT_OPERAND1]);
3643 			break;
3644 		case '>':	/* > */
3645 			result = (arg[LEFT_OPERAND] > arg[RIGHT_OPERAND1]);
3646 			break;
3647 		case '%':	/* % */
3648 			result = (!gmt_M_is_zero (fmod (arg[LEFT_OPERAND], arg[RIGHT_OPERAND1])));
3649 			break;
3650 		case 'I':	/* [] inclusive range */
3651 			result = (arg[LEFT_OPERAND] >= arg[RIGHT_OPERAND1] && arg[LEFT_OPERAND] <= arg[RIGHT_OPERAND2]);
3652 			break;
3653 		case 'i':	/* <> exclusive range */
3654 			result = (arg[LEFT_OPERAND] > arg[RIGHT_OPERAND1] && arg[LEFT_OPERAND] < arg[RIGHT_OPERAND2]);
3655 			break;
3656 		case 'l':	/* [> in/ex-clusive range */
3657 			result = (arg[LEFT_OPERAND] >= arg[RIGHT_OPERAND1] && arg[LEFT_OPERAND] < arg[RIGHT_OPERAND2]);
3658 			break;
3659 		case 'r':	/* <] ex/in-clusive range */
3660 			result = (arg[LEFT_OPERAND] > arg[RIGHT_OPERAND1] && arg[LEFT_OPERAND] <= arg[RIGHT_OPERAND2]);
3661 			break;
3662 		case 'E':	/* var == NaN */
3663 			result = gmt_M_is_dnan (arg[LEFT_OPERAND]);
3664 			break;
3665 		default:
3666 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized symbol macro numerical operator (%d = '%c') passed to gmt_draw_custom_symbol\n", s->operator, (char)s->operator);
3667 			return GMT_PARSE_ERROR;
3668 			break;
3669 	}
3670 	if (s->negate) result = !result;	/* Negate the test since we used a ! operator , e.g., != */
3671 	*retval = !result;		/* Return the opposite of the test result */
3672 	return (GMT_NOERROR);
3673 }
3674 
gmtplot_flush_symbol_piece(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double * x,double * y,uint64_t * n,struct GMT_PEN * p,struct GMT_FILL * f,int outline,bool * flush)3675 GMT_LOCAL void gmtplot_flush_symbol_piece (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double *x, double *y, uint64_t *n, struct GMT_PEN *p, struct GMT_FILL *f, int outline, bool *flush) {
3676 	int draw_outline = (outline && p->rgb[0] != -1) ? 1 : 0;
3677 
3678 	if (draw_outline) gmt_setpen (GMT, p);
3679 	if (outline == 2) {	/* Stroke path only */
3680 		PSL_plotline (PSL, x, y, (int)*n, PSL_MOVE|PSL_STROKE);
3681 	}
3682 	else {	/* Fill polygon and possibly stroke outline */
3683 		gmt_setfill (GMT, f, draw_outline);
3684 		PSL_plotpolygon (PSL, x, y, (int)*n);
3685 	}
3686 	*flush = false;
3687 	*n = 0;
3688 }
3689 
gmtplot_format_symbol_string(struct GMT_CTRL * GMT,struct GMT_CUSTOM_SYMBOL_ITEM * s,double size[],char * text)3690 GMT_LOCAL void gmtplot_format_symbol_string (struct GMT_CTRL *GMT, struct GMT_CUSTOM_SYMBOL_ITEM *s, double size[], char *text) {
3691 	/* Returns the [possibly reformatted] string to use for the letter macro.
3692 	 * These are the things that can happen:
3693 	 * 1. Action = GMT_SYMBOL_TEXT means we have a static fixed text string; just copy
3694 	 * 2. s->text is $t.  Then we use the trailing text from the input as the text.
3695 	 *    Optionally, a single word of the trailing text can be selected by appending
3696 	 *    a word integer, $t0 is the first word, $t1 the second, etc.
3697 	 * 3. We have a format statement that contains free-form text with interspersed
3698 	 *    special formatting commands.  These have the syntax
3699 	 *    %X  Add longitude or x using chosen default format.
3700 	 *    %Y  Add latitude or y using chosen default format.
3701 	 *    $<n>[+X|Y|T]  Format the numerical variable $<n>; if
3702 	 *      followed by +X|Y|T we format as lon, lat, or absolute dateTtime,
3703 	 *      else we use FORMAT_FLOAT_OUT.
3704 	 *    Limitation: Currently, $<n> expects <n> to be 0-9 only.
3705 	 */
3706 	unsigned int n;
3707 	if (s->action == GMT_SYMBOL_TEXT)	/* Constant text given, just duplicate */
3708 		strcpy (text, s->string);
3709 	else if (!strcmp (s->string, "$t"))	/* Get entire string from trailing text in the input */
3710 		strcpy (text, GMT->current.io.curr_trailing_text);
3711 	else if (!strncmp (s->string, "$t", 2U) && isdigit (s->string[2])) {	/* Get word number n from trailing text in the input */
3712 		char *word = gmt_get_word (GMT->current.io.curr_trailing_text, " \t", s->var[0]);
3713 		if (word) {	/* Got it */
3714 			strcpy (text, word);
3715 			gmt_M_str_free (word);
3716 		}
3717 		else {
3718 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "No word # %d in the trailing text - return all text instead\n", s->var[0]);
3719 			strcpy (text, GMT->current.io.curr_trailing_text);
3720 		}
3721 	}
3722 	else {	/* Must replace special items within a template string */
3723 		unsigned int n_skip, in, out;
3724 		char tmp[GMT_LEN64] = {""};
3725 		gmt_M_memset (text, GMT_LEN256, char);
3726 		for (in = out = 0; s->string[in]; in++) {
3727 			switch (s->string[in]) {
3728 				case '%':	/* Possibly a special %X, %Y request */
3729 					if (s->string[in+1] == 'X' || s->string[in+1] == 'Y') {	/* Yes it was */
3730 						n = (s->string[in+1] == 'X') ? GMT_X : GMT_Y;
3731 						gmt_ascii_format_col (GMT, tmp, GMT->current.io.curr_rec[n], GMT_IN, n);
3732 						strcat (text, tmp);
3733 						in++;	/* Skip past the X or Y */
3734 						out += (unsigned int)strlen (tmp);
3735 					}
3736 					else /* Just a % sign */
3737 						text[out++] = s->string[in];
3738 					break;
3739 				case '$':	/* Possibly a variable $n */
3740 					if (isdigit (s->string[in+1])) {	/* Yes, it was */
3741 						n = (s->string[in+1] - '0');
3742 						n_skip = 1;
3743 						if (s->string[in+2] == '+' && strchr ("TXY", s->string[in+3])) {	/* Specific formatting requested */
3744 							if (s->string[in+3] == 'X') gmt_ascii_format_col (GMT, tmp, size[n], GMT_IN, GMT_X);
3745 							else if (s->string[in+3] == 'Y') gmt_ascii_format_col (GMT, tmp, size[n], GMT_IN, GMT_Y);
3746 							else if (s->string[in+3] == 'T') gmt_format_abstime_output (GMT, size[n], tmp);
3747 							n_skip += 2;
3748 						}
3749 						else
3750 							snprintf (tmp, GMT_LEN64, GMT->current.setting.format_float_out, size[n]);
3751 						strcat (text, tmp);
3752 						in += n_skip;	/* Skip past the $n[+X|Y|T] */
3753 						out += (unsigned int)strlen (tmp);
3754 					}
3755 					else	/* Just pass regular text along */
3756 						text[out++] = s->string[in];
3757 					break;
3758 				default:	/* Just pass regular text along */
3759 					text[out++] = s->string[in];
3760 					break;
3761 			}
3762 		}
3763 	}
3764 }
3765 
gmtplot_encodefont(struct PSL_CTRL * PSL,int font_no,char * name,unsigned int id)3766 GMT_LOCAL void gmtplot_encodefont (struct PSL_CTRL *PSL, int font_no, char *name, unsigned int id) {
3767 	/* Create the custom symbol macro that selects the correct font and size for the symbol item */
3768 
3769 	bool encode = (PSL->init.encoding && !PSL->internal.font[font_no].encoded);
3770 
3771 	if (PSL->internal.comments) PSL_command (PSL, "%% Set font encoding and size for this custom symbol %s item %d\n", name, id);
3772 	PSL_command (PSL, "/PSL_symbol_%s_setfont_%d {", name, id);
3773 	if (encode) {	/* Re-encode fonts with Standard+ or ISOLatin1[+] encodings */
3774 		PSL_command (PSL, " PSL_font_encode %d get 0 eq {%s_Encoding /%s /%s PSL_reencode PSL_font_encode %d 1 put} if", font_no, PSL->init.encoding, PSL->internal.font[font_no].name, PSL->internal.font[font_no].name, font_no);
3775 		PSL->internal.font[font_no].encoded = true;
3776 	}
3777 	PSL_command (PSL, " F%d } def\n", font_no);
3778 }
3779 
gmtplot_contlabel_debug(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,struct GMT_CONTOUR * G)3780 GMT_LOCAL void gmtplot_contlabel_debug (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, struct GMT_CONTOUR *G) {
3781 	uint64_t row;
3782 	double size[1] = {0.025};
3783 
3784 	/* If called we simply draw the helper lines or points to assist in debug */
3785 
3786 	gmt_setpen (GMT, &G->debug_pen);
3787 	if (G->fixed) {	/* Place a small open circle at each fixed point */
3788 		PSL_setfill (PSL, GMT->session.no_rgb, PSL_OUTLINE);
3789 		for (row = 0; row < (uint64_t)G->f_n; row++)
3790 			PSL_plotsymbol (PSL, G->f_xy[0][row], G->f_xy[1][row], size, PSL_CIRCLE);
3791 	}
3792 	else if (G->crossing) {	/* Draw a thin line */
3793 		uint64_t seg;
3794 		unsigned int *pen = NULL;
3795 		struct GMT_DATASEGMENT *S = NULL;
3796 		for (seg = 0; seg < G->X->n_segments; seg++) {
3797 			S = G->X->table[0]->segment[seg];	/* Current segment */
3798 			pen = gmt_M_memory (GMT, NULL, S->n_rows, unsigned int);
3799 			for (row = 1, pen[0] = PSL_MOVE; row < S->n_rows; row++) pen[row] = PSL_DRAW;
3800 			gmt_plot_line (GMT, S->data[GMT_X], S->data[GMT_Y], pen, S->n_rows, PSL_LINEAR);
3801 			gmt_M_free (GMT, pen);
3802 		}
3803 	}
3804 }
3805 
gmtplot_contlabel_drawlines(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,struct GMT_CONTOUR * G,unsigned int mode)3806 GMT_LOCAL void gmtplot_contlabel_drawlines (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, struct GMT_CONTOUR *G, unsigned int mode) {
3807 	uint64_t seg, k;
3808 	unsigned int *pen = NULL;
3809 	struct GMT_CONTOUR_LINE *L = NULL;
3810 	for (seg = 0; seg < G->n_segments; seg++) {
3811 		L = G->segment[seg];	/* Pointer to current segment */
3812 		if (L->annot && mode == 1) continue; /* Annotated lines done with curved text routine */
3813 		gmt_setpen (GMT, &L->pen);
3814 		pen = gmt_M_memory (GMT, NULL, L->n, unsigned int);
3815 		for (k = 1, pen[0] = PSL_MOVE; k < L->n; k++) pen[k] = PSL_DRAW;
3816 		PSL_comment (PSL, "%s: %s\n", G->line_name, L->name);
3817 		gmt_plot_line (GMT, L->x, L->y, pen, L->n, PSL_LINEAR);
3818 		gmt_M_free (GMT, pen);
3819 	}
3820 }
3821 
gmtplot_contlabel_plotlabels(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,struct GMT_CONTOUR * G,unsigned int mode)3822 GMT_LOCAL void gmtplot_contlabel_plotlabels (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, struct GMT_CONTOUR *G, unsigned int mode) {
3823 	/* mode controls what takes place:
3824 	 * mode = 1: We place all the PSL variables required to use the text for clipping of painting.
3825 	 * mode = 2: We paint the text that is stored in the PSL variables.
3826 	 * mode = 4: We use the text stored in the PSL variables to set up a clip path.  CLipping is turned ON.
3827 	 * mode = 8: We draw the lines.
3828 	 * mode = 16: We turn clip path OFF.
3829 	 * mode = 32: We want rounded rectangles instead of straight rectangular boxes [straight text only].
3830 	 */
3831 	int justify = 0, form = 0, *just = NULL, *node = NULL, *nlabels_per_segment = NULL, *npoints_per_segment = NULL;
3832 	uint64_t k, m, seg, n_points = 0;
3833 	unsigned int n_segments = 0, n_labels = 0, first_point_in_segment = 0, this_seg;
3834 	double *angle = NULL, *xpath = NULL, *ypath = NULL, *xtxt = NULL, *ytxt = NULL;
3835 	char **txt = NULL, **pen = NULL, **fonts = NULL;
3836 	struct GMT_CONTOUR_LINE *L = NULL;
3837 	void *A1 = NULL, *A2 = NULL;
3838 
3839 	form = mode;					/* Which actions to take */
3840 	if (G->box & 4) form |= PSL_TXT_ROUND;		/* Want round box shape */
3841 	if (G->curved_text) form |= PSL_TXT_CURVED;	/* Want text set along curved path */
3842 	if (G->fillbox) form |= PSL_TXT_FILLBOX;	/* Want the box filled */
3843 	if (G->box & 1) form |= PSL_TXT_DRAWBOX;	/* Want box outline */
3844 	if (G->font_label.form & 2)	/* Must supply both font rgb and pen rgb and use charpath and S to fill and outline text */
3845 		form |= (G->font_label.form & 8) ? PSL_TXT_PENFILL : PSL_TXT_FILLPEN;
3846 	if (mode & PSL_TXT_INIT) {	/* Determine and places all PSL attributes */
3847 		char *font = NULL;
3848 		if (G->number_placement && G->n_cont == 1)		/* Special 1-label justification check */
3849 			justify = G->end_just[(G->number_placement+1)/2];	/* Gives index 0 or 1 */
3850 		else
3851 			justify = G->just;
3852 
3853 		for (seg = 0; seg < G->n_segments; seg++) {
3854 			L = G->segment[seg];	/* Pointer to current segment */
3855 			n_segments++;			/* Number of segments */
3856 			n_points += (unsigned int)L->n;		/* Total number of points in all segments so far */
3857 			n_labels += L->n_labels;	/* Number of labels so far */
3858 		}
3859 
3860 		if (n_labels == 0) return;	/* There are no labels */
3861 
3862 		gmt_M_malloc2 (GMT, xpath, ypath, n_points, NULL, double);
3863 		gmt_M_malloc2 (GMT, npoints_per_segment, nlabels_per_segment, n_segments, NULL, int);
3864 		if (G->curved_text) {	/* Must pass node locations of labels */
3865 			node = gmt_M_memory (GMT, NULL, n_labels, int);
3866 			A1 = node;
3867 		}
3868 		else {			/* Must pass x,y locations of labels */
3869 			gmt_M_malloc2 (GMT, xtxt, ytxt, n_points, NULL, double);
3870 			A1 = xtxt;
3871 			A2 = ytxt;
3872 		}
3873 		angle = gmt_M_memory (GMT, NULL, n_labels, double);
3874 		txt   = gmt_M_memory (GMT, NULL, n_labels, char *);
3875 		pen   = gmt_M_memory (GMT, NULL, n_segments, char *);
3876 		fonts = gmt_M_memory (GMT, NULL, n_labels, char *);
3877 		PSL_setfont (PSL, G->font_label.id);
3878 		for (seg = m = this_seg = 0; seg < G->n_segments; seg++) {	/* Process all segments, skip those without labels */
3879 			L = G->segment[seg];	/* Pointer to current segment */
3880 			npoints_per_segment[this_seg] = (int)L->n;	/* Points along this segment path */
3881 			if (this_seg > 0) first_point_in_segment += npoints_per_segment[this_seg-1];		/* First point id in combined path */
3882 			gmt_M_memcpy (&xpath[first_point_in_segment], L->x, L->n, double);	/* Append this segment path to the combined path array */
3883 			gmt_M_memcpy (&ypath[first_point_in_segment], L->y, L->n, double);
3884 			nlabels_per_segment[this_seg] = L->n_labels;		/* Number of labels for this path */
3885 			pen[this_seg] = strdup (PSL_makepen (GMT->PSL, L->pen.width, L->pen.rgb, L->pen.style, L->pen.offset));	/* Get pen PSL setting for this segment */
3886 			for (k = 0; k < L->n_labels; k++, m++) {	/* Process all labels for this segment */
3887 				angle[m] = L->L[k].angle;
3888 				txt[m]   = L->L[k].label;
3889 				if (G->curved_text)	/* Need local node number for text placement */
3890 					node[m]  = (int)L->L[k].node;	/* node is a local index for the relevant segment */
3891 				else {	/* Need coordinate of text placement */
3892 					xtxt[m]    = L->L[k].x;
3893 					ytxt[m]    = L->L[k].y;
3894 				}
3895 				if (G->font_label.form & 2) {	/* Must supply both font rgb and pen rgb and use charpath and S to fill and outline text; see PSL_labels.ps */
3896 					char string[GMT_LEN128] = {""};
3897 					char *F = PSL_makecolor (PSL, L->L[k].rgb);
3898 					char *P = PSL_makepen (PSL, G->font_label.pen.width, G->font_label.pen.rgb, G->font_label.pen.style, G->font_label.pen.offset);
3899 					font = PSL_makefontsize (PSL, G->font_label.size);
3900 					snprintf (string, GMT_LEN128, "{%s} FS %s %s", F, P, font);	/* E.g., "{1 0 0 C} FS 4 W 0 A [] 0 B 667 F5" */
3901 					fonts[m] = strdup (string);
3902 				}
3903 				else {	/* Regular font fill */
3904 					font = PSL_makefont (PSL, G->font_label.size, L->L[k].rgb);	/* e.g., "1 0 0 C 667 F5" */
3905 					fonts[m] = strdup (font);
3906 				}
3907 			}
3908 			this_seg++;
3909 		}
3910 
3911 		PSL_comment (PSL, "Store path and label attributes:\n");
3912 		gmt_textpath_init (GMT, &G->pen, G->rgb);
3913 		PSL_comment (PSL, "Store pens used for each line segment:\n");
3914 		psl_set_txt_array (PSL, "path_pen", pen, n_segments);
3915 		/* While all contours & quoted lines have same justification and font (except maybe color), this is not true of pstext items
3916 		 * so we use separate arrays for both font and justify [here justify and possibly font are constants]  */
3917 		just = gmt_M_memory (GMT, NULL, n_labels, int);
3918 		for (k = 0; k < n_labels; k++)
3919 			just[k] = abs (justify);
3920 		PSL_comment (PSL, "Store text justification for each text label:\n");
3921 		psl_set_int_array   (PSL, "label_justify", just, n_labels);
3922 		PSL_comment (PSL, "Store font setting for each text label:\n");
3923 		psl_set_txt_array   (PSL, "label_font", fonts, n_labels);
3924 		gmt_M_free (GMT, just);
3925 		for (k = 0; k < n_labels; k++)
3926 			gmt_M_str_free (fonts[k]);
3927 		gmt_M_free (GMT, fonts);
3928 	}
3929 	PSL_plottextline (PSL, xpath, ypath, npoints_per_segment, n_segments, A1, A2, txt, angle, nlabels_per_segment, G->font_label.size, justify, G->clearance, form);
3930 	if (mode & PSL_TXT_INIT) {	/* Free up the things we allocated above */
3931 		gmt_M_free (GMT, npoints_per_segment);
3932 		gmt_M_free (GMT, nlabels_per_segment);
3933 		for (k = 0; k < n_segments; k++) gmt_M_str_free (pen[k]);
3934 		gmt_M_free (GMT, pen);
3935 		gmt_M_free (GMT, angle);
3936 		gmt_M_free (GMT, txt);
3937 		gmt_M_free (GMT, xpath);
3938 		gmt_M_free (GMT, ypath);
3939 		if (G->curved_text)
3940 			gmt_M_free (GMT, node);
3941 		else {
3942 			gmt_M_free (GMT, xtxt);
3943 			gmt_M_free (GMT, ytxt);
3944 		}
3945 	}
3946 }
3947 
3948 #if 0
3949 GMT_LOCAL void gmtplot_ellipsoid_name_convert2 (char *inname, char outname[]) {
3950 	/* Convert the ellipsoid names to the slightly different way that they are called in proj4 */
3951 	if (!strcmp(inname, "WGS84"))
3952 		sprintf(outname, "WGS-84");
3953 	else if (!strcmp(inname, "WGS72"))
3954 		sprintf(outname, "WGS-72");
3955 	else if (!strcmp(inname, "WGS66"))
3956 		sprintf(outname, "WGS-66");
3957 	else if (!strcmp(inname, "WGS60"))
3958 		sprintf(outname, "WGS-60");
3959 	else if (!strcmp(inname, "airy"))
3960 		sprintf(outname, "Airy");
3961 	else if (!strcmp(inname, "mod_airy"))
3962 		sprintf(outname, "Airy-Ireland");
3963 	else if (!strcmp(inname, "andrae"))
3964 		sprintf(outname, "Andrae");
3965 	else if (!strcmp(inname, "APL4.9"))
3966 		sprintf(outname, "APL4.9");
3967 	else if (!strcmp(inname, "aust_SA"))
3968 		sprintf(outname, "Australian");
3969 	else if (!strcmp(inname, "bessel"))
3970 		sprintf(outname, "Bessel");
3971 	else if (!strcmp(inname, "bess_nam"))
3972 		sprintf(outname, "Bessel-Namibia");
3973 	else if (!strcmp(inname, "clark66"))
3974 		sprintf(outname, "Clarke-1866");
3975 	else if (!strcmp(inname, "clark80"))
3976 		sprintf(outname, "Clarke-1880");
3977 	else if (!strcmp(inname, "CPM"))
3978 		sprintf(outname, "CPM");
3979 	else if (!strcmp(inname, "delmbr"))
3980 		sprintf(outname, "Delambre");
3981 	else if (!strcmp(inname, "engelis"))
3982 		sprintf(outname, "Engelis");
3983 	else if (!strcmp(inname, "evrst30"))
3984 		sprintf(outname, "Everest-1830");
3985 	else if (!strcmp(inname, "evrst48"))
3986 		sprintf(outname, "Everest-1830-Kertau");
3987 	else if (!strcmp(inname, "evrst56"))
3988 		sprintf(outname, "Everest-1830-Kalianpur");
3989 	else if (!strcmp(inname, "evrstSS"))
3990 		sprintf(outname, "Everest-1830-Timbalai");
3991 	else if (!strcmp(inname, "fschr60"))
3992 		sprintf(outname, "Fischer-1960");
3993 	else if (!strcmp(inname, "fschr60m"))
3994 		sprintf(outname, "Fischer-1960-SouthAsia");
3995 	else if (!strcmp(inname, "fschr68"))
3996 		sprintf(outname, "Fischer-1968");
3997 	else if (!strcmp(inname, "GRS80"))
3998 		sprintf(outname, "GRS-80");
3999 	else if (!strcmp(inname, "GRS67"))
4000 		sprintf(outname, "GRS-67");
4001 	else if (!strcmp(inname, "helmert"))
4002 		sprintf(outname, "Helmert-1906");
4003 	else if (!strcmp(inname, "hough"))
4004 		sprintf(outname, "Hough");
4005 	else if (!strcmp(inname, "intl"))
4006 		sprintf(outname, "Hayford-1909");
4007 	else if (!strcmp(inname, "new_intl"))
4008 		sprintf(outname, "International-1967");
4009 	else if (!strcmp(inname, "MERIT"))
4010 		sprintf(outname, "MERIT-83");
4011 	else if (!strcmp(inname, "krass"))
4012 		sprintf(outname, "Krassovsky");
4013 	else if (!strcmp(inname, "kaula"))
4014 		sprintf(outname, "Kaula");
4015 	else if (!strcmp(inname, "NWL-9D"))
4016 		sprintf(outname, "NWL9D");
4017 	else if (!strcmp(inname, "IAU76"))
4018 		sprintf(outname, "IAG-75");
4019 	else if (!strcmp(inname, "lerch"))
4020 		sprintf(outname, "Lerch");
4021 	else if (!strcmp(inname, "mprts"))
4022 		sprintf(outname, "Maupertius");
4023 	else if (!strcmp(inname, "SEasia"))
4024 		sprintf(outname, "Modified-Fischer-1960");
4025 	else if (!strcmp(inname, "SGS85"))
4026 		sprintf(outname, "SGS-85");
4027 	else if (!strcmp(inname, "plessis"))
4028 		sprintf(outname, "Plessis");
4029 	else if (!strcmp(inname, "walbeck"))
4030 		sprintf(outname, "Walbeck");
4031 	else if (!strcmp(inname, "sphere"))
4032 		sprintf(outname, "Sphere");
4033 	else if (!strcmp(inname, "sphere"))
4034 		sprintf(outname, "FlatEarth");
4035 	else
4036 		outname[0] = '\0';
4037 }
4038 #endif
4039 
gmtlib_ellipsoid_name_convert(char * inname,char outname[])4040 void gmtlib_ellipsoid_name_convert (char *inname, char outname[]) {
4041 	/* Convert the ellipsoid names to the slightly different way that they are called in proj4 */
4042 	if (!strcmp(inname, "WGS-84"))
4043 		sprintf(outname, "WGS84");
4044 	else if (!strcmp(inname, "WGS-72"))
4045 		sprintf(outname, "WGS72");
4046 	else if (!strcmp(inname, "WGS-66"))
4047 		sprintf(outname, "WGS66");
4048 	else if (!strcmp(inname, "WGS-60"))
4049 		sprintf(outname, "WGS60");
4050 	else if (!strcmp(inname, "Airy"))
4051 		sprintf(outname, "airy");
4052 	else if (!strcmp(inname, "Airy-Ireland"))
4053 		sprintf(outname, "mod_airy");
4054 	else if (!strcmp(inname, "Andrae"))
4055 		sprintf(outname, "andrae");
4056 	else if (!strcmp(inname, "APL4.9"))
4057 		sprintf(outname, "APL4.9");
4058 	else if (!strcmp(inname, "Australian"))
4059 		sprintf(outname, "aust_SA");
4060 	else if (!strcmp(inname, "Bessel"))
4061 		sprintf(outname, "bessel");
4062 	else if (!strcmp(inname, "Bessel-Namibia"))
4063 		sprintf(outname, "bess_nam");
4064 	else if (!strcmp(inname, "Clarke-1866"))
4065 		sprintf(outname, "clark66");
4066 	else if (!strcmp(inname, "Clarke-1880"))
4067 		sprintf(outname, "clark80");
4068 	else if (!strcmp(inname, "CPM"))
4069 		sprintf(outname, "CPM");
4070 	else if (!strcmp(inname, "Delambre"))
4071 		sprintf(outname, "delmbr");
4072 	else if (!strcmp(inname, "Engelis"))
4073 		sprintf(outname, "engelis");
4074 	else if (!strcmp(inname, "Everest-1830"))
4075 		sprintf(outname, "evrst30");
4076 	else if (!strcmp(inname, "Everest-1830-Kertau"))
4077 		sprintf(outname, "evrst48");
4078 	else if (!strcmp(inname, "Everest-1830-Kalianpur"))
4079 		sprintf(outname, "evrst56");
4080 	else if (!strcmp(inname, "Everest-1830-Timbalai"))
4081 		sprintf(outname, "evrstSS");
4082 	else if (!strcmp(inname, "Fischer-1960"))
4083 		sprintf(outname, "fschr60");
4084 	else if (!strcmp(inname, "Fischer-1960-SouthAsia"))
4085 		sprintf(outname, "fschr60m");
4086 	else if (!strcmp(inname, "Fischer-1968"))
4087 		sprintf(outname, "fschr68");
4088 	else if (!strcmp(inname, "GRS-80"))
4089 		sprintf(outname, "GRS80");
4090 	else if (!strcmp(inname, "GRS-67"))
4091 		sprintf(outname, "GRS67");
4092 	else if (!strcmp(inname, "Helmert-1906"))
4093 		sprintf(outname, "helmert");
4094 	else if (!strcmp(inname, "Hough"))
4095 		sprintf(outname, "hough");
4096 	else if (!strcmp(inname, "Hayford-1909"))
4097 		sprintf(outname, "intl");
4098 	else if (!strcmp(inname, "International-1967"))
4099 		sprintf(outname, "new_intl");
4100 	else if (!strcmp(inname, "MERIT-83"))
4101 		sprintf(outname, "MERIT");
4102 	else if (!strcmp(inname, "Krassovsky"))
4103 		sprintf(outname, "krass");
4104 	else if (!strcmp(inname, "Kaula"))
4105 		sprintf(outname, "kaula");
4106 	else if (!strcmp(inname, "NWL-9D"))
4107 		sprintf(outname, "NWL9D");
4108 	else if (!strcmp(inname, "IAG-75"))
4109 		sprintf(outname, "IAU76");
4110 	else if (!strcmp(inname, "Lerch"))
4111 		sprintf(outname, "lerch");
4112 	else if (!strcmp(inname, "Maupertius"))
4113 		sprintf(outname, "mprts");
4114 	else if (!strcmp(inname, "Modified-Fischer-1960"))
4115 		sprintf(outname, "SEasia");
4116 	else if (!strcmp(inname, "SGS-85"))
4117 		sprintf(outname, "SGS85");
4118 	else if (!strcmp(inname, "Plessis"))
4119 		sprintf(outname, "plessis");
4120 	else if (!strcmp(inname, "Walbeck"))
4121 		sprintf(outname, "walbeck");
4122 	else if (!strcmp(inname, "Sphere"))
4123 		sprintf(outname, "sphere");
4124 	else if (!strcmp(inname, "FlatEarth"))
4125 		sprintf(outname, "sphere");
4126 	else
4127 		sprintf(outname, "unnamed");
4128 }
4129 
4130 #if 0
4131 /* Used to dump an array to file for debug */
4132 GMT_LOCAL void gmtplot_dumpfile (struct GMT_CTRL *GMT, double *x, double *y, unsigned int *pen, uint64_t n, char *file) {
4133 	FILE *fp = fopen (file, "w");
4134 	uint64_t k;
4135 	unsigned int ps = 0;
4136 	double w;
4137 	for (k = 0; k < n; k++) {
4138 		w = 2.0 * gmt_half_map_width (GMT, y[k]);
4139 		if (pen) {
4140 			ps += pen[k];
4141 			fprintf (fp, "%.16g\t%.16g\t%d\t%.16g\n", x[k], y[k], ps, w);
4142 		}
4143 		else
4144 			fprintf (fp, "%.16g\t%.16g\t%.16g\n", x[k], y[k], w);
4145 			}
4146 	fclose (fp);
4147 }
4148 #endif
4149 
gmtplot_geo_polygon(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n,bool init,const char * comment)4150 GMT_LOCAL uint64_t gmtplot_geo_polygon (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n, bool init, const char *comment) {
4151 	/* When geographic data are plotted, polygons that cross the west map boundary will
4152 	 * sometimes appear on the area bounded by the east map boundary - they "wrap around".
4153 	 * This usually means we have a global map with (east-west) = 360.
4154 	 * This function solves this by determining the polygon outline three times:
4155 	 * First time: Truncate polygon between left and right border
4156 	 * Second time: Find where the polygon jumps and set all the points between jumps to
4157 	 *	       the point on the west boundary at the same latitude.
4158 	 * Third time: Find where the polygon jumps and set all the points between jumps to
4159 	 *	       the point on the east boundary at the same latitude.
4160 	 * In reality it depends on the nature of the first jump in which order we do the
4161 	 * west and east truncation above.
4162 	 * If the polygon is clipped or wraps around at a periodic boundary then we must
4163 	 * be careful how we draw the outline (if selected).  This function only lays down
4164 	 * the paths; filling/outline is controlled by higher powers (gmt_geo_polygons).
4165 	 */
4166 
4167 #define JUMP_L 0
4168 #define JUMP_R 1
4169 #define JUMP_B 0
4170 #define JUMP_T 1
4171 
4172 	uint64_t total = 0;
4173 	double *xp = NULL, *yp = NULL;
4174 	struct PSL_CTRL *PSL= GMT->PSL;
4175 
4176 	if (gmt_M_eq (PSL->current.rgb[PSL_IS_FILL][0], -1.0)) {
4177 		/* Just draw optional outline, no fill, nor pattern */
4178 	}
4179 	else if (gmt_M_is_azimuthal (GMT) || !GMT->current.map.is_world) {	/* Testing without !is_world map to rediscover the original issue */
4180 		/* Because points way outside the map might get close to the antipode we must
4181 		 * clip the polygon first.  The new radial clip handles this by excluding points
4182 		 * beyond the horizon and adding arcs along the boundary between exit points
4183 		 */
4184 
4185 		if ((GMT->current.plot.n = gmt_clip_to_map (GMT, lon, lat, n, &xp, &yp)) == 0) return 0;		/* All points are outside region */
4186 		//gmtplot_dumpfile (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, "raw.txt");
4187 
4188 		if (init) {
4189 			PSL_comment (PSL, "Temporarily set FO to P for complex polygon building\n");
4190 			PSL_command (PSL, "/FO {P}!\n");		/* Temporarily replace FO so we can build a complex path of closed polygons using {P} */
4191 		}
4192 		PSL_comment (PSL, comment);
4193 		PSL_plotpolygon (PSL, xp, yp, (unsigned int)GMT->current.plot.n);	/* Fill Cartesian polygon and possibly draw outline */
4194 		/* Free the memory we are done with */
4195 		gmt_M_free (GMT, xp);
4196 		gmt_M_free (GMT, yp);
4197 		total = GMT->current.plot.n;
4198 	}
4199 	else if (GMT->current.proj.projection_GMT == GMT_TM && gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) {	/* Here, any jumps are in the y-direction */
4200 		uint64_t k, first, i;
4201 		int jump_dir = JUMP_B;
4202 		bool jump, plot_main = true;
4203 		double y_on_border[2] = {GMT->current.proj.rect[YHI], GMT->current.proj.rect[YLO]};
4204 
4205 		/* Here we come for the periodic Transverse Mercator projection where longitude wrapping happens in the y-direction */
4206 
4207 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, n)) == 0) return 0;		/* Convert to (x,y,pen) - return if nothing to do */
4208 		if (init) {
4209 			PSL_comment (PSL, "Temporarily set FO to P for complex polygon building\n");
4210 			PSL_command (PSL, "/FO {P}!\n");		/* Temporarily replace FO so we can build a complex path of closed polygons using {P} */
4211 		}
4212 		PSL_comment (PSL, comment);
4213 		//gmtplot_dumpfile (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, "raw.txt");
4214 
4215 		/* Check if there are any boundary jumps in the data as evidenced by pen up [PSL_MOVE] */
4216 
4217 		jump = (*GMT->current.map.will_it_wrap) (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n, &first);	/* Polygon does indeed wrap */
4218 
4219 		if (!jump) {	/* We happened to avoid the periodic boundary - just paint and return */
4220 			PSL_plotpolygon (PSL, GMT->current.plot.x, GMT->current.plot.y, (unsigned int)GMT->current.plot.n);
4221 			return GMT->current.plot.n;
4222 		}
4223 
4224 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Polygon wraps in y-direction for TM global projection\n");
4225 
4226 		/* Polygon wraps and we will plot it up to three times by truncating the part that would wrap the wrong way.
4227 		 * Here we cannot use the clipped/wrapped polygon to draw outline - that is done at the end, separately.
4228 		 * Note: We may still have problems if a polygon that wraps across the top/bottom boundary also is clipped
4229 		 * by the left or right boundary. */
4230 
4231 		/* Temporary array to hold the modified y values */
4232 
4233 		yp = gmt_M_memory (GMT, NULL, GMT->current.plot.n, double);
4234 
4235 		/* Do the main truncation of bulk of polygon */
4236 
4237 		for (i = 0, jump = false; i < GMT->current.plot.n; i++) {
4238 			if (GMT->current.plot.pen[i] & PSL_MOVE && i) {
4239 				jump = !jump;
4240 				jump_dir = (GMT->current.plot.y[i] > GMT->current.map.half_height) ? JUMP_T : JUMP_B;
4241 			}
4242 			yp[i] = (jump) ? y_on_border[jump_dir] : GMT->current.plot.y[i];
4243 		}
4244 
4245 		//gmtplot_dumpfile (GMT, GMT->current.plot.x, yp, NULL, GMT->current.plot.n, "main.txt");
4246 		if (plot_main) {
4247 			PSL_plotpolygon (PSL, GMT->current.plot.x, yp, (unsigned int)GMT->current.plot.n);	/* Paint the truncated polygon */
4248 			total = GMT->current.plot.n;
4249 		}
4250 
4251 		/* Then do the Bottom truncation since some wrapped pieces might not have been plotted (k > 0 means we found a piece) */
4252 
4253 		jump_dir = (GMT->current.plot.y[first] > GMT->current.map.half_height) ? JUMP_B : JUMP_T;	/* Opposite */
4254 		for (i = k = 0, jump = true; i < GMT->current.plot.n; i++) {
4255 			if ((GMT->current.plot.pen[i] & PSL_MOVE) && i) {
4256 				jump = !jump;
4257 				jump_dir = (GMT->current.plot.y[i] > GMT->current.map.half_height) ? JUMP_T : JUMP_B;
4258 			}
4259 			yp[i] = (jump || jump_dir == JUMP_T) ? y_on_border[JUMP_T]: GMT->current.plot.y[i], k++;
4260 		}
4261 		if (k) {
4262 			//gmtplot_dumpfile (GMT, GMT->current.plot.x, yp, NULL, GMT->current.plot.n, "B.txt");
4263 			PSL_plotpolygon (PSL, GMT->current.plot.x, yp, (unsigned int)GMT->current.plot.n);	/* Paint the truncated polygon */
4264 			total += GMT->current.plot.n;
4265 		}
4266 
4267 		/* Then do the Top truncation since some wrapped pieces might not have been plotted (k > 0 means we found a piece) */
4268 
4269 		jump_dir = (GMT->current.plot.y[first] > GMT->current.map.half_height) ? JUMP_T : JUMP_B;	/* Opposite */
4270 		for (i = k = 0, jump = true; i < GMT->current.plot.n; i++) {
4271 			if ((GMT->current.plot.pen[i] & PSL_MOVE) && i) {
4272 				jump = !jump;
4273 				jump_dir = (GMT->current.plot.y[i] > GMT->current.map.half_height) ? JUMP_T : JUMP_B;
4274 			}
4275 			yp[i] = (jump || jump_dir == JUMP_L) ? y_on_border[JUMP_B] : GMT->current.plot.y[i], k++;
4276 		}
4277 		if (k) {
4278 			//gmtplot_dumpfile (GMT, GMT->current.plot.x, yp, NULL, GMT->current.plot.n, "T.txt");
4279 			PSL_plotpolygon (PSL, GMT->current.plot.x, yp, (unsigned int)GMT->current.plot.n);	/* Paint the truncated polygon */
4280 			total = GMT->current.plot.n;
4281 		}
4282 
4283 		/* Free the memory we are done with */
4284 		gmt_M_free (GMT, yp);
4285 	}
4286 	else {
4287 		uint64_t k, first, i;
4288 		int jump_dir = JUMP_L;
4289 		bool jump, plot_main = true;
4290 		double (*x_on_border[2]) (struct GMT_CTRL *, double) = {NULL, NULL};
4291 
4292 		/* Here we come for all other non-azimuthal projections */
4293 
4294 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, n)) == 0) return 0;		/* Convert to (x,y,pen) - return if nothing to do */
4295 		if (init) {
4296 			PSL_comment (PSL, "Temporarily set FO to P for complex polygon building\n");
4297 			PSL_command (PSL, "/FO {P}!\n");		/* Temporarily replace FO so we can build a complex path of closed polygons using {P} */
4298 		}
4299 		PSL_comment (PSL, comment);
4300 		//gmtplot_dumpfile (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, "raw.txt");
4301 
4302 		if (gmt_M_is_cartesian (GMT, GMT_IN)) {		/* Not geographic data so there are no periodic boundaries to worry about */
4303 			PSL_plotpolygon (PSL, GMT->current.plot.x, GMT->current.plot.y, (unsigned int)GMT->current.plot.n);
4304 			return GMT->current.plot.n;
4305 		}
4306 
4307 		/* Check if there are any boundary jumps in the data as evidenced by pen up [PSL_MOVE] */
4308 
4309 		jump = (*GMT->current.map.will_it_wrap) (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n, &first);	/* Polygon does indeed wrap */
4310 
4311 		if (!jump) {	/* We happened to avoid the periodic boundary - just paint and return */
4312 			PSL_plotpolygon (PSL, GMT->current.plot.x, GMT->current.plot.y, (unsigned int)GMT->current.plot.n);
4313 			return GMT->current.plot.n;
4314 		}
4315 
4316 		/* Polygon wraps and we will plot it up to three times by truncating the part that would wrap the wrong way.
4317 		 * Here we cannot use the clipped/wrapped polygon to draw outline - that is done at the end, separately */
4318 
4319 		/* Temporary array to hold the modified x values */
4320 
4321 		xp = gmt_M_memory (GMT, NULL, GMT->current.plot.n, double);
4322 
4323 		x_on_border[JUMP_R] = gmtlib_left_boundary;	/* Pointers to functions that supply the x-coordinate of boundary for given y */
4324 		x_on_border[JUMP_L] = gmtlib_right_boundary;
4325 
4326 		/* Do the main truncation of bulk of polygon */
4327 
4328 		for (i = 0, jump = false; i < GMT->current.plot.n; i++) {
4329 			if (GMT->current.plot.pen[i] & PSL_MOVE && i) {
4330 				jump = !jump;
4331 				jump_dir = (GMT->current.plot.x[i] > GMT->current.map.half_width) ? JUMP_R : JUMP_L;
4332 			}
4333 			xp[i] = (jump) ? (*x_on_border[jump_dir]) (GMT, GMT->current.plot.y[i]) : GMT->current.plot.x[i];
4334 		}
4335 		if (GMT->current.map.is_world && doubleAlmostEqualZero (GMT->current.plot.y[0], GMT->current.plot.y[GMT->current.plot.n-1]) && !(gmt_M_is_zero (GMT->current.plot.y[0]) || doubleAlmostEqualZero (GMT->current.plot.y[0], GMT->current.proj.rect[YHI])) ) {
4336 			/* Watch for E-W line crossing jumps. We have no defense against these erratic near-pole polygons that are split across two periodic boundaries...
4337 			 * The test checks for horizontal lines NOT at top of bottom of plot, for global maps only.
4338 			 * For an example that triggers this, see test psxy/nearpole.sh and comments therein. PW, 5/29/2018 */
4339 			double w = 1.9 * gmt_half_map_width (GMT, GMT->current.plot.y[0]);
4340 			if (fabs (xp[GMT->current.plot.n-1] - xp[0]) > w) {	/* Does the jump exceed 85% of map width? */
4341 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Truncated wrapped polygon still has E-W jump and was skipped. Please report to developers if plot has artifacts.\n");
4342 				plot_main = false;
4343 			}
4344 		}
4345 		//gmtplot_dumpfile (GMT, xp, GMT->current.plot.y, NULL, GMT->current.plot.n, "main.txt");
4346 		if (plot_main) {
4347 			PSL_plotpolygon (PSL, xp, GMT->current.plot.y, (unsigned int)GMT->current.plot.n);	/* Paint the truncated polygon */
4348 			total = GMT->current.plot.n;
4349 		}
4350 
4351 		/* Then do the Left truncation since some wrapped pieces might not have been plotted (k > 0 means we found a piece) */
4352 
4353 		jump_dir = (GMT->current.plot.x[first] > GMT->current.map.half_width) ? JUMP_L : JUMP_R;	/* Opposite */
4354 		for (i = k = 0, jump = true; i < GMT->current.plot.n; i++) {
4355 			if ((GMT->current.plot.pen[i] & PSL_MOVE) && i) {
4356 				jump = !jump;
4357 				jump_dir = (GMT->current.plot.x[i] > GMT->current.map.half_width) ? JUMP_R : JUMP_L;
4358 			}
4359 			xp[i] = (jump || jump_dir == JUMP_R) ? (*x_on_border[JUMP_R]) (GMT, GMT->current.plot.y[i]) : GMT->current.plot.x[i], k++;
4360 		}
4361 		if (k) {
4362 			//gmtplot_dumpfile (GMT, xp, GMT->current.plot.y, NULL, GMT->current.plot.n, "L.txt");
4363 			PSL_plotpolygon (PSL, xp, GMT->current.plot.y, (unsigned int)GMT->current.plot.n);	/* Paint the truncated polygon */
4364 			total += GMT->current.plot.n;
4365 		}
4366 
4367 		/* Then do the R truncation since some wrapped pieces might not have been plotted (k > 0 means we found a piece) */
4368 
4369 		jump_dir = (GMT->current.plot.x[first] > GMT->current.map.half_width) ? JUMP_R : JUMP_L;	/* Opposite */
4370 		for (i = k = 0, jump = true; i < GMT->current.plot.n; i++) {
4371 			if ((GMT->current.plot.pen[i] & PSL_MOVE) && i) {
4372 				jump = !jump;
4373 				jump_dir = (GMT->current.plot.x[i] > GMT->current.map.half_width) ? JUMP_R : JUMP_L;
4374 			}
4375 			xp[i] = (jump || jump_dir == JUMP_L) ? (*x_on_border[JUMP_L]) (GMT, GMT->current.plot.y[i]) : GMT->current.plot.x[i], k++;
4376 		}
4377 		if (k) {
4378 			//gmtplot_dumpfile (GMT, xp, GMT->current.plot.y, NULL, GMT->current.plot.n, "R.txt");
4379 			PSL_plotpolygon (PSL, xp, GMT->current.plot.y, (unsigned int)GMT->current.plot.n);	/* Paint the truncated polygon */
4380 			total = GMT->current.plot.n;
4381 		}
4382 
4383 		/* Free the memory we are done with */
4384 		gmt_M_free (GMT, xp);
4385 	}
4386 	return (total);
4387 }
4388 
gmtplot_reverse_polygon(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)4389 GMT_LOCAL void gmtplot_reverse_polygon (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
4390 	uint64_t k, n1 = S->n_rows - 1;
4391 	/* Reverse the direction of this polygon, i.e, swap points n-1-k and k */
4392 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Make polygon to clockwise\n");
4393 	for (k = 0; k < S->n_rows/2; k++) {
4394 		gmt_M_double_swap (S->data[GMT_X][k], S->data[GMT_X][n1-k]);
4395 		gmt_M_double_swap (S->data[GMT_Y][k], S->data[GMT_Y][n1-k]);
4396 	}
4397 }
4398 
4399 #if 0
4400 GMT_LOCAL uint64_t gmtplot_geo_polarcap_segment_orig (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, bool first, const char *comment) {
4401 	/* Special treatment for polar caps since they must add int parts of possibly curved periodic boundaries
4402 	 * from the pole up to the intersection with the cap perimeter.  We handle this case separately here.
4403 	 * This is in response to issue # 852. P. Wessel */
4404 	uint64_t k0, perim_n, n_new, m, n = S->n_rows, k;
4405 	double start_lon, stop_lon, yc = 0.0, dx, pole_lat = 90.0 * S->pole;
4406 	double *x_perim = NULL, *y_perim = NULL, *plon = NULL, *plat = NULL;
4407 	static char *pole = "S N";
4408 	int type;
4409 #if 0
4410 	FILE *fp;
4411 #endif
4412 	/* We want this code to be used for the misc. global projections but also global cylindrical or linear(if degrees) maps */
4413 	if (!(gmt_M_is_misc(GMT) || (GMT->current.map.is_world  && (gmt_M_is_cylindrical(GMT) || (gmt_M_is_linear(GMT) && gmt_M_is_geographic(GMT,GMT_IN)))))) return 0;	/* We are only concerned with the global misc projections here */
4414 
4415 	/* Global projection need to handle pole path properly */
4416 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Try to include %c pole in polar cap path\n", pole[S->pole+1]);
4417 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "First longitude = %g.  Last longitude = %g\n", S->data[GMT_X][0], S->data[GMT_X][n-1]);
4418 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "West longitude = %g.  East longitude = %g\n", GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4419 	type = gmtlib_determine_pole (GMT, S->data[GMT_X], S->data[GMT_Y], n);
4420 	if (abs(type) == 2) {	/* The algorithm only works for clockwise polygon so anything CCW we simply reverse... */
4421 		gmtplot_reverse_polygon (GMT, S);
4422 		type = (type == -2) ? -1 : +1;	/* Now just going clockwise */
4423 	}
4424 	start_lon = GMT->common.R.wesn[XHI];
4425 	stop_lon  = GMT->common.R.wesn[XLO];
4426 
4427 	for (k = 1, k0 = 0; k0 == 0 && k < n; k++) {	/* Determine where the perimeter crossing with the west boundary occurs */
4428 		if (k && (GMT->common.R.wesn[XLO]-S->data[GMT_X][k]) >= 0.0 && (GMT->common.R.wesn[XLO]-S->data[GMT_X][k-1]) <= 0.0) k0 = k;
4429 	}
4430 	/* Determine the latitude of that crossing */
4431 	if (k0) {	/* Occurred somewhere along the perimeter between points k0 and k0-1 */
4432 		gmt_M_set_delta_lon (S->data[GMT_X][k0-1], S->data[GMT_X][k0], dx);	/* Handles the 360 jump cases */
4433 		yc = S->data[GMT_Y][k0-1] - (S->data[GMT_Y][k0] - S->data[GMT_Y][k0-1]) * (S->data[GMT_X][k0-1] - GMT->common.R.wesn[XLO]) / dx;
4434 	}
4435 	else	/* Very first point is at the right longitude */
4436 		yc = S->data[GMT_Y][k0];
4437 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Crossing at %g,%g\n", GMT->common.R.wesn[XLO], yc);
4438 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "k at point closest to lon %g is = %d [n = %d]\n", GMT->common.R.wesn[XLO], (int)k0, (int)n);
4439 	/* Then, add path from pole to start longitude, then copy perimeter path, then add path from stop longitude back to pole */
4440 	/* WIth cylindrical projections we may not go all the way to the pole, so we adjust pole_lat based on -R: */
4441 	if (pole_lat < GMT->common.R.wesn[YLO]) pole_lat = GMT->common.R.wesn[YLO];
4442 	if (pole_lat >GMT->common.R.wesn[YHI])  pole_lat = GMT->common.R.wesn[YHI];
4443 	/* 1. Calculate the path from yc to pole: */
4444 	perim_n = gmtlib_lonpath (GMT, start_lon, pole_lat, yc, &x_perim, &y_perim);
4445 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Created path from %g/%g to %g/%g [%d points]\n", start_lon, pole_lat, start_lon, yc, perim_n);
4446 	/* 2. Allocate enough space for new polar cap polygon */
4447 	n_new = 2 * perim_n + n;
4448 	plon = gmt_M_memory (GMT, NULL, n_new, double);
4449 	plat = gmt_M_memory (GMT, NULL, n_new, double);
4450 	/* Start off with the path from the pole to the crossing */
4451 	gmt_M_memcpy (plon, x_perim, perim_n, double);
4452 	gmt_M_memcpy (plat, y_perim, perim_n, double);
4453 	/* Now walk from k0 to the end of polygon, wrapping around if needed */
4454 	m = perim_n;	/* Index of next output point */
4455 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Add perimeter data from k0->n [%d->%d], then 0->k0 [%d]\n", k0, n, k0);
4456 	for (k = k0; k < n; k++, m++) {
4457 		plon[m] = S->data[GMT_X][k];
4458 		plat[m] = S->data[GMT_Y][k];
4459 	}
4460 	for (k = 0; k < k0; k++, m++) {
4461 		plon[m] = S->data[GMT_X][k];
4462 		plat[m] = S->data[GMT_Y][k];
4463 	}
4464 	/* Then add the opposite path to the pole, switching the longitude to stop_lon */
4465 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Add path from %g/%g to %g/%g [%d points]\n", stop_lon, yc, stop_lon, pole_lat, perim_n);
4466 	for (k = perim_n-1; k > 0; k--, m++) {
4467 		plon[m] = stop_lon;
4468 		plat[m] = y_perim[k];
4469 	}
4470 	/* Finally add the duplicate pole at the end of polygon so it is closed */
4471 	plon[m] = stop_lon;
4472 	plat[m++] = pole_lat;
4473 	gmt_M_free (GMT, x_perim);	gmt_M_free (GMT, y_perim);	/* No longer needed */
4474 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "New path has %d points, we allocated %d points\n", m, n_new);
4475 #if 0
4476 	fp = fopen ("shit.txt", "w");
4477 	for (k = 0; k < m; k++) {
4478 		fprintf (fp, "%lg\t%lg\n", plon[k], plat[k]);
4479 	}
4480 	fclose (fp);
4481 #endif
4482 	k = gmtplot_geo_polygon (GMT, plon, plat, m, first, comment);	/* Plot filled polygon [no outline] */
4483 	gmt_M_free (GMT, plon);	gmt_M_free (GMT, plat);			/* No longer needed */
4484 	return (k);	/* Number of points plotted */
4485 }
4486 #endif
4487 
gmtplot_at_pole(double * lat,uint64_t n)4488 GMT_LOCAL bool gmtplot_at_pole (double *lat, uint64_t n) {
4489 	return (lat[0] == lat[n-1] && fabs (lat[0]) == 90.0);
4490 }
4491 
gmtplot_geo_polygon_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,bool add_pole,bool first,const char * comment)4492 GMT_LOCAL uint64_t gmtplot_geo_polygon_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, bool add_pole, bool first, const char *comment) {
4493 	/* Handles the laying down of polygons suitable for filling only; outlines are done separately later.
4494 	 * Polar caps need special treatment in that we must add a detour to the pole.
4495 	 * That detour will not be drawn, only used for fill. However, due to the insanity that is called GIS,
4496 	 * some user polygons may already have artificial lines drawn to the pole in order to work in GIS.
4497 	 * Thus, if we detect such a pole as part of the line then we do NOT add yet another detour.  */
4498 
4499 	uint64_t n = S->n_rows, k;
4500 	double *plon = S->data[GMT_X], *plat = S->data[GMT_Y], t_lat;	/* Default is to plot incoming array as is via plon,plat pointers */
4501 	bool ap = gmtplot_at_pole (plat, n);	/* Is the first and last point exactly at the pole? */
4502 	bool free_memory = false;
4503 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
4504 	if (ap) plon[n-1] = plon[0];	/* Just enforce the same longitude at the pole point */
4505 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Polar cap: %d\n", (int)add_pole);
4506 	if (add_pole) {	/* Make sure there is not already a detour in the data as given */
4507 		double p_lat = SH->pole * 90.0;	/* Latitude of the pole in question */
4508 		bool need_detour = true;	/* Until proven otherwise we assume we must add a detour */
4509 		if (GMT->common.R.oblique) {	/* Determine if any of the 4 map corners are inside this polygon */
4510 			double X, Y;
4511 			gmt_set_inside_mode (GMT, NULL, GMT_IOO_SPHERICAL);
4512 			gmt_xy_to_geo (GMT, &X, &Y, GMT->current.proj.rect[XLO], GMT->current.proj.rect[YLO]);
4513 			GMT->current.proj.corner[0] = gmt_inonout (GMT, X, Y, S);
4514 			gmt_xy_to_geo (GMT, &X, &Y, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YLO]);
4515 			GMT->current.proj.corner[1] = gmt_inonout (GMT, X, Y, S);
4516 			gmt_xy_to_geo (GMT, &X, &Y, GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
4517 			GMT->current.proj.corner[2] = gmt_inonout (GMT, X, Y, S);
4518 			gmt_xy_to_geo (GMT, &X, &Y, GMT->current.proj.rect[XLO], GMT->current.proj.rect[YHI]);
4519 			GMT->current.proj.corner[3] = gmt_inonout (GMT, X, Y, S);
4520 			need_detour = false;	/* Trying a different tack for these cases */
4521 		}
4522 		for (k = 0; need_detour && k < S->n_rows; k++) {	/* Check every point */
4523 			if (doubleAlmostEqual (S->data[GMT_Y][k], p_lat)) {	/* Point is exactly at the pole in question */
4524 				/* We want to distinguish between a path that gently touches the pole and one that has a fake straight detour to the pole.
4525 				 * We assume a fake detour will have the same longitudes for this point and the previous and that they are both either +/-180 or 0. */
4526 				if (k && doubleAlmostEqual (S->data[GMT_X][k], S->data[GMT_X][k-1]) && (doubleAlmostEqual (fabs (S->data[GMT_X][k]), 180.0) || gmt_M_is_zero (S->data[GMT_X][k])))
4527 					need_detour = false;	/* Well, what do you know. Probably arcGIS or some other handicapped program */
4528 			}
4529 		}
4530 		if (!need_detour) {	/* Do not add another detour but process via gmt_geo_polarcap_segment to handle jumps in our polygon */
4531 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Path already had a detour to the pole, skip adding another detour\n");
4532 			add_pole = false;
4533 			n = gmt_geo_polarcap_segment (GMT, S, &plon, &plat);
4534 			if (plon != S->data[GMT_X])	/* Pointer changed so we allocated, must delete below */
4535 				free_memory = true;
4536 		}
4537 	}
4538 	if (add_pole) {	/* If we get here then a detour will be needed */
4539 		if ((n = gmt_geo_polarcap_segment (GMT, S, &plon, &plat)) == 0) {	/* Not a global map */
4540 			/* Here we must detour to the N or S pole, then resample the path */
4541 			n = S->n_rows + 2;	/* Add new first and last point to connect to the pole */
4542 			plon = gmt_M_memory (GMT, NULL, n, double);	/* This memory must be freed below */
4543 			plat = gmt_M_memory (GMT, NULL, n, double);
4544 			free_memory = true;
4545 			t_lat = SH->pole * 90.0;	/* This is presumably the correct pole, but could fail if just touching the pole */
4546 			if (S->data[GMT_Y][0] * t_lat < 0.0) t_lat = -t_lat;	/* Well, I'll be damned... */
4547 			plat[0] = plat[n-1] = t_lat;
4548 			plon[0] = S->data[GMT_X][0];
4549 			plon[n-1] = S->data[GMT_X][S->n_rows-1];
4550 			gmt_M_memcpy (&plon[1], S->data[GMT_X], S->n_rows, double);
4551 			gmt_M_memcpy (&plat[1], S->data[GMT_Y], S->n_rows, double);
4552 			if (GMT->current.map.path_mode == GMT_RESAMPLE_PATH && (n = gmt_fix_up_path (GMT, &plon, &plat, n, 0.0, 0)) == 0) {
4553 				gmt_M_free (GMT, plon);
4554 				gmt_M_free (GMT, plat);
4555 				return 0;
4556 			}
4557 		}
4558 		else if (plon != S->data[GMT_X])	/* Pointer changed so we allocated, must delete below */
4559 			free_memory = true;
4560 	}
4561 	k = gmtplot_geo_polygon (GMT, plon, plat, n, first, comment);	/* Plot filled polygon [no outline] */
4562 	if (free_memory) {	/* Delete what we allocated */
4563 		gmt_M_free (GMT, plon);
4564 		gmt_M_free (GMT, plat);
4565 	}
4566 	return (k);	/* Number of points plotted */
4567 }
4568 
gmtplot_inch_to_degree_scale(struct GMT_CTRL * GMT,double lon0,double lat0,double azimuth)4569 GMT_LOCAL float gmtplot_inch_to_degree_scale (struct GMT_CTRL *GMT, double lon0, double lat0, double azimuth) {
4570 	/* Used to determine delta radius in degrees for thickness of vector lines to be drawn as small or
4571 	 * great circles.  We must convert pen thickness to some sense of spherical degrees.
4572 	 * Determine the map scale at (lon, lat) in a direction normal to the vector and use that
4573 	 * to scale items in inches to spherical degrees.  We repeat this for all locations except
4574 	 * if the projection is perspective, when we pick the center as the map distortion will be the least here.
4575 	 * This scaling is approximate only but needed to convert geovector head lengths to degrees. */
4576 
4577 	double tlon, tlat, x0, y0, dx, x1, y1, length;
4578 	float scale;
4579 
4580 	length = 0.001 * (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]);		/* 0.1 percent of latitude extent is fairly small */
4581 	gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);					/* Get map position in inches for close point */
4582 	gmtlib_get_point_from_r_az (GMT, lon0, lat0, length, azimuth-90.0, &tlon, &tlat);	/* ANearby arbitrary 2nd point in direction normal to vector at (lon0,lat0) */
4583 	gmt_geo_to_xy (GMT, tlon, tlat, &x1, &y1);					/* Get map position in inches for close point */
4584 	dx = fabs (x1 - x0);
4585 	if (dx > (0.25 * GMT->current.map.half_width)) dx = GMT->current.map.width - dx;
4586 	scale = (float) (length / hypot (dx, y1 - y0));				/* This scales a length in inches to degrees, approximately */
4587 	return (scale);
4588 }
4589 
gmtplot_great_circle_arc(struct GMT_CTRL * GMT,double * A,double * B,double step,bool longway,double ** xp,double ** yp)4590 GMT_LOCAL uint64_t gmtplot_great_circle_arc (struct GMT_CTRL *GMT, double *A, double *B, double step, bool longway, double **xp, double **yp) {
4591 	/* Given vectors A and B, return great circle path sampled every step.  Shorest path is selected unless longway is true */
4592 	/* Determine unit vector pole of great circle or use the one given by small circle pole and its opening rot */
4593 	uint64_t k, n;
4594 	double P[3], X[3], R[3][3], R0[3][3], c, w, *xx = NULL, *yy = NULL;
4595 
4596 	gmt_cross3v (GMT, A, B, P);	/* Parallel to rotation pole */
4597 	gmt_normalize3v (GMT, P);		/* Rotation pole unit vector */
4598 	c = d_acosd (gmt_dot3v (GMT, A, B));	/* opening angle in degrees */
4599 
4600 	if (longway) {	/* Want to go the long way */
4601 		c = 360.0 - c;
4602 		P[0] = -P[0], P[1] = -P[1], P[2] = -P[2];
4603 	}
4604 	if (gmt_M_is_zero (step)) step = GMT->current.map.path_step;	/* Use default map-step if given as 0 */
4605 	n = lrint (ceil (c / step)) + 1;	/* Number of segments needed for smooth curve from A to B inclusive */
4606 	step = D2R * c / (n - 1);	/* Adjust step for exact fit, convert to radians */
4607 	gmt_M_malloc2 (GMT, xx, yy, n, NULL, double);	/* Allocate space for arrays */
4608 	gmtlib_init_rot_matrix (R0, P);			/* Get partial rotation matrix since no actual angle is applied yet */
4609 	for (k = 0; k < n; k++) {	/* March along the arc */
4610 		w = k * step;					/* Opening angle from A to this point X */
4611 		gmt_M_memcpy (R, R0, 9, double);			/* Get a copy of the "0-angle" rotation matrix */
4612 		gmtlib_load_rot_matrix (w, R, P);			/* Build the actual rotation matrix for this angle */
4613 		gmt_matrix_vect_mult (GMT, 3U, R, A, X);			/* Rotate point A towards B and get X */
4614 		gmt_cart_to_geo (GMT, &yy[k], &xx[k], X, true);	/* Get lon/lat of this point along arc */
4615 	}
4616 	*xp = xx;	*yp = yy;
4617 	return (n);
4618 }
4619 
gmtplot_small_circle_arc(struct GMT_CTRL * GMT,double * A,double step,double P[],double rot,double ** xp,double ** yp)4620 GMT_LOCAL uint64_t gmtplot_small_circle_arc (struct GMT_CTRL *GMT, double *A, double step, double P[], double rot, double **xp, double **yp) {
4621 	/* Given vectors A and B, return small circle path sampled every step. */
4622 	/* Use small circle pole P and its opening rot */
4623 	uint64_t k, n;
4624 	double X[3], R[3][3], R0[3][3], w, *xx = NULL, *yy = NULL;
4625 
4626 	if (step == 0 || gmt_M_is_zero (step)) step = GMT->current.map.path_step;	/* Use default map-step if given as 0 */
4627 	n = lrint (ceil (fabs (rot) / step)) + 1;	/* Number of segments needed for smooth curve from A to B inclusive */
4628 	step = D2R * rot / (n - 1);	/* Adjust step for exact fit, convert to radians */
4629 	gmt_M_malloc2 (GMT, xx, yy, n, NULL, double);	/* Allocate space for arrays */
4630 	gmtlib_init_rot_matrix (R0, P);			/* Get partial rotation matrix since no actual angle is applied yet */
4631 	for (k = 0; k < n; k++) {	/* March along the arc */
4632 		w = k * step;					/* Opening angle from A to this point X */
4633 		gmt_M_memcpy (R, R0, 9, double);			/* Get a copy of the "0-angle" rotation matrix */
4634 		gmtlib_load_rot_matrix (w, R, P);			/* Build the actual rotation matrix for this angle */
4635 		gmt_matrix_vect_mult (GMT, 3U, R, A, X);			/* Rotate point A towards B and get X */
4636 		gmt_cart_to_geo (GMT, &yy[k], &xx[k], X, true);	/* Get lon/lat of this point along arc */
4637 	}
4638 	*xp = xx;	*yp = yy;
4639 	return (n);
4640 }
4641 
gmtplot_get_local_scale(struct GMT_CTRL * GMT,double lon0,double lat0,double length,double azimuth)4642 GMT_LOCAL double gmtplot_get_local_scale (struct GMT_CTRL *GMT, double lon0, double lat0, double length, double azimuth) {
4643 	/* Determine the local scale at lon0,lat in the direction azimuth using a test distance length in degrees.
4644 	 * The scale returned can be used to convert a map distance in inch to great circle degrees.
4645 	 * This is approximate only. */
4646 
4647 	double tlon, tlat, x0, y0, x1, y1;
4648 	gmtlib_get_point_from_r_az (GMT, lon0, lat0, length, azimuth, &tlon, &tlat);	/* Arbitrary 2nd point near (lon0,lat0) in the azimuth direction */
4649 	gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);	/* Get map position in inches for (lon0,lat0) */
4650 	gmt_geo_to_xy (GMT, tlon, tlat, &x1, &y1);	/* Get map position in inches for close point */
4651 	return (length / hypot (x1 - x0, y1 - y0));	/* This scales a length in inches to degrees, approximately */
4652 }
4653 
gmtplot_circle_pen_poly(struct GMT_CTRL * GMT,double * A,double * B,bool longway,double rot,struct GMT_PEN * pen,struct GMT_SYMBOL * S,struct GMT_CIRCLE * C,double scale)4654 GMT_LOCAL void gmtplot_circle_pen_poly (struct GMT_CTRL *GMT, double *A, double *B, bool longway, double rot, struct GMT_PEN *pen, struct GMT_SYMBOL *S, struct GMT_CIRCLE *C, double scale) {
4655 	/* Given vectors A and B, return a small circle polygon path sampled every step that approximates a pen of given width
4656 	 * drawn on the map.  Use small circle pole and its opening rot */
4657 	uint64_t k, n, n2;
4658 	double Ai[3], Ao[3], Px[3], X[3], R[3][3], R0[3][3], w, step;
4659 	struct GMT_DATASEGMENT *L = NULL;
4660 	gmt_M_unused(B);
4661 
4662 	gmt_cross3v (GMT, A, C->P, Px);	/* Px is Pole to plane through A and P  */
4663 	gmt_normalize3v (GMT, Px);			/* Rotation pole unit vector */
4664 	/* Rotate A back/fore by rotation angle of +/- pen halfwidth about Px */
4665 	w = 0.5 * scale * pen->width * GMT->session.u2u[GMT_PT][GMT_INCH] * S->v.scale;		/* Half-width of pen in degrees */
4666 	gmt_make_rot_matrix2 (GMT, Px, +w, R);		/* Rotation of rot_v degrees about pole P */
4667 	gmt_matrix_vect_mult (GMT, 3U, R, A, Ai);	/* Get Ai = R * A */
4668 	gmt_make_rot_matrix2 (GMT, Px, -w, R);		/* Rotation of rot_v degrees about pole P */
4669 	gmt_matrix_vect_mult (GMT, 3U, R, A, Ao);	/* Get Ao = R * A */
4670 	if (longway) rot = 360.0 - rot;
4671 
4672 	step = GMT->current.map.path_step;		/* Use default map-step if given as 0 */
4673 	n = lrint (ceil (fabs (rot) / step)) + 1;	/* Number of segments needed for smooth curve from A to B inclusive */
4674 	step = D2R * rot / (n - 1);			/* Adjust step for exact fit, convert to radians */
4675 	L = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, 2*n+1, 2, NULL, NULL);	/* Allocate polygon to draw filled path */
4676 	n2 = 2*n-1;
4677 	gmtlib_init_rot_matrix (R0, C->P);			/* Get partial rotation matrix since no actual angle is applied yet */
4678 	for (k = 0; k < n; k++) {	/* March along the arc */
4679 		w = k * step;					/* Opening angle from A to this point X */
4680 		gmt_M_memcpy (R, R0, 9U, double);			/* Get a copy of the "0-angle" rotation matrix */
4681 		gmtlib_load_rot_matrix (w, R, C->P);			/* Build the actual rotation matrix for this angle */
4682 		gmt_matrix_vect_mult (GMT, 3U, R, Ai, X);		/* Rotate point Ai towards B and get X */
4683 		gmt_cart_to_geo (GMT, &L->data[GMT_Y][k], &L->data[GMT_X][k], X, true);	/* Get lon/lat of this point along arc */
4684 		gmt_matrix_vect_mult (GMT, 3U, R, Ao, X);		/* Rotate point Ai towards B and get X */
4685 		gmt_cart_to_geo (GMT, &L->data[GMT_Y][n2-k], &L->data[GMT_X][n2-k], X, true);	/* Get lon/lat of this point along arc */
4686 	}
4687 	L->data[GMT_X][2*n] = L->data[GMT_X][0];	/* Explicitly close the polygon */
4688 	L->data[GMT_Y][2*n] = L->data[GMT_Y][0];
4689 
4690 	/* Plot pen as a closed filled polygon without outline */
4691 	PSL_command (GMT->PSL, "V\n");
4692 	gmt_setpen (GMT, pen);		/* Set pen width just so later setpen's will work */
4693 	PSL_setfill (GMT->PSL, pen->rgb, 0);	/* Fill, no outline */
4694 	gmt_geo_polygons (GMT, L);	/* "Draw" the line */
4695 	PSL_command (GMT->PSL, "U\n");
4696 
4697 	gmt_free_segment (GMT, &L);
4698 }
4699 
gmtplot_gcircle_sub(struct GMT_CTRL * GMT,double lon0,double lat0,double azimuth,double length,struct GMT_SYMBOL * S,struct GMT_CIRCLE * C)4700 GMT_LOCAL void gmtplot_gcircle_sub (struct GMT_CTRL *GMT, double lon0, double lat0, double azimuth, double length, struct GMT_SYMBOL *S, struct GMT_CIRCLE *C) {
4701 	/* We must determine points A and B, whose great-circle connector is the arc we seek to draw */
4702 
4703 	int justify = PSL_vec_justify (S->v.status);	/* Return justification as 0-3 */
4704 	double x, y;
4705 	gmt_M_memset (C, 1, struct GMT_CIRCLE);	/* Set all to zero */
4706 
4707 	if (S->v.status & PSL_VEC_JUST_S) { /* Was given coordinates of A and B */
4708 		justify = 3;
4709 	}
4710 	switch (justify) {	/* A and B depends on chosen justification */
4711 		case 0: /* Was given coordinates of A; determine B */
4712 			C->lon[0] = lon0;	C->lat[0] = lat0;
4713 			gmt_geo_to_cart (GMT, C->lat[0], C->lon[0], C->A, true);
4714 			C->r0 = C->r = length / GMT->current.proj.DIST_KM_PR_DEG;	/* Arch length in spherical degrees */
4715 			if (C->r > 180.0) {C->longway = true; C->r -= 180.0;}	/* Temporarily adjust if arcs > 180 degrees are chosen */
4716 			gmtlib_get_point_from_r_az (GMT, C->lon[0], C->lat[0], C->r, azimuth, &C->lon[1], &C->lat[1]);
4717 			if (C->longway) C->lon[1] += 180.0, C->lat[1] = -C->lat[1];	/* Undo adjustment */
4718 			gmt_geo_to_cart (GMT, C->lat[1], C->lon[1], C->B, true);	/* Get B */
4719 			break;
4720 		case 1: /* Was given coordinates of halfway point; determine A and B */
4721 			C->r0 = C->r = length / GMT->current.proj.DIST_KM_PR_DEG;	/* Arch length in spherical degrees */
4722 			if (C->r > 180.0) C->longway = true;	/* Temporarily adjust if arcs > 180 degrees are chosen */
4723 			gmtlib_get_point_from_r_az (GMT, lon0, lat0, 0.5*C->r, azimuth, &C->lon[1], &C->lat[1]);
4724 			gmt_geo_to_cart (GMT, C->lat[1], C->lon[1], C->B, true);	/* Get B */
4725 			gmtlib_get_point_from_r_az (GMT, lon0, lat0, 0.5*C->r, azimuth+180.0, &x, &y);
4726 			C->lon[0] = x;	C->lat[0] = y;	/* Replace the original A point */
4727 			gmt_geo_to_cart (GMT, C->lat[0], C->lon[0], C->A, true);	/* Get A */
4728 			break;
4729 		case 2: /* Was given coordinates of B point; determine A */
4730 			C->lon[0] = lon0;	C->lat[0] = lat0;
4731 			gmt_geo_to_cart (GMT, C->lat[0], C->lon[0], C->B, true);
4732 			C->r0 = C->r = length / GMT->current.proj.DIST_KM_PR_DEG;	/* Arch length in spherical degrees */
4733 			if (C->r > 180.0) {C->longway = true; C->r -= 180.0;}	/* Temporarily adjust if arcs > 180 degrees are chosen */
4734 			gmtlib_get_point_from_r_az (GMT, C->lon[0], C->lat[0], C->r, azimuth+180.0, &C->lon[1], &C->lat[1]);
4735 			if (C->longway) C->lon[1] += 180.0, C->lat[1] = -C->lat[1];	/* Undo adjustment */
4736 			gmt_geo_to_cart (GMT, C->lat[1], C->lon[1], C->A, true);	/* Get A */
4737 			gmt_M_double_swap (C->lon[0], C->lon[1]);	gmt_M_double_swap (C->lat[0], C->lat[1]);	/* Now A is first and B is second */
4738 			break;
4739 		case 3: /* Was given coordinates of B instead of azimuth and length; can never be longway */
4740 			C->lon[0] = lon0;	C->lat[0] = lat0;
4741 			C->lat[1] = length;	C->lon[1] = azimuth;
4742 			gmt_geo_to_cart (GMT, C->lat[0], C->lon[0], C->A, true);	/* Get A */
4743 			gmt_geo_to_cart (GMT, C->lat[1], C->lon[1], C->B, true);	/* Get B */
4744 			C->r0 = C->r = d_acosd (gmt_dot3v (GMT, C->A, C->B));		/* Arc length in degrees */
4745 			break;
4746 	}
4747 	gmt_cross3v (GMT, C->A, C->B, C->P);	/* Rotation pole */
4748 	gmt_normalize3v (GMT, C->P);		/* Rotation pole unit vector */
4749 
4750 	if (C->longway) {	/* Want to go the long way */
4751 		C->P[0] = -C->P[0], C->P[1] = -C->P[1], C->P[2] = -C->P[2];
4752 	}
4753 }
4754 
gmtplot_scircle_sub(struct GMT_CTRL * GMT,double lon0,double lat0,double angle_1,double angle_2,struct GMT_SYMBOL * S,struct GMT_CIRCLE * C)4755 GMT_LOCAL void gmtplot_scircle_sub (struct GMT_CTRL *GMT, double lon0, double lat0, double angle_1, double angle_2, struct GMT_SYMBOL *S, struct GMT_CIRCLE *C) {
4756 	/* We must determine points A and B, whose small-circle connector about pole P is the arc we seek to draw */
4757 
4758 	int justify = PSL_vec_justify (S->v.status);	/* Return justification as 0-3 */
4759 	double R[3][3], M[3];
4760 	gmt_M_memset (C, 1, struct GMT_CIRCLE);	/* Set all to zero */
4761 	/* Requires the rotation matrix for pole S->v.pole */
4762 
4763 	/* Here angle_1, angle_2 are not necessarily that, depending on S->v.status:
4764 	 * S->v.pole & PSL_VEC_ANGLES : angle_1 is opening angle1 and angle_2 is opening angle2 about the pole.
4765 	 * Otherwise:	angle_2 is the length of the arc in km */
4766 	gmt_geo_to_cart (GMT, lat0, lon0, M, true);	/* Given input point */
4767 	gmt_geo_to_cart (GMT, S->v.pole[GMT_Y], S->v.pole[GMT_X], C->P, true);
4768 	C->colat = d_acosd (gmt_dot3v (GMT, M, C->P));	/* Colatitude of input point relative to pole, in degrees */
4769 
4770 	if (S->v.status & PSL_VEC_ANGLES) {
4771 		/* Was given the two opening angles; compute A and B accordingly */
4772 		gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], angle_1, R);
4773 		gmt_matrix_vect_mult (GMT, 3U, R, M, C->A);	/* Get A */
4774 		gmt_cart_to_geo (GMT, &C->lat[0], &C->lon[0], C->A, true);
4775 		gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], angle_2, R);
4776 		gmt_matrix_vect_mult (GMT, 3U, R, M, C->B);	/* Get B */
4777 		gmt_cart_to_geo (GMT, &C->lat[1], &C->lon[1], C->B, true);
4778 		C->rot = C->r0 = C->r = angle_2 - angle_1;
4779 	}
4780 	else {
4781 		/* Here A, B, or midpoint was given, + the arc length via angle_2 */
4782 		/* Determine co-latitude for this point */
4783 		C->rot = C->r0 = C->r = (angle_1 / GMT->current.proj.DIST_KM_PR_DEG) / sind (C->colat);	/* Opening angle in spherical degrees */
4784 		switch (justify) {	/* A and B depends on chosen justification */
4785 			case 0: /* Was given coordinates of A; determine B */
4786 				gmt_M_memcpy (C->A, M, 3, double);
4787 				C->lon[0] = lon0;	C->lat[0] = lat0;
4788 				if (C->r > 180.0) {C->longway = true; C->r -= 180.0;}	/* Temporarily adjust if arcs > 180 degrees are chosen */
4789 				/* Rotate A by C->r0 degrees about P to get B */
4790 				gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], C->r0, R);
4791 				gmt_matrix_vect_mult (GMT, 3U, R, C->A, C->B);	/* Get B */
4792 				gmt_cart_to_geo (GMT, &C->lat[1], &C->lon[1], C->B, true);
4793 				break;
4794 			case 1: /* Was given coordinates of halfway point; determine A and B */
4795 				if (C->r > 180.0) C->longway = true;	/* Temporarily adjust if arcs > 180 degrees are chosen */
4796 				/* Rotate M by -C->r0/2 degrees about P to get A */
4797 				gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], -0.5 * C->r0, R);
4798 				gmt_matrix_vect_mult (GMT, 3U, R, M, C->A);	/* Get A */
4799 				gmt_cart_to_geo (GMT, &C->lat[0], &C->lon[0], C->A, true);
4800 				/* Rotate M by +C->r0/2 degrees about P to get B */
4801 				gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], +0.5 * C->r0, R);
4802 				gmt_matrix_vect_mult (GMT, 3U, R, M, C->B);	/* Get B */
4803 				gmt_cart_to_geo (GMT, &C->lat[1], &C->lon[1], C->B, true);
4804 				break;
4805 			case 2: /* Was given coordinates of B point; determine A */
4806 				gmt_M_memcpy (C->B, M, 3, double);
4807 				C->lon[1] = lon0;	C->lat[1] = lat0;
4808 				if (C->r > 180.0) {C->longway = true; C->r -= 180.0;}	/* Temporarily adjust if arcs > 180 degrees are chosen */
4809 				/* Rotate B by -C->r0 degrees about P to get A */
4810 				gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], -C->r0, R);
4811 				gmt_matrix_vect_mult (GMT, 3U, R, C->B, C->A);	/* Get A */
4812 				gmt_cart_to_geo (GMT, &C->lat[0], &C->lon[0], C->A, true);
4813 				break;
4814 		}
4815 	}
4816 }
4817 
gmtplot_smallcircle_az(struct GMT_CTRL * GMT,double P[],struct GMT_SYMBOL * S)4818 GMT_LOCAL double gmtplot_smallcircle_az (struct GMT_CTRL *GMT, double P[], struct GMT_SYMBOL *S) {
4819 	/* Compute the azimuth at P along small circle given pole */
4820 	double R[3][3], X[3], xlon1, xlat1, xlon2, xlat2, az;
4821 	/* Make rotation matrix for a +0.005 degree rotation */
4822 	gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], -0.005, R);
4823 	gmt_matrix_vect_mult (GMT, 3U, R, P, X);	/* Get point really close to P along small circle */
4824 	gmt_cart_to_geo (GMT, &xlat1, &xlon1, X, true);	/* Get coordinates of X */
4825 	gmt_make_rot_matrix (GMT, S->v.pole[GMT_X], S->v.pole[GMT_Y], +0.005, R);
4826 	gmt_matrix_vect_mult (GMT, 3U, R, P, X);	/* Get point really close to P along small circle */
4827 	gmt_cart_to_geo (GMT, &xlat2, &xlon2, X, true);	/* Get coordinates of X */
4828 	az = gmt_az_backaz (GMT, xlon1, xlat1, xlon2, xlat2, false);	/* Compute the azimuth from P to X at A */
4829 	return (az);
4830 }
4831 
gmtplot_plot_vector_head(struct GMT_CTRL * GMT,double * xp,double * yp,uint64_t n,struct GMT_SYMBOL * S)4832 GMT_LOCAL void gmtplot_plot_vector_head (struct GMT_CTRL *GMT, double *xp, double *yp, uint64_t n, struct GMT_SYMBOL *S) {
4833 	/* PW: Plots the polygon that makes up a vector head.  Because sometimes these head stick
4834 	 * across a periodic boundary we must check if that is the case and plot the two parts separately.
4835 	 * When that is the case we cannot draw the outline of the two new polygons since we wish to show
4836 	 * the heads as "clipped" by the boundary; hence all the rigamorole below. */
4837 	uint64_t start = 0, nin = 0;
4838 	unsigned int n_use, *pin = NULL;	/* Copy of the pen moves */
4839 	unsigned int cap = GMT->PSL->internal.line_cap;
4840 	double *xin = NULL, *yin = NULL;	/* Temp vector with possibly clipped x,y line returned by gmt_geo_to_xy_line */
4841 	if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, xp, yp, n)) == 0) return;	/* All outside, or use plot.x|y array */
4842 	PSL_setlinecap (GMT->PSL, PSL_SQUARE_CAP);	/* In case there are clipped heads and we want to do the best we can with the lines */
4843 	n = GMT->current.plot.n;	/* Possibly fewer points */
4844 	if (PSL_vec_outline (S->v.status)) {
4845 		bool close = (GMT->current.plot.n > 2 && gmt_polygon_is_open (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n));
4846 		nin = n;
4847 		PSL_command (GMT->PSL, "O0\n");	/* Temporary turn off outline; must draw outline separately when head is split */
4848 		if (close) nin++;
4849 		xin = gmt_M_memory (GMT, NULL, nin, double);
4850 		yin = gmt_M_memory (GMT, NULL, nin, double);
4851 		pin = gmt_M_memory (GMT, NULL, nin, unsigned int);
4852 		gmt_M_memcpy (xin, GMT->current.plot.x, n, double);
4853 		gmt_M_memcpy (yin, GMT->current.plot.y, n, double);
4854 		gmt_M_memcpy (pin, GMT->current.plot.pen, n, unsigned int);
4855 		if (close) {	/* Explicitly close the polygon outline */
4856 			xin[n] = xin[0];
4857 			yin[n] = yin[0];
4858 		}
4859 	}
4860 
4861 	if ((*GMT->current.map.will_it_wrap) (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n, &start)) {	/* Polygon does indeed wrap */
4862 		double *xtmp = NULL, *ytmp = NULL;	/* Temp vector for map truncating */
4863 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Vector head polygon will wrap at periodic boundary and will be split into two sections\n");
4864 		xtmp = gmt_M_memory (GMT, NULL, n, double);
4865 		ytmp = gmt_M_memory (GMT, NULL, n, double);
4866 		gmt_M_memcpy (xtmp, GMT->current.plot.x, n, double);
4867 		gmt_M_memcpy (ytmp, GMT->current.plot.y, n, double);
4868 		/* First truncate against left border */
4869 		GMT->current.plot.n = gmt_map_truncate (GMT, xtmp, ytmp, n, start, -1);
4870 		n_use = (unsigned int)gmt_compact_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n, false, 0);
4871 		PSL_beginclipping (GMT->PSL, GMT->current.plot.x, GMT->current.plot.y, (int)n_use, GMT->session.no_rgb, 3);
4872 		PSL_plotpolygon (GMT->PSL, GMT->current.plot.x, GMT->current.plot.y, (int)n_use);
4873 		if (PSL_vec_outline (S->v.status)) gmt_plot_line (GMT, xin, yin, pin, nin, PSL_LINEAR);
4874 		PSL_endclipping (GMT->PSL, 1);
4875 		/* Then truncate against right border */
4876 		GMT->current.plot.n = gmt_map_truncate (GMT, xtmp, ytmp, n, start, +1);
4877 		n_use = (unsigned int)gmt_compact_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.n, false, 0);
4878 		PSL_beginclipping (GMT->PSL, GMT->current.plot.x, GMT->current.plot.y, (int)n_use, GMT->session.no_rgb, 3);
4879 		PSL_plotpolygon (GMT->PSL, GMT->current.plot.x, GMT->current.plot.y, (int)n_use);
4880 		if (PSL_vec_outline (S->v.status)) gmt_plot_line (GMT, xin, yin, pin, nin, PSL_LINEAR);
4881 		PSL_endclipping (GMT->PSL, 1);
4882 		gmt_M_free (GMT, xtmp);		gmt_M_free (GMT, ytmp);
4883 	}
4884 	else {	/* No wrapping but may be clipped */
4885 		PSL_beginclipping (GMT->PSL, GMT->current.plot.x, GMT->current.plot.y, (int)GMT->current.plot.n, GMT->session.no_rgb, 3);
4886 		PSL_plotpolygon (GMT->PSL, GMT->current.plot.x, GMT->current.plot.y, (int)GMT->current.plot.n);
4887 		if (PSL_vec_outline (S->v.status)) gmt_plot_line (GMT, xin, yin, pin, nin, PSL_LINEAR);
4888 		PSL_endclipping (GMT->PSL, 1);
4889 	}
4890 	PSL_setlinecap (GMT->PSL, cap);
4891 	if (PSL_vec_outline (S->v.status)) {	/* Turn on outline again and free temp memory */
4892 		PSL_command (GMT->PSL, "O1\n");
4893 		gmt_M_free (GMT, xin);
4894 		gmt_M_free (GMT, yin);
4895 		gmt_M_free (GMT, pin);
4896 	}
4897 }
4898 
gmtplot_geo_vector_smallcircle(struct GMT_CTRL * GMT,double lon0,double lat0,double azimuth,double length,struct GMT_PEN * ppen,struct GMT_SYMBOL * S)4899 GMT_LOCAL unsigned int gmtplot_geo_vector_smallcircle (struct GMT_CTRL *GMT, double lon0, double lat0, double azimuth, double length, struct GMT_PEN *ppen, struct GMT_SYMBOL *S) {
4900 	/* Draws a small-circle vector with or without heads, etc. There are some complications to consider:
4901 	 * When there are no heads it is simple.  If +n is active we may shrink the line thickness.
4902 	 * With heads there are these cases:
4903 	 * Head length is longer than 90% of the vector length.  We then skip the head and return 1
4904 	 * +n<norm> is in effect.  We shrink vector pen and head length.  Still, the shrunk head
4905 	 * may be longer than 90% of the vector length.  We then shrink head (not pen) further and return 2
4906 	*/
4907 
4908 	uint64_t n1, n2, n, add;
4909 	int heads, side[2], outline = 0;
4910 	unsigned int warn = 0;
4911 	size_t n_alloc;
4912 	bool perspective, pure;
4913 	double P[3], Pa[3], Ax[3], Bx[3], Ax2[3], Bx2[3], R[3][3], h_length_limit, max_length;
4914 	double dr[2] = {0.0, 0.0}, az[2] = {0.0, 0.0}, oaz[2] = {0.0, 0.0}, scl[2];
4915 	double da = 0.0, dshift[2] = {0.0, 0.0}, s = 1.0, s1 = 1.0, s2 = 1.0, olon[2], olat[2], head_length, arc_width, n_az, arc;
4916 	double rot[2] = {0.0, 0.0}, rot_v[2] = {0.0, 0.0};
4917 	double *xp = NULL, *yp = NULL, *xp2 = NULL, *yp2 = NULL;
4918 	double *rgb = S->v.fill.rgb;
4919 	struct GMT_CIRCLE C;
4920 
4921 #if 0
4922 	/* We must determine points A and B, whose great-circle connector is the arc we seek to draw */
4923 	justify = PSL_vec_justify (S->v.status);	/* Return justification as 0-3 */
4924 #endif
4925 
4926 	gmtplot_scircle_sub (GMT, lon0, lat0, azimuth, length, S, &C);
4927 	perspective = gmt_M_is_perspective (GMT);
4928 
4929 	/* Here we have the endpoints A and B of the great (or small) circle arc */
4930 
4931 	/* If shrink-option (+n) is active we may have to scale down head attributes and pen width */
4932 	/* If shrink-option (+n) is active we may have to scale down head attributes and pen width */
4933 	heads = PSL_vec_head (S->v.status);	/* Return head selection as 0-3 */
4934 	pure = (S->v.v_norm == -1.0f);	/* True if no shrinking has been specified */
4935 	h_length_limit = (1.0 - S->v.v_stem) * C.r0;	/* Max length of arrow in degrees to ensure the stem is still showing */
4936 	if (heads == 3) h_length_limit *= 0.5;		/* Split this length between the two heads */
4937 	if (heads && !pure) {	/* Need to determine head length in degrees */
4938 		az[0] = gmtplot_smallcircle_az (GMT, C.A, S);	/* Compute the azimuth from A to B at A along small circle */
4939 		scl[0] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[0], C.lat[0], 0.001 * C.r, az[0]);	/* Get local deg/inch scale at A in az[0] direction */
4940 		dr[0] = scl[0] * S->size_x;	/* This is arrow head length in degrees, approximately */
4941 		az[1] = -gmtplot_smallcircle_az (GMT, C.B, S);	/* Compute the azimuth from B to A at B along small circle */
4942 		scl[1] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[1], C.lat[1], 0.01 * C.r, az[1]);	/* Get local deg/inch scale */
4943 		dr[1] = scl[1] * S->size_x;	/* This is arrow head length in degrees, approximately, adjusted for ~pen thickness to ensure no gap between head and line */
4944 		max_length = MAX (dr[0], dr[1]);
4945 		if (max_length > h_length_limit) {
4946 			s2 = h_length_limit / max_length;
4947 			warn = 2;
4948 		}
4949 	}
4950 
4951 	/* Might have to shrink things */
4952 	if (C.r0 < S->v.v_norm)
4953 		s1 = C.r0 / S->v.v_norm;
4954 	if (s1 < s2) {	/* Pick the smallest scale required for head shrinking */
4955 		s = s1;
4956 		warn = 0;	/* When requesting +n there is no warning unless s2 is the smaller scale */
4957 	}
4958 	else
4959 		s = s2;
4960 	head_length = s * S->size_x;
4961 	arc_width   = s1 * S->v.v_width;	/* Use scale s1 for pen shrinking */
4962 
4963 	gmt_M_memcpy (olon, C.lon, 2, double);	gmt_M_memcpy (olat, C.lat, 2, double);	/* Keep copy of original coordinates */
4964 
4965 	/* When only one side of a vector head is requested (side = -1/+1) there are complications that leads to some
4966 	 * extra work: Since we are clipping the head polygon, the head outline pen is effectively half that of the
4967 	 * vector.  Thus, there will be an offset of 1/2 penwidth at the end of the vector line and the start of the
4968 	 * back-end of the half vector head.  We adjust this changing the colatitude from the pole by the equivalent
4969 	 * distance of 1/2 pen width away from the side with the half arrowhead.  This makes the outline of the head
4970 	 * align with the vector line. */
4971 	for (n = 0; n < 2; n++) {
4972 		side[n] = PSL_vec_side (S->v.status, n);	/* Return side selection as 0,-1,+1 for this head */
4973 		dshift[n] = (side[n]) ? 0.5 * arc_width : 0.0;	/* Half-width of arc thickness if side != 0 */
4974 	}
4975 	if (heads & 1) {	/* Placing head at A means we must shorten the arc and use Ax instead of A */
4976 		az[0] = gmtplot_smallcircle_az (GMT, C.A, S);	/* Compute the azimuth from A to B at A along small circle */
4977 		scl[0] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[0], C.lat[0], 0.001 * C.r, az[0]);	/* Get local deg/inch scale at A in az[0] direction */
4978 		dr[0] = scl[0] * (head_length - 1.1*dshift[0]);	/* This is arrow head length in degrees, approximately, adjusted for ~pen thickness to ensure no gap between head and line (the 1.1 slop) */
4979 		if (pure && dr[0] > h_length_limit) {	/* Head length too long, refuse to plot it */
4980 			gmt_M_memcpy (Ax, C.A, 3, double);	/* No need to shorten arc at beginning */
4981 			heads -= 1;	/* Not purse this head any further */
4982 			warn = 1;
4983 		}
4984 		else {
4985 			dr[0] /= sind (C.colat);	/* Scale dr[0] to opening angle degrees given colatitude */
4986 			/* Determine mid-back point of arrow head by rotating A back by chosen fraction rot of dr[0] */
4987 			rot_v[0] = 0.5 * dr[0] * (1.95 - S->v.v_shape);	/* 1.95 instead of 2 to allow for slop */
4988 			gmt_make_rot_matrix2 (GMT, C.P, rot_v[0], R);	/* Rotation of rot_v[0] degrees about pole P */
4989 			gmt_matrix_vect_mult (GMT, 3U, R, C.A, Ax);	/* Get Ax = R * A*/
4990 			C.rot -= rot_v[0];	/* Shorten full arc by the same amount */
4991 			dr[0] = scl[0] * head_length;	/* This is arrow head length in great-circle degrees, approximately, without any pen-width compensation */
4992 		}
4993 	}
4994 	else
4995 		gmt_M_memcpy (Ax, C.A, 3, double);	/* No need to shorten arc at beginning */
4996 
4997 	if (heads & 2) { /* Place arrow head at B */
4998 		az[1] = -gmtplot_smallcircle_az (GMT, C.B, S);	/* Compute the azimuth from B to A at B along small circle */
4999 		scl[1] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[1], C.lat[1], 0.01 * C.r, az[1]);	/* Get local deg/inch scale */
5000 		dr[1] = S->v.scale * (head_length - 1.1*dshift[1]);	/* This is arrow head length in degrees, approximately, adjusted for ~pen thickness to ensure no gap between head and line */
5001 		if (pure && dr[1] > h_length_limit) {	/* Head length too long, refuse to plot it */
5002 			gmt_M_memcpy (Bx, C.B, 3, double);	/* No need to shorten arc at end */
5003 			heads -= 1;	/* Not purse this head any further */
5004 			warn = 1;
5005 		}
5006 		else {
5007 			dr[1] /= sind (C.colat);	/* Scale dr[1] to opening angle degrees given colatitude */
5008 			/* Determine mid-back point of arrow head by rotating B back by chosen fraction rot of dr[1] */
5009 			rot_v[1] = 0.5 * dr[1] * (1.95 - S->v.v_shape);	/* 1.95 instead of 2 to allow for slop */
5010 			gmt_make_rot_matrix2 (GMT, C.P, -rot_v[1], R);	/* Rotation of -rot_v[1] degrees about pole P */
5011 			gmt_matrix_vect_mult (GMT, 3U, R, C.B, Bx);	/* Get Bx = R * B*/
5012 			C.rot -= rot_v[1];	/* Shorten full arc by the same amount */
5013 			dr[1] = scl[1] * head_length;	/* This is arrow head length in great-circle degrees, approximately, without any pen-width compensation */
5014 		}
5015 	}
5016 	else
5017 		gmt_M_memcpy (Bx, C.B, 3, double);	/* No need to shorten arc at end */
5018 
5019 	gmt_M_memcpy (oaz, az, 2, double);	/* Keep copy of original azimuths */
5020 
5021 	/* Get array of lon,lat points that defines the arc */
5022 
5023 	if (side[0] || side[1]) {	/* Must adjust the distance from pole to A, B by 1/2 the pen width */
5024 		double Scl, Off, xlon, xlat, tlon, tlat;
5025 		gmt_cart_to_geo (GMT, &xlat, &xlon, Ax, true);
5026 		n_az = gmt_az_backaz (GMT, xlon, xlat, S->v.pole[GMT_X], S->v.pole[GMT_Y], false);	/* Compute the azimuth from Ax to P at Ax along great circle */
5027 		if (side[0] == +1) n_az += 180.0;	/* Might be for side == +1, check */
5028 		Scl = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, xlon, xlat, 0.01, n_az);	/* Get deg/inch scale at A perpendicular to arc */
5029 		Off = Scl * dshift[0];	/* Offset in degrees due to 1/2 pen thickness */
5030 		gmtlib_get_point_from_r_az (GMT, xlon, xlat, Off, n_az, &tlon, &tlat);	/* Adjusted Ax */
5031 		gmt_geo_to_cart (GMT, tlat, tlon, Ax2, true);
5032 		gmt_make_rot_matrix2 (GMT, C.P, C.rot, R);		/* Rotation of C->rot degrees about pole P */
5033 		gmt_matrix_vect_mult (GMT, 3U, R, Ax2, Bx2);		/* Get revised Bx = R * Ax */
5034 	}
5035 	else {
5036 		gmt_M_memcpy (Ax2, Ax, 3, double);	/* No need to shorten arc at end */
5037 		gmt_M_memcpy (Bx2, Bx, 3, double);	/* No need to shorten arc at end */
5038 	}
5039 
5040 	gmtplot_circle_pen_poly (GMT, Ax2, Bx2, false, C.rot, ppen, S, &C, s1);
5041 
5042 	if (!heads) return (warn);	/* All done */
5043 
5044 	PSL_command (GMT->PSL, "V\n");
5045 	/* Get half-angle at head and possibly change pen */
5046 	da = 0.5 * S->v.v_angle;	/* Half-opening angle at arrow head */
5047 	if ((S->v.status & PSL_VEC_OUTLINE) == 0)
5048 		PSL_command (GMT->PSL, "O0\n");	/* Turn off outline */
5049 	else {
5050 		gmt_setpen (GMT, &S->v.pen);
5051 		outline = 1;
5052 	}
5053 	if ((S->v.status & PSL_VEC_FILL) == 0)
5054 		PSL_command (GMT->PSL, "FQ\n");	/* Turn off vector head fill */
5055 	else
5056 		PSL_setfill (GMT->PSL, rgb, outline);
5057 
5058 	if (heads & 1) { /* Place arrow head at A */
5059 		if (C.longway) az[0] += 180.0;
5060 		rot[0] = dr[0] / sind (C.colat);	/* Small circle rotation angle to draw arrow outline */
5061 		if (side[0] != +1) {	/* Want to draw left side of arrow */
5062 			gmt_make_rot_matrix2 (GMT, C.A, da, R);		/* Rotation of da degrees about A */
5063 			gmt_matrix_vect_mult (GMT, 3U, R, C.P, Pa);	/* Rotate pole C.P to inner arc pole location Pa */
5064 			gmt_make_rot_matrix2 (GMT, Pa, rot[0], R);	/* Rotation of rot[0] degrees about Pa */
5065 			gmt_matrix_vect_mult (GMT, 3U, R, C.A, P);	/* Rotate A to inner arc end point P */
5066 			arc = rot[0];
5067 		}
5068 		else {
5069 			gmt_M_memcpy (P, Ax, 3, double);		/* Start from (possibly adjusted) mid point instead... */
5070 			gmt_M_memcpy (Pa, C.P, 3, double);	/* ...and use circle pole */
5071 			arc = rot_v[0];
5072 		}
5073 		n1 = (int)gmtplot_small_circle_arc (GMT, P, 0.0, Pa, -arc, &xp, &yp);	/* Compute small circle arc from P to A */
5074 		if (side[0] != -1) {	/* Want to draw right side of arrow */
5075 			gmt_make_rot_matrix2 (GMT, C.A, -da, R);	/* Rotation of -da degrees about A */
5076 			gmt_matrix_vect_mult (GMT, 3U, R, C.P, Pa);	/* Rotate pole C.P to outer arc pole location Pa */
5077 			gmt_make_rot_matrix2 (GMT, Pa, rot[0], R);	/* Rotation of rot[0] degrees about Pa */
5078 			gmt_matrix_vect_mult (GMT, 3U, R, C.A, P);	/* Rotate A to outer arc end point P */
5079 			arc = rot[0];
5080 		}
5081 		else {
5082 			gmt_M_memcpy (P, Ax, 3, double);		/* Start from (adjusted) mid point instead... */
5083 			gmt_M_memcpy (Pa, C.P, 3, double);	/* ...and use circle pole */
5084 			arc = rot_v[0];
5085 		}
5086 		n2 = (unsigned int)gmtplot_small_circle_arc (GMT, C.A, 0.0, Pa, arc, &xp2, &yp2);	/* Compute great circle arc from A to P */
5087 		add = (side[0] == 0) ? 1 : 0;	/* Need to add mid point explicitly */
5088 		n_alloc = n = n1 + n2 + add;
5089 		gmt_M_malloc2 (GMT, xp, yp, 0U, &n_alloc, double);	/* Allocate space for total path */
5090 		gmt_M_memcpy (&xp[n1], xp2, n2, double);
5091 		gmt_M_memcpy (&yp[n1], yp2, n2, double);
5092 		if (add) {	/* Mid point of arrow */
5093 			gmt_cart_to_geo (GMT, &yp[n-1], &xp[n-1], Ax, true);	/* Add geo coordinates of this new back mid point for arc */
5094 		}
5095 		gmtplot_plot_vector_head (GMT, xp, yp, n, S);
5096 		gmt_M_free (GMT, xp);	gmt_M_free (GMT, yp);
5097 		gmt_M_free (GMT, xp2);	gmt_M_free (GMT, yp2);
5098 	}
5099 	if (heads & 2) { /* Place arrow head at B */
5100 		if (C.longway) az[1] += 180.0;
5101 		rot[1] = dr[1] / sind (C.colat);	/* Small circle rotation angle to draw arrow outline */
5102 		if (side[1] != +1) {	/* Want to draw left side of arrow */
5103 			gmt_make_rot_matrix2 (GMT, C.B, -da, R);	/* Rotation of -da degrees about B */
5104 			gmt_matrix_vect_mult (GMT, 3U, R, C.P, Pa);	/* Rotate pole C.P to inner arc pole location Pa */
5105 			gmt_make_rot_matrix2 (GMT, Pa, -rot[1], R);	/* Rotation of -rot[1] degrees about Pa */
5106 			gmt_matrix_vect_mult (GMT, 3U, R, C.B, P);	/* Rotate B to inner arc end point P */
5107 			arc = rot[1];
5108 		}
5109 		else {
5110 			gmt_M_memcpy (P, Bx, 3, double);		/* Start from (adjusted) mid point instead... */
5111 			gmt_M_memcpy (Pa, C.P, 3, double);	/* ...and use circle pole */
5112 			arc = rot_v[1];
5113 		}
5114 		n1 = gmtplot_small_circle_arc (GMT, P, 0.0, Pa, arc, &xp, &yp);	/* Compute small circle arc from P to B */
5115 		if (side[1] != -1) {	/* Want to draw right side of arrow */
5116 			gmt_make_rot_matrix2 (GMT, C.B, da, R);		/* Rotation of da degrees about B */
5117 			gmt_matrix_vect_mult (GMT, 3U, R, C.P, Pa);	/* Rotate pole C.P to outer arc pole location Pa */
5118 			gmt_make_rot_matrix2 (GMT, Pa, -rot[1], R);	/* Rotation of -rot[1] degrees about Pa */
5119 			gmt_matrix_vect_mult (GMT, 3U, R, C.B, P);	/* Rotate B to outer arc end point P */
5120 			arc = rot[1];
5121 		}
5122 		else {
5123 			gmt_M_memcpy (P, Bx, 3, double);		/* Start from (adjusted) mid point instead */
5124 			gmt_M_memcpy (Pa, C.P, 3, double);	/* ...and use circle pole */
5125 			arc = rot_v[1];
5126 		}
5127 		n2 = (unsigned int)gmtplot_small_circle_arc (GMT, C.B, 0.0, Pa, -arc, &xp2, &yp2);	/* Compute small circle arc from B to P */
5128 		add = (side[1] == 0) ? 1 : 0;	/* Need to add mid point explicitly */
5129 		n_alloc = n = n1 + n2 + add;
5130 		gmt_M_malloc2 (GMT, xp, yp, 0U, &n_alloc, double);	/* Allocate space for total path */
5131 		gmt_M_memcpy (&xp[n1], xp2, n2, double);
5132 		gmt_M_memcpy (&yp[n1], yp2, n2, double);
5133 		if (add) {	/* Mid point of arrow */
5134 			gmt_cart_to_geo (GMT, &yp[n-1], &xp[n-1], Bx, true);	/* Add geo coordinates of this new back-mid point for arc */
5135 		}
5136 		gmtplot_plot_vector_head (GMT, xp, yp, n, S);
5137 		gmt_M_free (GMT, xp);	gmt_M_free (GMT, yp);
5138 		gmt_M_free (GMT, xp2);	gmt_M_free (GMT, yp2);
5139 	}
5140 	PSL_command (GMT->PSL, "U\n");
5141 	return (warn);
5142 }
5143 
gmtplot_geo_vector_greatcircle(struct GMT_CTRL * GMT,double lon0,double lat0,double azimuth,double length,struct GMT_PEN * ppen,struct GMT_SYMBOL * S)5144 GMT_LOCAL unsigned int gmtplot_geo_vector_greatcircle (struct GMT_CTRL *GMT, double lon0, double lat0, double azimuth, double length, struct GMT_PEN *ppen, struct GMT_SYMBOL *S) {
5145 	/* Draws a great-circle vector with our without heads, etc. There are some complications to consider:
5146 	 * When there are no heads it is simple.  If +n is on we may shrink the line thickness.
5147 	 * With heads there are these cases:
5148 	 * Head length is longer than 90% of the vector length.  We then skip the head and return 1
5149 	 * +n<norm> is in effect.  We shrink vector pen and head length.  Still, the shrunk head
5150 	 * may be longer than 90% of the vector length.  We then shrink head (not pen) further and return 2
5151 	*/
5152 
5153 	uint64_t n1, n2, n, add;
5154 	int heads, side[2], outline = 0;
5155 	unsigned int warn = 0;
5156 	size_t n_alloc;
5157 	bool perspective, pure;
5158 	double tlon, tlat, mlon, mlat, P[3], Ax[3], Bx[3], h_length_limit, max_length;
5159 	double dr[2] = {0.0, 0.0}, az[2] = {0.0, 0.0}, oaz[2] = {0.0, 0.0}, off[2] = {0.0, 0.0};
5160 	double da = 0.0, dshift[2] = {0.0, 0.0}, s = 1.0, s1 = 1.0, s2 = 1.0, olon[2], olat[2], head_length, arc_width, rot, scl[2];
5161 	double *xp = NULL, *yp = NULL, *xp2 = NULL, *yp2 = NULL;
5162 	double *rgb = S->v.fill.rgb;
5163 	struct GMT_CIRCLE C;
5164 
5165 	/* We must determine points A and B, whose great-circle connector is the arc we seek to draw */
5166 	gmtplot_gcircle_sub (GMT, lon0, lat0, azimuth, length, S, &C);
5167 	perspective = gmt_M_is_perspective (GMT);
5168 
5169 	/* Here we have the endpoints A and B of the great (or small) circle arc */
5170 
5171 	/* If shrink-option (+n) is active we may have to scale down head attributes and pen width */
5172 	pure = (S->v.v_norm == -1.0f);	/* True if no shrinking has been specified */
5173 	heads = PSL_vec_head (S->v.status);	/* Return head selection as 0-3 */
5174 	h_length_limit = (1.0 - S->v.v_stem) * C.r0;	/* Max length of arrow in degrees to ensure the stem is still showing */
5175 	if (heads == 3) h_length_limit *= 0.5;		/* Split this length between the two heads */
5176 	if (heads && !pure) {	/* Need to determine head length in degrees */
5177 		az[0]  = gmt_az_backaz (GMT, C.lon[0], C.lat[0], C.lon[1], C.lat[1], false);	/* Compute the azimuth from A to B at A along great circle */
5178 		scl[0] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[0], C.lat[0], 0.001 * C.r, az[0]);	/* Get local deg/inch scale at A in az[0] direction */
5179 		dr[0] = scl[0] * S->size_x;	/* This is arrow head length in degrees, approximately */
5180 		az[1]  = gmt_az_backaz (GMT, C.lon[1], C.lat[1], C.lon[0], C.lat[0], false);	/* Compute the azimuth from B to A at B along great circle */
5181 		scl[1] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[1], C.lat[1], 0.01 * C.r, az[1]);	/* Get local deg/inch scale */
5182 		dr[1] = scl[1] * S->size_x;	/* This is arrow head length in degrees, approximately, adjusted for ~pen thickness to ensure no gap between head and line */
5183 		max_length = MAX (dr[0], dr[1]);
5184 		if (max_length > h_length_limit) {
5185 			s2 = h_length_limit / max_length;
5186 			warn = 2;
5187 		}
5188 	}
5189 
5190 	/* Might have to shrink things */
5191 	if (C.r0 < S->v.v_norm)
5192 		s1 = C.r0 / S->v.v_norm;
5193 	if (s1 < s2) {	/* Pick the smallest scale required for head shrinking */
5194 		s = s1;
5195 		warn = 0;	/* When requesting +n there is no warning unless s2 is the smaller scale */
5196 	}
5197 	else
5198 		s = s2;
5199 	head_length = s * S->size_x;
5200 	arc_width   = s1 * S->v.v_width;	/* Use scale s1 for pen shrinking */
5201 	gmt_M_memcpy (olon, C.lon, 2, double);	gmt_M_memcpy (olat, C.lat, 2, double);	/* Keep copy of original coordinates */
5202 
5203 	/* When only one side of a vector head is requested (side = -1/+1) there are complications that leads to some
5204 	 * extra work: Since we are clipping the head polygon, the head outline pen is effectively half that of the
5205 	 * vector.  Thus, there will be an offset of 1/2 penwidth at the end of the vector line and the start of the
5206 	 * back-end of the half vector head.  We adjust this (similar to straight and curved vectors in postscriptlight) by moving
5207 	 * the vector tip and the mid-vector back point the equivalent distance of 1/2 pen width away from the side with
5208 	 * the half arrowhead.  This makes the outline of the head align with the vector line. */
5209 
5210 	for (n = 0; n < 2; n++) {
5211 		side[n] = PSL_vec_side (S->v.status, n);	/* Return side selection as 0,-1,+1 for this head */
5212 		dshift[n] = (side[n]) ? 0.5 * arc_width : 0.0;	/* Half-width of arc thickness if side != 0 */
5213 	}
5214 	side[0] = -side[0];	/* Since implmenented backwards */
5215 	if (heads & 1) {	/* Placing head at A means we must shorten the arc and use Ax instead of A */
5216 		az[0] = gmt_az_backaz (GMT, C.lon[0], C.lat[0], C.lon[1], C.lat[1], false);	/* Compute the azimuth from A to B at A along great circle */
5217 		scl[0] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[0], C.lat[0], 0.001 * C.r, az[0]);	/* Get local deg/inch scale at A in az[0] direction */
5218 		dr[0] = scl[0] * (head_length - 1.1*dshift[0]);	/* This is arrow head length in degrees, approximately, adjusted for ~pen thickness to ensure no gap between head and line */
5219 		if (pure && dr[0] > h_length_limit) {	/* Head length too long, refuse to plot it */
5220 			gmt_M_memcpy (Ax, C.A, 3, double);	/* No need to shorten arc at beginning */
5221 			heads -= 1;	/* Not purse this head any further */
5222 			warn = 1;
5223 		}
5224 		else {
5225 			gmtlib_get_point_from_r_az (GMT, C.lon[0], C.lat[0], 0.5*dr[0]*(1.95 - S->v.v_shape), az[0], &tlon, &tlat);	/* Back mid-point of arrow */
5226 			gmt_geo_to_cart (GMT, tlat, tlon, Ax, true);	/* Get Cartesian coordinates of this new start point for arc */
5227 			dr[0] = scl[0] * head_length;	/* This is arrow head length in degrees, approximately, without any pen-width compensation */
5228 		}
5229 	}
5230 	else
5231 		gmt_M_memcpy (Ax, C.A, 3, double);	/* No need to shorten arc at beginning */
5232 
5233 	if (heads & 2) { /* Place arrow head at B */
5234 		az[1] = gmt_az_backaz (GMT, C.lon[1], C.lat[1], C.lon[0], C.lat[0], false);	/* Compute the azimuth from B to A at B along great circle */
5235 		scl[1] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, C.lon[1], C.lat[1], 0.01 * C.r, az[1]);	/* Get local deg/inch scale */
5236 		dr[1] = scl[1] * (head_length - 1.1*dshift[1]);	/* This is arrow head length in degrees, approximately, adjusted for ~pen thickness to ensure no gap between head and line */
5237 		if (pure && dr[1] > h_length_limit) {	/* Head length too long, refuse to plot it */
5238 			gmt_M_memcpy (Bx, C.B, 3, double);	/* No need to shorten arc at end */
5239 			heads -= 2;	/* Not purse this head any further */
5240 			warn = 1;
5241 		}
5242 		else {
5243 			gmtlib_get_point_from_r_az (GMT, C.lon[1], C.lat[1], 0.5*dr[1]*(1.95 - S->v.v_shape), az[1], &tlon, &tlat);	/* Back mid-point of arrow */
5244 			gmt_geo_to_cart (GMT, tlat, tlon, Bx, true);	/* Get Cartesian coordinates of this new end point for arc */
5245 			dr[1] = scl[1] * head_length;	/* This is arrow head length in degrees, approximately, without any pen-width compensation */
5246 		}
5247 	}
5248 	else
5249 		gmt_M_memcpy (Bx, C.B, 3, double);	/* No need to shorten arc at end */
5250 
5251 	gmt_M_memcpy (oaz, az, 2, double);	/* Keep copy of original azimuths */
5252 
5253 	rot = d_acosd (gmt_dot3v (GMT, Ax, Bx));	/* opening angle in degrees */
5254 	gmtplot_circle_pen_poly (GMT, Ax, Bx, C.longway, rot, ppen, S, &C, s1);
5255 
5256 	if (!heads) return (warn);	/* All done */
5257 
5258 	/* Get half-angle at head and possibly change pen */
5259 	da = 0.5 * S->v.v_angle;	/* Half-opening angle at arrow head */
5260 	PSL_command (GMT->PSL, "V\n");
5261 	if ((S->v.status & PSL_VEC_OUTLINE) == 0)
5262 		PSL_command (GMT->PSL, "O0\n");	/* Turn off outline */
5263 	else {
5264 		gmt_setpen (GMT, &S->v.pen);
5265 		outline = 1;
5266 	}
5267 	if ((S->v.status & PSL_VEC_FILL) == 0)
5268 		PSL_command (GMT->PSL, "FQ\n");	/* Turn off vector head fill */
5269 	else
5270 		PSL_setfill (GMT->PSL, rgb, outline);
5271 
5272 	if (heads & 1) { /* Place arrow head at A */
5273 		if (C.longway) az[0] += 180.0;
5274 		gmtlib_get_point_from_r_az (GMT, C.lon[0], C.lat[0], 0.5*dr[0]*(2.0 - S->v.v_shape), az[0], &mlon, &mlat);	/* Back mid-point of arrow  */
5275 		if (side[0]) {	/* Must adjust the back mid- and end point by 1/2 the pen width */
5276 			az[0] = gmt_az_backaz (GMT, mlon, mlat, C.lon[1], C.lat[1], false);	/* Compute the azimuth from M to B at M */
5277 			scl[0] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, mlon, mlat, tand (da) * dr[0], az[0]+side[0]*90.0);	/* Get deg/inch scale at M perpendicular to arc */
5278 			off[0] = scl[0] * dshift[0];	/* Offset in degrees due to 1/2 pen thickness */
5279 			gmtlib_get_point_from_r_az (GMT, mlon, mlat, off[0], az[0]+side[0]*90.0, &tlon, &tlat);	/* Adjusted back mid-point of arrow head */
5280 			mlon = tlon;	mlat = tlat;	/* Update shifted mid-point */
5281 			gmtlib_get_point_from_r_az (GMT, C.lon[0], C.lat[0], off[0], oaz[0]+side[0]*90.0, &tlon, &tlat);	/* Adjusted tip of arrow head A */
5282 			C.lon[0] = tlon;	C.lat[0] = tlat;	/* Update shifted A location */
5283 			gmt_geo_to_cart (GMT, tlat, tlon, C.A, true);	/* New A vector */
5284 		}
5285 		if (side[0] != +1) {	/* Want to draw left side of arrow */
5286 			gmtlib_get_point_from_r_az (GMT, olon[0], olat[0], dr[0]+off[0], oaz[0]+da, &tlon, &tlat);	/* Start point of arrow on left side */
5287 			gmt_geo_to_cart (GMT, tlat, tlon, P, true);
5288 		}
5289 		else
5290 			gmt_geo_to_cart (GMT, mlat, mlon, P, true);	/* Start from (adjusted) mid point instead */
5291 		n1 = gmtplot_great_circle_arc (GMT, P, C.A, 0.0, false, &xp, &yp);	/* Compute great circle arc from P to A */
5292 		if (side[0] != -1) {	/* Want to draw right side of arrow */
5293 			gmtlib_get_point_from_r_az (GMT, olon[0], olat[0], dr[0]+off[0], oaz[0]-da, &tlon, &tlat);	/* End point of arrow on right side */
5294 			gmt_geo_to_cart (GMT, tlat, tlon, P, true);
5295 		}
5296 		else
5297 			gmt_geo_to_cart (GMT, mlat, mlon, P, true);	/* End at (adjusted) mid point instead */
5298 		n2 = gmtplot_great_circle_arc (GMT, C.A, P, 0.0, false, &xp2, &yp2);	/* Compute great circle arc from A to P */
5299 		add = (side[0] == 0) ? 1 : 0;	/* Need to add mid point explicitly */
5300 		n_alloc = n = n1 + n2 + add;
5301 		gmt_M_malloc2 (GMT, xp, yp, 0U, &n_alloc, double);	/* Allocate space for total path */
5302 		gmt_M_memcpy (&xp[n1], xp2, n2, double);
5303 		gmt_M_memcpy (&yp[n1], yp2, n2, double);
5304 		if (add) {	/* Mid point of arrow */
5305 			xp[n-1] = mlon;	yp[n-1] = mlat;
5306 		}
5307 		gmtplot_plot_vector_head (GMT, xp, yp, n, S);
5308 		gmt_M_free (GMT, xp);	gmt_M_free (GMT, yp);
5309 		gmt_M_free (GMT, xp2);	gmt_M_free (GMT, yp2);
5310 	}
5311 	if (heads & 2) { /* Place arrow head at B */
5312 		if (C.longway) az[1] += 180.0;
5313 		gmtlib_get_point_from_r_az (GMT, C.lon[1], C.lat[1], 0.5*dr[1]*(2.0 - S->v.v_shape), az[1], &mlon, &mlat);	/* Mid point of arrow */
5314 		if (side[1]) {	/* Must adjust the mid-point and end point by 1/2 the pen width */
5315 			az[1] = gmt_az_backaz (GMT, C.lon[1], C.lat[1], C.lon[0], C.lat[0], false);	/* Compute the azimuth from M to A at M */
5316 			scl[1] = (perspective) ? S->v.scale : gmtplot_get_local_scale (GMT, mlon, mlat, tand (da) * dr[1], az[1]+side[1]*90.0);	/* Get deg/inch scale at M perpendicular to arc */
5317 			off[1] = scl[1] * dshift[1];	/* Offset in degrees due to 1/2 pen thickness */
5318 			gmtlib_get_point_from_r_az (GMT, mlon, mlat, off[1], az[1]+side[1]*90.0, &tlon, &tlat);	/* Adjusted back mid-point of arrow head  */
5319 			mlon = tlon;	mlat = tlat;	/* Update shifted mid-point */
5320 			gmtlib_get_point_from_r_az (GMT, C.lon[1], C.lat[1], off[1], oaz[1]+side[1]*90.0, &tlon, &tlat);	/* Adjusted tip of arrow head */
5321 			C.lon[1] = tlon;	C.lat[1] = tlat;	/* Update shifted B location */
5322 			gmt_geo_to_cart (GMT, tlat, tlon, C.B, true);	/* New B vector */
5323 		}
5324 		if (side[1] != +1) {	/* Want to draw left side of arrow */
5325 			gmtlib_get_point_from_r_az (GMT, olon[1], olat[1], dr[1]+off[1], oaz[1]+da, &tlon, &tlat);	/* Start point of arrow on left side */
5326 			gmt_geo_to_cart (GMT, tlat, tlon, P, true);
5327 		}
5328 		else
5329 			gmt_geo_to_cart (GMT, mlat, mlon, P, true);	/* Start from (adjusted)mid point instead */
5330 		n1 = gmtplot_great_circle_arc (GMT, P, C.B, 0.0, false, &xp, &yp);	/* Compute great circle arc from P to B */
5331 		if (side[1] != -1) {	/* Want to draw right side of arrow */
5332 			gmtlib_get_point_from_r_az (GMT, olon[1], olat[1], dr[1]+off[1], oaz[1]-da, &tlon, &tlat);	/* Start point of arrow on other side */
5333 			gmt_geo_to_cart (GMT, tlat, tlon, P, true);
5334 		}
5335 		else
5336 			gmt_geo_to_cart (GMT, mlat, mlon, P, true);	/* End at (adjusted) mid point instead */
5337 		n2 = gmtplot_great_circle_arc (GMT, C.B, P, 0.0, false,  &xp2, &yp2);	/* Compute great circle arc from B to P */
5338 		add = (side[1] == 0) ? 1 : 0;	/* Need to add mid point explicitly */
5339 		n_alloc = n = n1 + n2 + add;
5340 		gmt_M_malloc2 (GMT, xp, yp, 0U, &n_alloc, double);	/* Allocate space for total path */
5341 		gmt_M_memcpy (&xp[n1], xp2, n2, double);
5342 		gmt_M_memcpy (&yp[n1], yp2, n2, double);
5343 		if (add) {	/* Mid point of arrow */
5344 			xp[n-1] = mlon;	yp[n-1] = mlat;
5345 		}
5346 		gmtplot_plot_vector_head (GMT, xp, yp, n, S);
5347 		gmt_M_free (GMT, xp);	gmt_M_free (GMT, yp);
5348 		gmt_M_free (GMT, xp2);	gmt_M_free (GMT, yp2);
5349 	}
5350 	PSL_command (GMT->PSL, "U\n");
5351 	return (warn);
5352 }
5353 
5354 /*----------------------------------------------------------|
5355  * Public functions that are part of the GMT Devel library  |
5356  *----------------------------------------------------------|
5357  */
5358 
gmt_map_text(struct GMT_CTRL * GMT,double x,double y,struct GMT_FONT * font,char * label,double angle,int just,unsigned int form)5359 void gmt_map_text (struct GMT_CTRL *GMT, double x, double y, struct GMT_FONT *font, char *label, double angle, int just, unsigned int form) {
5360 	/* Function to plot single-line text in pstext.c */
5361 	struct PSL_CTRL *PSL= GMT->PSL;
5362 
5363 	if (gmt_text_is_latex (GMT, label)) {	/* Detected LaTeX commands, i.e., "....@[LaTeX...@[ ..." or  "....<math>LaTeX...</math> ..." */
5364 		double w, h;
5365 		unsigned char *eps = NULL;
5366 		struct imageinfo header;
5367 
5368 		if ((eps = gmtplot_latex_eps (GMT, font, label, &header)) == NULL) {
5369 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_map_text: Conversion of LaTeX to EPS failed\n");
5370 			return;	/* Done */
5371 		}
5372 		/* Scale up EPS dimensions by the ratio of label font size to LaTeX default size of 10p */
5373 		w = (header.width  / 72.0) * (font->size / 10.0);
5374 		h = (header.height / 72.0) * (font->size / 10.0);
5375 		/* Place EPS file as label, then free eps */
5376 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_map_text: Conversion of LaTeX gave dimensions %g x %g\n", w, h);
5377 		PSL_command (PSL, "V\n");	/* Keep the relative changes inside a save/restore block */
5378 		PSL_setorigin (PSL, x, y, angle, PSL_FWD);		/* Move to desired point and possibly rotate to angle */
5379 		PSL_plotlatexeps (PSL, 0.0, 0.0, w, h, just, eps, font->fill.rgb, &header);	/* Place the EPS plot */
5380 		PSL_command (PSL, "U\n");	/* Close up the block */
5381 		PSL_free (eps);
5382 	}
5383 	else	/* Regular text label */
5384 		PSL_plottext (PSL, x, y, font->size, label, angle, just, form);
5385 }
5386 
gmt_xy_axis(struct GMT_CTRL * GMT,double x0,double y0,double length,double val0,double val1,struct GMT_PLOT_AXIS * A,bool below,unsigned side)5387 void gmt_xy_axis (struct GMT_CTRL *GMT, double x0, double y0, double length, double val0, double val1, struct GMT_PLOT_AXIS *A, bool below, unsigned side) {
5388 	unsigned int k, i, nx, nx1, np = 0;/* Misc. variables */
5389 	unsigned int annot_pos;	/* Either 0 for upper annotation or 1 for lower annotation */
5390 	unsigned int primary;		/* Axis item number of annotation with largest interval/unit */
5391 	unsigned int axis = A->id;	/* Axis id (GMT_X, GMT_Y, GMT_Z) */
5392 	unsigned int justify;
5393 	unsigned int lx_just = PSL_BC, ly_just = PSL_BC;
5394 	bool horizontal;		/* true if axis is horizontal */
5395 	bool annotate = ((side & GMT_AXIS_ANNOT) > 0);
5396 	bool neg = below;		/* true if annotations are to the left of or below the axis */
5397 	bool faro;			/* true if the anchor point of annotations is on the far side of the axis */
5398 	bool first = true;
5399 	bool is_interval;		/* true when the annotation is interval annotation and not tick annotation */
5400 	bool do_annot;		/* true unless we are dealing with Gregorian weeks */
5401 	bool do_tick;		/* true unless we are dealing with bits of weeks */
5402 	bool form;			/* true for outline font */
5403 	bool ortho = false;		/* true if annotations are orthogonal to axes */
5404 	bool flip = false;		/* true if annotations are inside axes */
5405 	bool MM_set = false;		/* true after we define the MM PS macro for label offsets */
5406 	bool angled = false;		/* True if user used +angle to select a slanted annotation */
5407 	bool skip = false, just_set = false;
5408 	bool save_pi = GMT->current.plot.substitute_pi;
5409 	double *knots = NULL, *knots_p = NULL;	/* Array pointers with tick/annotation knots, the latter for primary annotations */
5410 	double x, t_use, text_angle, cos_a = 0.0, sin_a = 0.0, delta;	/* Misc. variables */
5411 	double x_angle_add = 0.0, y_angle_add = 0.0;	/* Used when dealing with perspectives */
5412 	struct GMT_FONT font;			/* Annotation font (FONT_ANNOT_PRIMARY or FONT_ANNOT_SECONDARY) */
5413 	struct GMT_PLOT_AXIS_ITEM *T = NULL;	/* Pointer to the current axis item */
5414 	char string[GMT_LEN256] = {""};	/* Annotation string */
5415 	char format[GMT_LEN256] = {""};	/* format used for non-time annotations */
5416 	char *axis_chr[3] = {"ns", "ew", "zz"};	/* Characters corresponding to axes */
5417 	char **label_c = NULL;
5418 	double (*xyz_fwd) (struct GMT_CTRL *, double) = NULL;
5419 	struct PSL_CTRL *PSL= GMT->PSL;
5420 
5421 	/* Initialize parameters for this axis */
5422 
5423 	if (GMT->current.proj.three_D && axis != GMT_Z) {
5424 		/* Because perspective is now done in PostScript we must sometimes reverse the label orientations
5425 		 * so they don't appear upside down when viewed from certain quadrants */
5426 		switch (GMT->current.proj.z_project.quadrant) {
5427 			case 1: x_angle_add = 180.0; lx_just = PSL_TC; break;
5428 			case 3: y_angle_add = 180.0; ly_just = PSL_TC; break;
5429 			case 4: x_angle_add = y_angle_add = 180.0; lx_just = ly_just = PSL_TC; break;
5430 			default: x_angle_add = y_angle_add = 0.0; break;
5431 		}
5432 	}
5433 	horizontal = (axis == GMT_X);	/* This is a horizontal axis */
5434 	xyz_fwd = ((axis == GMT_X) ? &gmt_x_to_xx : (axis == GMT_Y) ? &gmt_y_to_yy : &gmt_z_to_zz);
5435 	primary = gmtplot_get_primary_annot (A);			/* Find primary axis items */
5436 	if (A->use_angle) {	/* Must honor the +a modifier */
5437 		if (axis != GMT_X && doubleAlmostEqualZero (A->angle, 90.0)) ortho = false, just_set = true;	/* Y/Z-Annotations are parallel */
5438 		else if (axis != GMT_X && doubleAlmostEqualZero (A->angle, 0.0)) ortho = true, just_set = true;	/* Y/Z-Annotations are normal */
5439 		if (doubleAlmostEqualZero (A->angle, 0.0)) skip = true;	/* Annotations are parallel so do nothing */
5440 	}
5441 	else if (strchr (GMT->current.setting.map_annot_ortho, axis_chr[axis][below]))
5442 		ortho = true;	/* Annotations are orthogonal for this axis */
5443 	if (GMT->current.setting.map_frame_type & GMT_IS_INSIDE) neg = !neg;	/* Annotations go either below or above the axis */
5444 	faro = (neg == (horizontal && !ortho));			/* Current point is at the far side of the tickmark? */
5445 	if (A->type != GMT_TIME) {						/* Set the annotation format template */
5446 		int n_dec = gmt_get_format (GMT, gmtlib_get_map_interval (GMT, A->type, &A->item[GMT_ANNOT_UPPER]), A->unit, A->prefix, format);
5447 		if (!doubleAlmostEqualZero (A->phase, 0.0)) {	/* Phase is nonzero, must see if adding it gives more decimals */
5448 			char format_w_phase[GMT_LEN256] = {""};	/* Format used if phase is added */
5449 			int ndec_p = gmt_get_format (GMT, gmtlib_get_map_interval (GMT, A->type, &A->item[GMT_ANNOT_UPPER]) + A->phase, A->unit, A->prefix, format_w_phase);
5450 			if (ndec_p > n_dec)	/* Use the phase format which has more decimals */
5451 				strncpy (format, format_w_phase, GMT_LEN256);
5452 		}
5453 	}
5454 	text_angle = (ortho == horizontal) ? 90.0 : 0.0;
5455 	justify = (ortho) ? PSL_MR : PSL_BC;
5456 	if (A->use_angle && !skip) {	/* User override annotation angle */
5457 		text_angle = A->angle;
5458 		angled = true;
5459 		if (!just_set) {
5460 			if (text_angle > 0.0)
5461 				justify = (below) ? PSL_MR : PSL_ML;
5462 			else
5463 				justify = (below) ? PSL_ML : PSL_MR;
5464 		}
5465 		if (axis == GMT_Y) {
5466 			ortho = true;
5467 			if (just_set && !below) justify = PSL_TC;
5468 		}
5469 		if (axis == GMT_X) {
5470 			cos_a = 0.5 * cosd (text_angle);	/* Half-height of text at an angle */
5471 			sin_a = fabs (sind (text_angle));	/* Fraction of y offset due to slanted annotation */
5472 		}
5473 		else {
5474 			cos_a = cosd (text_angle);	/* Full-height of text at an angle */
5475 			sin_a = fabs (sind (text_angle));	/* The y offset due to slanted annotation */
5476 		}
5477 	}
5478 	flip = (GMT->current.setting.map_frame_type & GMT_IS_INSIDE);	/* Inside annotation */
5479 	if (axis != GMT_Z && GMT->current.proj.three_D && GMT->current.proj.z_project.cos_az > 0) {	/* Rotate x/y-annotations when seen "from North" */
5480 		if (!flip) justify = gmt_flip_justify (GMT, justify);
5481 		text_angle += 180.0;
5482 	}
5483 //	else if (flip)
5484 //		justify = gmt_flip_justify (GMT, justify);
5485 
5486 	/* Ready to draw axis */
5487 	if (axis == GMT_X)
5488 		PSL_comment (PSL, below ? "Start of lower x-axis\n" : "Start of upper x-axis\n");
5489 	else if (axis == GMT_Y)
5490 		PSL_comment (PSL, below ? "Start of left y-axis\n" : "Start of right y-axis\n");
5491 	else
5492 		PSL_comment (PSL, below ? "Start of front z-axis\n" : "Start of back z-axis\n");
5493 	PSL_setorigin (PSL, x0, y0, 0.0, PSL_FWD);
5494 
5495 	PSL_comment (PSL, "Axis tick marks and annotations\n");
5496 	gmt_setpen (GMT, &GMT->current.setting.map_frame_pen);
5497 	if (horizontal)
5498 		PSL_plotsegment (PSL, 0.0, 0.0, length, 0.0);
5499 	else
5500 		PSL_plotsegment (PSL, 0.0, length, 0.0, 0.0);
5501 	if (GMT->current.setting.map_frame_type & GMT_IS_GRAPH) {	/* Extend axis with an arrow */
5502 		struct GMT_FILL arrow;
5503 		double vector_width, dim[PSL_MAX_DIMS], g_scale_begin = 0.0, g_scale_end = 0.0, g_ext = 0.0;
5504 		gmt_init_fill (GMT, &arrow, GMT->current.setting.map_frame_pen.rgb[0], GMT->current.setting.map_frame_pen.rgb[1], GMT->current.setting.map_frame_pen.rgb[2]);
5505 		gmt_setfill (GMT, &arrow, 0);
5506 		gmt_M_memset (dim, PSL_MAX_DIMS, double);
5507 		vector_width = rint (PSL_DOTS_PER_INCH * GMT->current.setting.map_frame_pen.width / PSL_POINTS_PER_INCH) / PSL_DOTS_PER_INCH;	/* Round off vector width same way as pen width */
5508 		dim[PSL_VEC_TAIL_WIDTH]    = vector_width;
5509 		dim[PSL_VEC_HEAD_LENGTH]   = 10.0 * vector_width;
5510 		dim[PSL_VEC_HEAD_WIDTH]    = 5.0 * vector_width;
5511 		dim[PSL_VEC_HEAD_SHAPE]    = GMT->current.setting.map_vector_shape;
5512 		dim[PSL_VEC_STATUS]        = PSL_VEC_END | PSL_VEC_FILL;
5513 		dim[PSL_VEC_HEAD_PENWIDTH] = 0.5 * GMT->current.setting.map_frame_pen.width;
5514 		PSL_defpen (PSL, "PSL_vecheadpen", GMT->current.setting.map_frame_pen.width, GMT->current.setting.map_frame_pen.style, GMT->current.setting.map_frame_pen.offset, GMT->current.setting.map_frame_pen.rgb);
5515 
5516 		/* The start of the vector at the end of a positive axis is computed as x = g_scale_end * length + g_ext;
5517 		 * The start of the vector at the start of a negative axis is computed as x = g_scale_begin * length - g_ext;
5518 		 * The required constants are set below */
5519 		if (GMT->current.setting.map_graph_extension_unit == GMT_GRAPH_EXTENSION_UNIT) {	/* Gave scaling in percent */
5520 			/* Here, the computed length is to the tip of the vector */
5521 			g_scale_end = 1.0 + 0.01 * GMT->current.setting.map_graph_extension;
5522 			g_scale_begin = -0.01 * GMT->current.setting.map_graph_extension;
5523 		}
5524 		else {	/* Gave actual length extension (now in inches) */
5525 			/* Here, the computed length is to the base of the vector */
5526 			g_scale_end = 1.0;
5527 			g_ext = GMT->current.setting.map_graph_extension + dim[3];
5528 		}
5529 
5530 		if (horizontal) {
5531 			double x = 0.0;
5532 			if (GMT->current.proj.xyz_pos[axis]) {
5533 				x = length;
5534 				dim[0] = g_scale_end * length + g_ext;
5535 			}
5536 			else
5537 				dim[0] = g_scale_begin * length - g_ext;
5538 			PSL_plotsymbol (PSL, x, 0.0, dim, PSL_VECTOR);
5539 		}
5540 		else {
5541 			double y = 0.0;
5542 			if (GMT->current.proj.xyz_pos[axis]) {
5543 				y = length;
5544 				dim[1] = g_scale_end * length + g_ext;
5545 			}
5546 			else
5547 				dim[1] = g_scale_begin * length - g_ext;
5548 			PSL_plotsymbol (PSL, 0.0, y, dim, PSL_VECTOR);
5549 		}
5550 	}
5551 
5552 	if (side == GMT_AXIS_DRAW) {	/* Just drawing the axis for this one */
5553 		PSL_setorigin (PSL, -x0, -y0, 0.0, PSL_INV);
5554 		return;
5555 	}
5556 
5557 	GMT->current.plot.substitute_pi = A->substitute_pi;
5558 	np = gmtlib_coordinate_array (GMT, val0, val1, &A->item[primary], &knots_p, NULL);	/* Get all the primary tick annotation knots */
5559 
5560 	/* Axis items are in order: GMT_ANNOT_UPPER, GMT_ANNOT_LOWER, GMT_TICK_UPPER, GMT_TICK_LOWER, GMT_GRID_UPPER, GMT_GRID_LOWER */
5561 
5562 	for (k = 0; k < 2; k++)	/* Define annotation offsets */
5563 		PSL_command (PSL, "/PSL_A%d_y %d def\n", k, A->item[k].active || A->item[k+2].active ? PSL_IZ (PSL, MAX(0.0,GMT->current.setting.map_tick_length[k])) : 0);	/* Length of primary/secondary tickmark */
5564 
5565 	for (k = 0; k < GMT_GRID_UPPER; k++) {	/* For each one of the 6 axis items (gridlines are done separately) */
5566 
5567 		T = &A->item[k];		/* Get pointer to this item */
5568 		if (!T->active) continue;	/* Do not want this item plotted - go to next item */
5569 
5570 		gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
5571 
5572 		is_interval = (T->type == 'i' || T->type == 'I');	/* Interval or tick mark annotation? */
5573 		nx = gmtlib_coordinate_array (GMT, val0, val1, &A->item[k], &knots, &label_c);	/* Get all the annotation tick knots */
5574 		delta = (label_c) ? GMT->session.d_NaN : A->item[k].interval;
5575 		do_annot = (nx && k < GMT_TICK_UPPER && annotate && !gmt_M_axis_is_geo_strict (GMT, axis) && T->unit != 'r');	/* Cannot annotate a Gregorian week */
5576 		do_tick = !((T->unit == 'K' || T->unit == 'k') && T->interval > 1 && fmod (T->interval, 7.0) > 0.0);	/* Do we want tick marks? */
5577 		nx1 = (nx > 0 && is_interval) ? nx - 1 : nx;
5578 		/* First plot all the tick marks */
5579 
5580 		if (do_tick) {
5581 			for (i = 0; i < nx; i++) {
5582 				x = (*xyz_fwd) (GMT, knots[i]);	/* Convert to inches on the page */
5583 				if (x < (- GMT_CONV4_LIMIT) || x > (length + GMT_CONV4_LIMIT)) continue;	/* Outside the range */
5584 				if (gmtplot_skip_second_annot (k, knots[i], knots_p, np, primary)) continue;	/* Minor tick marks skipped when coinciding with major */
5585 				if (horizontal)
5586 					PSL_plotsegment (PSL, x, 0.0, x, ((neg) ? -1.0 : 1.0) * GMT->current.setting.map_tick_length[k]);
5587 				else
5588 					PSL_plotsegment (PSL, 0.0, x, ((neg) ? -1.0 : 1.0) * GMT->current.setting.map_tick_length[k], x);
5589 			}
5590 		}
5591 
5592 		/* Then do annotations too - here just set text height/width parameters in PostScript */
5593 
5594 		if (do_annot) {
5595 			annot_pos = (T->type == 'A' || T->type == 'I') ? 1 : 0;	/* 1 means lower annotation, 0 means upper (close to axis) */
5596 			font = GMT->current.setting.font_annot[annot_pos];	/* Set the font to use */
5597 			form = gmt_setfont (GMT, &font);
5598 			if (A->type != GMT_TIME) PSL_settextmode (PSL, PSL_TXTMODE_MINUS);	/* Replace hyphens with minus */
5599 			if (first) {	/* Change up/down (neg) and/or flip coordinates (exch) */
5600 				PSL_command (PSL, "/MM {%s%sM} def\n", neg ? "neg " : "", (axis != GMT_X) ? "exch " : "");
5601 				first = false;
5602 				MM_set = true;
5603 			}
5604 			PSL_command (PSL, "/PSL_AH%d 0\n", annot_pos);	/* Largest annotation width (or height) so far */
5605 
5606 			for (i = 0; i < nx1; i++) {
5607 				if (gmtlib_annot_pos (GMT, val0, val1, T, &knots[i], &t_use)) continue;			/* Outside range */
5608 				if (axis == GMT_Z && fabs (knots[i] - GMT->current.proj.z_level) < GMT_CONV8_LIMIT) continue;	/* Skip z annotation coinciding with z-level plane */
5609 				x = (*xyz_fwd) (GMT, knots[i]);	/* Convert to inches on the page */
5610 				if (gmtplot_skip_end_annotation (GMT, axis, x, length)) continue;	/* Don't want annotations exactly at one or both ends of the axis */
5611 				if (!is_interval && gmtplot_skip_second_annot (k, knots[i], knots_p, np, primary)) continue;	/* Secondary annotation skipped when coinciding with primary annotation */
5612 				if (label_c && label_c[i] && label_c[i][0]) {
5613 					strncpy (string, label_c[i], GMT_LEN256-1);
5614 				}
5615 				else
5616 					gmtlib_get_coordinate_label (GMT, string, &GMT->current.plot.calclock, format, T, knots[i], delta);	/* Get annotation string */
5617 				PSL_deftextdim (PSL, ortho ? "-w" : "-h", font.size, string);
5618 				PSL_command (PSL, "mx\n");		/* Update the longest annotation stored in PSL_AH? */
5619 			}
5620 			PSL_command (PSL, "def\n");	/* Finalize the definition of longest (y-axis) or tallest (x-annotation) found */
5621 			if (angled && axis == GMT_X) {	/* Also need annotation width since a component of length is projected in the y-direction so label and title must be displaced by this amount */
5622 				/* Note: PSL_slant_y will also be used when placing a Cartesian frame title */
5623 				PSL_command (PSL, "/PSL_slant_y ");
5624 				if (label_c && label_c[nx1-1] && label_c[nx1-1][0])	/* We only use the string for the max annotation value at index nx1-1 */
5625 					strncpy (string, label_c[nx1-1], GMT_LEN256-1);
5626 				else
5627 					gmtlib_get_coordinate_label (GMT, string, &GMT->current.plot.calclock, format, T, knots[nx1-1], delta);	/* Get annotation string */
5628 				PSL_deftextdim (PSL, "-w", font.size, string);	/* Compute the width */
5629 				PSL_command (PSL, " %.12g mul def\n", sin_a);	/* Multiply this width by sine of the angle to get the y-component */
5630 				//PSL_command (PSL, " %.12g mul %g add def\n", sin_a, PSL_IZ (PSL, 72.0 * GMT->current.setting.font_annot[GMT_PRIMARY].size));	/* Multiply this width by sine of the angle to get the y-component */
5631 			}
5632 			else if (angled && axis == GMT_Y) {	/* Need a slant in x and reset PSL_AH? to 0 */
5633 				//PSL_command (PSL, "/PSL_slant_x PSL_AH%d %.12g mul %g add def\n", annot_pos, cos_a, PSL_IZ (PSL, 72.0 * GMT->current.setting.font_annot[GMT_PRIMARY].size));	/* Largest annotation width (or height) so far */
5634 				PSL_command (PSL, "/PSL_slant_x PSL_AH%d %.12g mul  def\n", annot_pos, cos_a);	/* Largest annotation width (or height) so far */
5635 				PSL_command (PSL, "/PSL_AH%d 0 def\n", annot_pos);	/* Largest annotation width (or height) so far */
5636 			}
5637 
5638 			if (annot_pos == 0)
5639 				PSL_command (PSL, "/PSL_A0_y PSL_A0_y %d add ", PSL_IZ (PSL, GMT->current.setting.map_annot_offset[annot_pos]));
5640 			else
5641 				PSL_command (PSL, "/PSL_A1_y PSL_A0_y PSL_A1_y mx %d add ", PSL_IZ (PSL, GMT->current.setting.map_annot_offset[annot_pos]));
5642 			if (faro) PSL_command (PSL, "PSL_AH%d add ", annot_pos);
5643 			PSL_command (PSL, "def\n");
5644 
5645 			for (i = 0; i < nx1; i++) {
5646 				if (gmtlib_annot_pos (GMT, val0, val1, T, &knots[i], &t_use)) continue;			/* Outside range */
5647 				if (axis == GMT_Z && fabs (knots[i] - GMT->current.proj.z_level) < GMT_CONV8_LIMIT) continue;	/* Skip z annotation coinciding with z-level plane */
5648 				x = (*xyz_fwd) (GMT, t_use);	/* Convert to inches on the page */
5649 				if (gmtplot_skip_end_annotation (GMT, axis, x, length)) continue;	/* Don't want annotations exactly at one or both ends of the axis */
5650 				if (!is_interval && gmtplot_skip_second_annot (k, knots[i], knots_p, np, primary)) continue;	/* Secondary annotation skipped when coinciding with primary annotation */
5651 				/* Move to new anchor point */
5652 				PSL_command (PSL, "%d PSL_A%d_y MM\n", PSL_IZ (PSL, x), annot_pos);
5653 				if (angled) {	/* Must compensate for rotated textbox sticking too close to axis */
5654 					if (below) /* S axis */
5655 						PSL_command (PSL, "0 PSL_AH%d 1 %.12g sub mul G\n", annot_pos, cos_a);
5656 					else /* N axis */
5657 						PSL_command (PSL, "0 PSL_AH%d %.12g mul G\n", annot_pos, cos_a);
5658 				}
5659 				if (label_c && label_c[i] && label_c[i][0])
5660 					strncpy (string, label_c[i], GMT_LEN256-1);
5661 				else
5662 					gmtlib_get_coordinate_label (GMT, string, &GMT->current.plot.calclock, format, T, knots[i], delta);	/* Get annotation string */
5663 				PSL_plottext (PSL, 0.0, 0.0, -font.size, string, text_angle, justify, form);
5664 			}
5665 			if (!faro) PSL_command (PSL, "/PSL_A%d_y PSL_A%d_y PSL_AH%d add def\n", annot_pos, annot_pos, annot_pos);
5666 		}
5667 
5668 		if (nx) gmt_M_free (GMT, knots);
5669 		if (label_c) {
5670 			for (i = 0; i < nx; i++) gmt_M_str_free (label_c[i]);
5671 			gmt_M_free (GMT, label_c);
5672 		}
5673 		PSL_settextmode (PSL, PSL_TXTMODE_HYPHEN);	/* Replace hyphens with minus */
5674 	}
5675 	if (np) gmt_M_free (GMT, knots_p);
5676 
5677 	/* Finally do axis label */
5678 
5679 	if (A->label[0] && annotate && !gmt_M_axis_is_geo_strict (GMT, axis)) {
5680 		unsigned int far_ = !below, l_just, L_axis = (axis == GMT_Z) ? GMT_X : axis;
5681 		double label_angle;
5682 		char *this_label = (far_ && A->secondary_label[0]) ? A->secondary_label : A->label;	/* Get primary or secondary axis label */
5683 
5684 		if (axis == GMT_Y && A->label_mode) {
5685 			l_just = (below) ? PSL_MR : PSL_ML;
5686 			label_angle = 0.0 + y_angle_add;
5687 		}
5688 		else {
5689 			double angle_add = (axis == GMT_X) ? x_angle_add : y_angle_add;
5690 			l_just = (axis == GMT_X) ? lx_just : ly_just;
5691 			label_angle = (horizontal ? 0.0 : 90.0) + angle_add;
5692 		}
5693 		if (!MM_set) PSL_command (PSL, "/MM {%s%sM} def\n", neg ? "neg " : "", (axis != GMT_X) ? "exch " : "");
5694 		form = gmt_setfont (GMT, &GMT->current.setting.font_label);
5695 		PSL_command (PSL, "/PSL_LH ");	/* PSL_LH is the height of the label text based on height of letter M */
5696 		PSL_deftextdim (PSL, "-h", GMT->current.setting.font_label.size, "M");
5697 		PSL_command (PSL, "def\n");
5698 		if (GMT->current.setting.map_label_mode[L_axis] == GMT_LABEL_AXIS) /* Base of label is a fixed distance from axis */
5699 			PSL_command (PSL, "/PSL_L_y %d %sdef\n", PSL_IZ (PSL, fabs (GMT->current.setting.map_label_offset[L_axis])), (neg == horizontal) ? "PSL_LH add " : "");
5700 		else
5701 			PSL_command (PSL, "/PSL_L_y PSL_A0_y PSL_A1_y mx %d add %sdef\n", PSL_IZ (PSL, GMT->current.setting.map_label_offset[L_axis]), (neg == horizontal) ? "PSL_LH add " : "");
5702 		/* Move to new anchor point for label */
5703 		if (angled && axis == GMT_X)	/* Add offset due to angled x-annotations */
5704 			PSL_command (PSL, "%d PSL_L_y PSL_slant_y add MM\n", PSL_IZ (PSL, 0.5 * length));
5705 		else if (angled && axis == GMT_Y)
5706 			PSL_command (PSL, "%d PSL_L_y PSL_slant_x add MM\n", PSL_IZ (PSL, 0.5 * length));
5707 		else
5708 			PSL_command (PSL, "%d PSL_L_y MM\n", PSL_IZ (PSL, 0.5 * length));
5709 		gmtplot_map_label (GMT, 0.0, 0.0, this_label, label_angle, l_just, axis, below);
5710 	}
5711 	else
5712 		PSL_command (PSL, "/PSL_LH 0 def /PSL_L_y PSL_A0_y PSL_A1_y mx def\n");
5713 	if (axis == GMT_X)
5714 		PSL_comment (PSL, below ? "End of lower x-axis\n" : "End of upper x-axis\n");
5715 	else if (axis == GMT_Y)
5716 		PSL_comment (PSL, below ? "End of left y-axis\n" : "End of right y-axis\n");
5717 	else
5718 		PSL_comment (PSL, below ? "End of front z-axis\n" : "End of back z-axis\n");
5719 	PSL_setorigin (PSL, -x0, -y0, 0.0, PSL_INV);
5720 
5721 	GMT->current.plot.substitute_pi = save_pi;
5722 }
5723 
gmtplot_the_side_we_cut(struct GMT_CTRL * GMT,double x,double y)5724 GMT_LOCAL unsigned int gmtplot_the_side_we_cut (struct GMT_CTRL *GMT, double x, double y) {
5725 	/* We know the (x,y) sits on the map boundary.  Depending on what map scheme
5726 	 * we have we use different strategies to find which side the cut is on. */
5727 	if (gmt_M_is_azimuthal (GMT) && !GMT->current.proj.polar) {	/* Not implemented yet */
5728 		/* Here the map boundaries are things like circles and there is no "side" */
5729 		return 9;
5730 	}
5731 	else if (GMT->common.R.oblique) {	/* Rectangular box, work with plot units */
5732 		/* We first check if the x value equal either the left or right x-coordinate
5733 		 * on the border for this y.  If that fails then we know it sits on the south
5734 		 * or north border and we can just pick the closest one. */
5735 		double width = gmt_half_map_width (GMT, y);	/* Width of map at current latitude (not all projections have straight w/e boundaries */
5736 		double xx = GMT->current.map.half_width - width;	/* x-coordinate on west boundary for this y */
5737 		if (doubleAlmostEqualZero (x, xx)) return 3;
5738 		xx = GMT->current.map.half_width + width;		/* x-coordinate on east boundary for this y */
5739 		if (doubleAlmostEqualZero (x, xx)) return 1;
5740 		if (y < GMT->current.map.half_height) return 0;		/* Sits on south border */
5741 		return 2;	/* OK, so it was north */
5742 	}
5743 	else {	/* Boundaries are meridians and parallels so just detect which extrema */
5744 		double lon, lat;
5745 		gmt_xy_to_geo (GMT, &lon, &lat, x, y);
5746 		if (doubleAlmostEqualZero (lon, GMT->common.R.wesn[XHI])) return 1;
5747 		else if (doubleAlmostEqualZero (lon, GMT->common.R.wesn[XLO])) return 3;
5748 		else if (doubleAlmostEqualZero (lat, GMT->common.R.wesn[YHI])) return 2;
5749 		else if (doubleAlmostEqualZero (lat, GMT->common.R.wesn[YLO])) return 0;
5750 		else return 9;	/* SHould not happen but if it does a 9 means "do not extend" */
5751 	}
5752 }
5753 
gmtplot_get_border_angle(struct GMT_CTRL * GMT,double xc,double yc,unsigned int side)5754 GMT_LOCAL double gmtplot_get_border_angle (struct GMT_CTRL *GMT, double xc, double yc, unsigned int side) {
5755 	/* Return the angle of the tangent to the map border at the point x, y.  This does
5756 	 * not work for circular boundaries yet */
5757 	double lon, lat, lon0, lat0, lon1, lat1, del, angle, x0, y0, x1, y1;
5758 	gmt_xy_to_geo (GMT, &lon, &lat, xc, yc);
5759 	switch (side) {
5760 		case 0:	case 2:	/* Pick two points along the parallel */
5761 			if (GMT->common.R.oblique) return (0.0);	/* We know it is horizontal */
5762 			del = (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / 360.0;
5763 			//if (del > 0.1) del = 0.1;
5764 			lon0 = lon - del;	lon1 = lon + del;	lat0 = lat1 = lat;
5765 			break;
5766 		default:	/* Pick two points along the meridian */
5767 			if (GMT->common.R.oblique) return (90.0);	/* We know it is vertical */
5768 			del = (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / 180.0;
5769 			//if (del > 0.1) del = 0.1;
5770 			/* Make sure lats do not exceed the limits */
5771 			lat0 = MAX (-90.0, lat - del);
5772 			lat1 = MIN (+90.0, lat + del);	lon0 = lon1 = lon;
5773 			break;
5774 	}
5775 	gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
5776 	gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
5777 	angle = atan2 (y1 - y0, x1 - x0) * R2D;	/* Get angle of line in direction from "on" point to "in" point */
5778 
5779 	return angle;
5780 }
5781 
5782 #if 0
5783 /* Saving this for now in case something blows */
5784 GMT_LOCAL void gmtplot_get_outside_point_extension_orig (struct GMT_CTRL *GMT, double x_on, double y_on, double x_in, double y_in, double *x_off, double *y_off) {
5785 	/* The point (x_on, y_on) is known to sit on a rectangular map border, and the point (x_in, y_in) is either inside or is on another border point.
5786 	 * A line will be drawn between the two points, but given the finite pen width we must adjust the starting and|or end point so that
5787 	 * the pen is not clipped, resulting in a gap between the end and the border. */
5788 	double W = 0.5 * GMT->current.setting.ps_penwidth * GMT->session.u2u[GMT_PT][GMT_INCH];	/* Half the current pen width in inches */
5789 	double L, angle, dx, dy, tan_angle;
5790 	static char side[] = "SENW";
5791 	unsigned int k = gmtplot_the_side_we_cut (GMT, x_on, y_on);	/* Which border side is this point on? */
5792 	dx = x_in - x_on;	dy = y_in - y_on;		/* Get coordinate increments from the "on" point to the "in" point */
5793 	angle = atan2 (dy, dx) * R2D;				/* Get angle of line in direction from "on" point to "in" point */
5794 	tan_angle = tand (angle);				/* Compute the tangent of that line direction (-180 to +180)  */
5795 	/* Need to modify the stuff below which assumes border_angle = 0|90 */
5796 	if (k%2 == 0) {	/* Cutting the S or N border */
5797 		if (doubleAlmostEqualZero (dx, 0.0))		/* Line is almost vertical; no need to extend it when at a horizontal border */
5798 			L = 0.0;
5799 		else if (doubleAlmostEqualZero (dy, 0.0))	/* Line is almost horizontal; would get infinity so truncate to width of map */
5800 			L = GMT->current.map.width;
5801 		else	/* Safe to get width (but still truncate to width of map) */
5802 			L = MIN (fabs (W / tan_angle), GMT->current.map.half_width);	/* L is positive */
5803 	}
5804 	else {	/* Cutting the E or W border */
5805 		if (doubleAlmostEqualZero (dx, 0.0))		/* Line is almost vertical; would get infinity so clip truncate height of map */
5806 			L = GMT->current.map.height;
5807 		else if (doubleAlmostEqualZero (dy, 0.0))	/* Line is almost horizontal; no need to extend it when at a vertical border */
5808 			L = 0.0;
5809 		else	/* Safe to get width (but still truncate to height of map) */
5810 			L = MIN (fabs (W * tan_angle), GMT->current.map.height);	/* L is positive */
5811 	}
5812 	/* Compute the coordinates of the point at a distance L away from clip point in the direction of angle */
5813 	*x_off = x_on - L * cosd (angle);
5814 	*y_off = y_on - L * sind (angle);
5815 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Extend from (%g, %g) to (%g, %g) in direction %g by %g\" on %c side\n", *x_off, *y_off, x_on, y_on, angle, L, side[k]);
5816 }
5817 #endif
5818 
gmtplot_get_outside_point_extension(struct GMT_CTRL * GMT,double x_on,double y_on,double x_in,double y_in,double * x_off,double * y_off)5819 GMT_LOCAL void gmtplot_get_outside_point_extension (struct GMT_CTRL *GMT, double x_on, double y_on, double x_in, double y_in, double *x_off, double *y_off) {
5820 	/* The point (x_on, y_on) is known to sit on a rectangular map border, and the point (x_in, y_in) is either inside or is on another border point.
5821 	 * A line will be drawn between the two points, but given the finite pen width we must adjust the starting and|or end point so that
5822 	 * the pen is not clipped, resulting in a gap between the end and the border. */
5823 	double W = 0.5 * GMT->current.setting.ps_penwidth * GMT->session.u2u[GMT_PT][GMT_INCH];	/* Half the current pen width in inches */
5824 	double L, angle, dx, dy, tan_angle, border_angle, d_angle;
5825 	static char side[] = "SENW";
5826 	unsigned int k = gmtplot_the_side_we_cut (GMT, x_on, y_on);	/* Which border side is this point on? */
5827 	if (k == 9) {	/* No can do yet */
5828 		*x_off = x_on; *y_off = y_on; return;
5829 	}
5830 	dx = x_in - x_on;	dy = y_in - y_on;		/* Get coordinate increments from the "on" point to the "in" point */
5831 	angle = atan2 (dy, dx) * R2D;				/* Get angle of line in direction from "on" point to "in" point */
5832 	tan_angle = tand (angle);				/* Compute the tangent of that line direction (-180 to +180)  */
5833 	border_angle = gmtplot_get_border_angle (GMT, x_on, y_on, k);
5834 	d_angle = angle - border_angle;		/* Compute the tangent of that line direction (-180 to +180)  */
5835 	tan_angle = tand (d_angle);		/* Compute the tangent of that line direction (-180 to +180)  */
5836 	/* The is_dnan catches bad cases yielding a NaN angle */
5837 	if (gmt_M_is_dnan (d_angle) || doubleAlmostEqualZero (fabs (d_angle), 90.0))		/* Line is orthogonal to border; no need to extend it when at a horizontal border */
5838 		L = 0.0;
5839 	else if (doubleAlmostEqualZero (d_angle, 0.0))	/* Line is parallel to border; would get infinity so truncate to width of map */
5840 		//L = GMT->current.map.width; /* What we want but still picking up stray horizontal lines, hence L = 0 for now */
5841 		L = 0.0;
5842 	else	/* Safe to get width (but still truncate to width of map) */
5843 		L = MIN (fabs (W / tan_angle), GMT->current.map.half_width);	/* L is positive */
5844 	/* Compute the coordinates of the point at a distance L away from clip point in the direction of angle */
5845 	*x_off = x_on - L * cosd (angle);
5846 	*y_off = y_on - L * sind (angle);
5847 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Extend from (%g, %g) to crossing point (%g, %g) in direction %g by %g\" on %c side with border angle %g and delta angle %g\n", *x_off, *y_off, x_on, y_on, angle, L, side[k], border_angle, d_angle);
5848 }
5849 
gmtplot_these_are_duplicates(double x0,double y0,double x1,double y1)5850 GMT_LOCAL bool gmtplot_these_are_duplicates (double x0, double y0, double x1, double y1) {
5851 	/* Return true if (x0,y0) and (x1,y1) is the same point within bitnoise */
5852 	if (!doubleAlmostEqualZero (x0, x1)) return false;
5853 	if (!doubleAlmostEqualZero (y0, y1)) return false;
5854 	return true;
5855 }
5856 
gmt_plot_line(struct GMT_CTRL * GMT,double * x,double * y,unsigned int * pen,uint64_t n,unsigned int mode)5857 void gmt_plot_line (struct GMT_CTRL *GMT, double *x, double *y, unsigned int *pen, uint64_t n, unsigned int mode) {
5858 	/* Mode = PSL_LINEAR [0] or PSL_BEZIER [1] */
5859 	/* Drawing lines of finite thickness that cross map boundaries is tricky because the exit or entry point
5860 	 * refers to the center line of a finite thickness line but the wider part of the line do not share those
5861 	 * coordinates.  To avoid ugly line gaps we must extend such lines a bit beyond the border and clip so that
5862 	 * things look pretty.  For now we only do this for a few situations:
5863 	 * 1) The map border must be rectangular
5864 	 * 2) The line must be a linear spline and not the Bezier spline
5865 	 * Hopefully we can work to avoid the first case soon */
5866 
5867 	uint64_t i, j, im1, ip1, k;
5868 	int way;
5869 	bool close, stop;
5870 	double x_cross[2], y_cross[2];
5871 	double x_ext, y_ext;	/* Coordinates of the external helper point */
5872 	unsigned int start_pen = PSL_MOVE, end_pen = PSL_STROKE;	/* Default pen code of given line endings */
5873 	struct PSL_CTRL *PSL= GMT->PSL;
5874 
5875 	if (n < 2) return;
5876 
5877 	gmtplot_NaN_pen_up (x, y, pen, n);	/* Ensure we don't have NaNs in the coordinates */
5878 
5879 	/* First skip any repeating PSL_MOVE in the beginning since only the last one will matter */
5880 	i = 0;
5881 	while (i < (n-1) && (pen[i+1] & PSL_MOVE)) i++;	/* Skip repeating pen & PSL_MOVE in beginning */
5882 	if ((n-i) < 2) return;	/* Less than 2 points is not a line */
5883 	j = i;	/* j is now the first valid point with a PSL_MOVE */
5884 	/* Then we want to prevent duplicate points since the first point may have a different pen (e.g., include PSL_CLIP) */
5885 	while (i < (n-1) && gmtplot_these_are_duplicates (x[j], y[j], x[i+1], y[i+1])) i++;	/* Skip duplicate points in beginning */
5886 	if ((n-i) < 2) return;	/* Less than 2 points is not a line */
5887 	if (i > j) pen[i] = pen[j];	/* Skipped initial duplicates but must maintain the initial pen code */
5888 	/* Likewise, skip any repeating PSL_MOVE at the end */
5889 	while (n > 1 && (pen[n-1] & PSL_MOVE)) n--;	/* Cut off repeating pen & PSL_MOVE at end */
5890 	if ((n-i) < 2) return;	/* Less than 2 points is not a line */
5891 	/* Then we want to prevent duplicate points at end since the last valid point may have a different pen (e.g., include PSL_CLIP) */
5892 	j = n - 1;	/* j is now last point after initial skip test */
5893 	while (n > 1 && gmtplot_these_are_duplicates (x[j], y[j], x[n-2], y[n-2])) n--;	/* Skip duplicate points at end */
5894 	if ((n-i) < 2) return;	/* Less than 2 points is not a line */
5895 	if (n <= j) {	/* Got a duplicate at the end */
5896 		if ((pen[n-1] & PSL_CLIP) == 0) pen[n-1] = pen[j];	/* Skipped trailing duplicates but must maintain initial pen code of last valid point unless has clip information */
5897 		while (n > 1 && (pen[n-1] & PSL_MOVE)) n--;	/* Cut off repeating pen & PSL_MOVE at end that might result after removing that duplicate */
5898 	}
5899 	for (j = i + 1; j < n && !(pen[j] & PSL_MOVE); j++);	/* j == n means no PSL_MOVEs present */
5900 	close = (j == n) ? (hypot (x[n-1] - x[i], y[n-1] - y[i]) < GMT_CONV4_LIMIT) : false;
5901 
5902 	/* First see if we can use the PSL_plotline call directly to save points */
5903 
5904 	for (j = i + 1, stop = false; !stop && j < n; j++) stop = (pen[j] & PSL_MOVE || (*GMT->current.map.jump) (GMT, x[j-1], y[j-1], x[j], y[j]));
5905 	if (!stop) {	/* Safe to draw single line directly since no jumping around */
5906 		if (mode == PSL_BEZIER)
5907 			PSL_plotcurve (PSL, &x[i], &y[i], (int)(n - i), PSL_MOVE + PSL_STROKE + close * PSL_CLOSE);
5908 		else {	/* Pay more attention here since exiting lines of finite thickness may need some corrections */
5909 			//bool rect = gmt_M_is_rect_graticule (GMT) || GMT->common.R.oblique;	/* We have a rectangular map boundary */
5910 			bool rect = !(gmt_M_is_azimuthal (GMT) && !GMT->current.proj.polar);
5911 			for (j = i + 1, stop = false; !stop && j < n; j++) stop = (pen[j] & PSL_CLIP);	/* Look for clipped end points */
5912 			if (!rect || !stop)	/* No troubled clip points or it is not a rectangular border */
5913 			//if (!stop)	/* No troubled clip points or it is not a rectangular border */
5914 				PSL_plotline (PSL, &x[i], &y[i], (int)(n - i), PSL_MOVE + PSL_STROKE + close * PSL_CLOSE);
5915 			else {	/* At least one clip point on a rectangular border will need a line extension  */
5916 				if (pen[i] & PSL_CLIP) { 	/* Must add extra point before the first point */
5917 					gmtplot_get_outside_point_extension (GMT, x[i], y[i], x[i+1], y[i+1], &x_ext, &y_ext);
5918 					start_pen = PSL_DRAW;	/* Cannot have a move when drawing line after this point */
5919 					PSL_plotpoint (PSL, x_ext, y_ext, PSL_MOVE);	/* Lay down new start point */
5920 				}
5921 				j = n - 1;	/* Last point index */
5922 				if (pen[j] & PSL_CLIP) {	/* Must draw to extra point after the last point, so set end_pen to draw since not done yet */
5923 					gmtplot_get_outside_point_extension (GMT, x[j], y[j], x[j-1], y[j-1], &x_ext, &y_ext);
5924 					end_pen = PSL_DRAW;
5925 				}
5926 				PSL_plotline (PSL, &x[i], &y[i], (int)(n - i), start_pen|end_pen);	/* Lay down the original line, possibly with adjusted pen code */
5927 				if (end_pen == PSL_DRAW) PSL_plotpoint (PSL, x_ext, y_ext, PSL_STROKE);	/* Add extension from last point to outside helper point */
5928 			}
5929 		}
5930 		return;
5931 	}
5932 
5933 	/* Here we must check for jumps, pen changes etc */
5934 
5935 	 if (GMT->common.R.oblique) {
5936 		  double www = GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO];
5937 		  for (k = i+1; k < n; k++) {
5938 				if (doubleAlmostEqual (fabs (x[k-1] - x[k]), www) && pen[k] & PSL_CLIP) {
5939 					 if (pen[k] == PSL_CLIP) /* Fix a mistake made earlier */
5940 						  pen[k] = PSL_MOVE;
5941 				}
5942 		  }
5943 	 }
5944 	start_pen = pen[i];
5945 	if (pen[i] & PSL_CLIP) { 	/* Must add extra extension point before the first point */
5946 		gmtplot_get_outside_point_extension (GMT, x[i], y[i], x[i+1], y[i+1], &x_ext, &y_ext);
5947 		start_pen = PSL_DRAW;	/* Cannot have a move when drawing line after this point */
5948 		PSL_plotpoint (PSL, x_ext, y_ext, PSL_MOVE);	/* Lay down new start point */
5949 	}
5950 	PSL_plotpoint (PSL, x[i], y[i], start_pen);
5951 
5952 	i++;
5953 	while (i < n) {
5954 		im1 = i - 1;
5955 		ip1 = i + 1;
5956 		if (pen[im1] & PSL_MOVE && pen[i] == PSL_DRAW && (ip1 == n || pen[ip1] & PSL_MOVE) && GMT->current.proj.projection_GMT == GMT_OBLIQUE_MERC && fabs (y[i]-y[im1]) < 0.001) {
5957 			double mw = 2.0 * gmt_half_map_width (GMT, y[i]);	/* Get map width */
5958 			/* We have a single 2-point ~horizontal line segment with move, draw, move.  Check if the draw is across the entire oblique Mercator map */
5959 			if (doubleAlmostEqual (fabs (x[i]-x[im1]), mw)) {	/* Yes, so skip such stray lines across map */
5960 				/* This fix was implemented in response to the problem first illustrated in test/psbasemap/oblique.sh.
5961 				 * Ideally, we should fix this upstream but not that easy to follow the logic. */
5962 				i++;
5963 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Skipping a stray line across map.\n");
5964 				continue;
5965 			}
5966 		}
5967 		if (pen[i] == pen[im1] && (way = (*GMT->current.map.jump) (GMT, x[im1], y[im1], x[i], y[i]))) {	/* Jumped across the map */
5968 			(*GMT->current.map.get_crossings) (GMT, x_cross, y_cross, x[im1], y[im1], x[i], y[i]);
5969 			if (way == -1) {	/* Add left border point */
5970 				PSL_plotpoint (PSL, x_cross[0], y_cross[0], PSL_DRAW);	/* Draw to left boundary... */
5971 				gmtplot_get_outside_point_extension (GMT, x[im1], y[im1], x_cross[0], y_cross[0], &x_ext, &y_ext);	/* And extend further to get proper clipping */
5972 				PSL_plotpoint (PSL, x_ext, y_ext, PSL_DRAW);	/* Draw to outside point */
5973 				gmtplot_get_outside_point_extension (GMT, x_cross[1], y_cross[1], x[i], y[i], &x_ext, &y_ext);	/* Find outside point on the right */
5974 				PSL_plotpoint (PSL, x_ext, y_ext, PSL_MOVE);	/* Lay down new outside point */
5975 				PSL_plotpoint (PSL, x_cross[1], y_cross[1], PSL_DRAW);	/* ...then jump to the right boundary */
5976 			}
5977 			else {
5978 				PSL_plotpoint (PSL, x_cross[1], y_cross[1], PSL_DRAW);	/* Draw to right boundary... */
5979 				gmtplot_get_outside_point_extension (GMT, x[im1], y[im1], x_cross[1], y_cross[1], &x_ext, &y_ext);	/* And extend further to get proper clipping */
5980 				PSL_plotpoint (PSL, x_ext, y_ext, PSL_DRAW);	/* Draw to outside point */
5981 				gmtplot_get_outside_point_extension (GMT, x[i], y[i], x_cross[0], y_cross[0], &x_ext, &y_ext);	/* Find outside point on the left */
5982 				PSL_plotpoint (PSL, x_ext, y_ext, PSL_MOVE);	/* Lay down new outside point */
5983 				PSL_plotpoint (PSL, x_cross[0], y_cross[0], PSL_DRAW);	/* ...then jump to the left boundary */
5984 			}
5985 			close = false;
5986 		}
5987 		if (pen[i] & PSL_CLIP) { 	/* Must add extra point before/after the next point */
5988 			if (pen[i] & PSL_MOVE) {	/* Start of new line, so find external point and lay down before clipped point */
5989 				gmtplot_get_outside_point_extension (GMT, x[i], y[i], x[i+1], y[i+1], &x_ext, &y_ext);
5990 				PSL_plotpoint (PSL, x_ext, y_ext, PSL_MOVE);	/* Lay down new start point */
5991 				PSL_plotpoint (PSL, x[i], y[i], PSL_DRAW);
5992 			}
5993 			else {	/* End of a line, add extension after placing the last point of the line  */
5994 				PSL_plotpoint (PSL, x[i], y[i], PSL_DRAW);
5995 				gmtplot_get_outside_point_extension (GMT, x[i], y[i], x[i-1], y[i-1], &x_ext, &y_ext);
5996 				PSL_plotpoint (PSL, x_ext, y_ext, PSL_DRAW);	/* Lay down new start point */
5997 			}
5998 		}
5999 		else	/* Just another point in the middle of the line */
6000 			PSL_plotpoint (PSL, x[i], y[i], pen[i]);
6001 		i++;
6002 	}
6003 	PSL_command (PSL, close ? "P S\n" : "S\n");
6004 }
6005 
gmt_xy_axis2(struct GMT_CTRL * GMT,double x0,double y0,double length,double val0,double val1,struct GMT_PLOT_AXIS * A,bool below,bool annotate,unsigned side)6006 void gmt_xy_axis2 (struct GMT_CTRL *GMT, double x0, double y0, double length, double val0, double val1, struct GMT_PLOT_AXIS *A, bool below, bool annotate, unsigned side) {
6007     /* Only used in psscale.c.  Because gmt_xy_axis does not do gridlines (done in gmt_map_basemap at a higher level),
6008      * we do that separately in psscale.c */
6009 	if (annotate) side |= GMT_AXIS_BARB;
6010 	gmt_xy_axis (GMT, x0, y0, length, val0, val1, A, below, side);
6011 }
6012 
gmt_linearx_grid(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double w,double e,double s,double n,double dval)6013 void gmt_linearx_grid (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double w, double e, double s, double n, double dval) {
6014 	double *x = NULL, ys, yn, p_cap = 0.0, cap_start[2] = {0.0, 0.0}, cap_stop[2] = {0.0, 0.0};
6015 	unsigned int idup = 0, i, nx;
6016 	bool cap = false;
6017 	char *type = (gmt_M_x_is_lon (GMT, GMT_IN)) ? "meridian" : "x";
6018 
6019 	/* Do we have duplicate e and w boundaries ? */
6020 	idup = (gmt_M_is_azimuthal(GMT) && doubleAlmostEqual(e-w, 360.0)) ? 1 : 0;
6021 
6022 	if (gmt_M_pole_is_point(GMT)) {	/* Might have two separate domains of gridlines */
6023 		if (GMT->current.proj.projection_GMT == GMT_POLAR) {	/* Different for polar graphs since "lat" = 0 is at the center */
6024 			ys = cap_stop[0] = cap_stop[1] = p_cap = 90.0 - GMT->current.setting.map_polar_cap[0];
6025 			yn = n;
6026 			cap_start[0] = cap_start[1] = 0.0;
6027 		}
6028 		else {
6029 			p_cap = GMT->current.setting.map_polar_cap[0];
6030 			ys = MAX (s, -p_cap);
6031 			yn = MIN (n, p_cap);
6032 			cap_start[0] = s;
6033 			cap_stop[0]  = -p_cap ;
6034 			cap_start[1] = p_cap;
6035 			cap_stop[1]  = n;
6036 		}
6037 		cap = !doubleAlmostEqual (GMT->current.setting.map_polar_cap[0], 90.0);
6038 	}
6039 	else {
6040 		ys = s;
6041 		yn = n;
6042 	}
6043 	nx = gmtlib_linear_array (GMT, w, e, dval, GMT->current.map.frame.axis[GMT_X].phase, &x);
6044 	if (idup && !gmt_M_360_range(x[0],x[nx-1])) idup = 0;	/* Probably due to phase we don't need to remove any duplicate */
6045 	for (i = 0; i < nx - idup; i++)  {
6046 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Draw %s = %g from %g to %g\n", type, x[i], ys, yn);
6047 		gmtplot_map_lonline (GMT, PSL, x[i], ys, yn);
6048 	}
6049 	if (nx) gmt_M_free (GMT, x);
6050 
6051 	if (cap) {	/* Also draw the polar cap(s) */
6052 		nx = 0;
6053 		if (s < -GMT->current.setting.map_polar_cap[0]) {	/* Must draw some or all of the S polar cap */
6054 			nx = gmtlib_linear_array (GMT, w, e, GMT->current.setting.map_polar_cap[1], GMT->current.map.frame.axis[GMT_X].phase, &x);
6055 			for (i = 0; i < nx - idup; i++) {
6056 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Draw S polar cap %s = %g from %g to %g\n", type, x[i], ys, yn);
6057 				gmtplot_map_lonline (GMT, PSL, x[i], cap_start[0], cap_stop[0]);
6058 			}
6059 			gmtplot_map_latline (GMT, PSL, -p_cap, w, e);
6060 		}
6061 		if (n > GMT->current.setting.map_polar_cap[0]) {	/* Must draw some or all of the N polar cap */
6062 			if (nx == 0) nx = gmtlib_linear_array (GMT, w, e, GMT->current.setting.map_polar_cap[1], GMT->current.map.frame.axis[GMT_X].phase, &x);
6063 			for (i = 0; i < nx - idup; i++) {
6064 				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Draw N polar cap %s = %g from %g to %g\n", type, x[i], ys, yn);
6065 				gmtplot_map_lonline (GMT, PSL, x[i], cap_start[1], cap_stop[1]);
6066 			}
6067 			gmtplot_map_latline (GMT, PSL, p_cap, w, e);
6068 		}
6069 		if (nx) gmt_M_free (GMT, x);
6070 	}
6071 }
6072 
gmtplot_check_primary_secondary(struct GMT_CTRL * GMT)6073 GMT_LOCAL void gmtplot_check_primary_secondary (struct GMT_CTRL *GMT) {
6074 	/* We requires the primary interval to be finer than the secondary interval (when given).
6075 	 * However, it is easy to get this mixed up. Here, we make the check and if we find
6076 	 * the primary interval is larger than the secondary we warn. */
6077 	struct GMT_PLOT_AXIS *A = NULL;
6078 	struct GMT_PLOT_AXIS_ITEM *P = NULL, *S = NULL;
6079 	static char *kind[3] = {"annotation", "tick", "grid-line"};
6080 	static char *axis[2][3] = { {"x", "y", "z"}, {"longitude", "latitude", "z"}};
6081 	unsigned int no, k, type;
6082 	double dP, dS;
6083 
6084 	type = gmt_M_is_geographic (GMT, GMT_IN);
6085 	for (no = 0; no <= GMT_Z; no++) {
6086 		if (no < GMT_Z && !(GMT->current.map.frame.side[no] && GMT->current.map.frame.side[no+2])) continue;	/* That axis will not be annotated */
6087 		A = &GMT->current.map.frame.axis[no];
6088 		if (A->type == GMT_TIME) continue;	/* We assume those are set correctly */
6089 		for (k = 0; k < 3; k++) {	/* For each axis */
6090 			P = &(A->item[2*k]);	/* Primary item */
6091 			S = &(A->item[2*k+1]);	/* Secondary item */
6092 			if ((P->active + S->active) < 2) continue;	/* Primary and secondary not both set */
6093 			if (P->special || S->special) continue;		/* Primary and/or secondary are custom so no fixed interval to compare with */
6094 			/* Here they are both set, check the intervals */
6095 			dP = gmtlib_get_map_interval (GMT, A->type, P);
6096 			dS = gmtlib_get_map_interval (GMT, A->type, S);
6097 			if (dP > dS) {	/* Must warn since primary should be the finer-grained interval */
6098 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your primary %s %s interval exceeds the secondary interval.\n", axis[type][no], kind[k]);
6099 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT expects it to be the other way around (primary annotations are closest to axis, secondary are further away)\n");
6100 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Consider correcting your command - proceeding with your selections\n");
6101 			}
6102 		}
6103 	}
6104 }
6105 
6106 
gmtplot_map_griditems(struct GMT_CTRL * GMT)6107 GMT_LOCAL void gmtplot_map_griditems (struct GMT_CTRL *GMT) {
6108 	double w, e, s, n;
6109 	struct PSL_CTRL *PSL= GMT->PSL;
6110 
6111 	if (GMT->current.map.frame.gridline_plotted) return;	/* Already plotted */
6112 	if (GMT->current.map.frame.order == GMT_BASEMAP_BEFORE && GMT->current.map.frame.basemap_flag & GMT_BASEMAP_GRID_AFTER) return;	/* Wrong order */
6113 	if (GMT->current.map.frame.order == GMT_BASEMAP_AFTER  && !(GMT->current.map.frame.basemap_flag & GMT_BASEMAP_GRID_AFTER)) return;	/* Wrong order */
6114 
6115 	if (GMT->common.B.active[GMT_PRIMARY] && GMT->common.B.active[GMT_SECONDARY]) {
6116 		/* Make sure primary intervals are < than secondary intervals, otherwise we swap them */
6117 		gmtplot_check_primary_secondary (GMT);
6118 	}
6119 
6120 	w = GMT->common.R.wesn[XLO], e = GMT->common.R.wesn[XHI], s = GMT->common.R.wesn[YLO], n = GMT->common.R.wesn[YHI];
6121 
6122 	PSL_comment (PSL, "Start of gridlines - if any\n");
6123 
6124 	PSL_setdash (PSL, NULL, 0);	/* To ensure no dashed pens are set prior */
6125 
6126 	if (GMT->current.proj.got_azimuths) gmt_M_uint_swap (GMT->current.map.frame.side[E_SIDE], GMT->current.map.frame.side[W_SIDE]);	/* Temporary swap to trick justify machinery */
6127 
6128 	gmtplot_map_gridlines (GMT, PSL, w, e, s, n);	/* At most only one of these three would kick in */
6129 	gmtplot_map_gridcross (GMT, PSL, w, e, s, n);
6130 	gmtplot_map_gridticks (GMT, PSL, w, e, s, n);
6131 
6132 	if (GMT->current.proj.got_azimuths) gmt_M_uint_swap (GMT->current.map.frame.side[E_SIDE], GMT->current.map.frame.side[W_SIDE]);	/* Undo temporary swap */
6133 
6134 	GMT->current.map.frame.gridline_plotted = true;	/* Since gmtplot_map_gridlines is called in gmt_map_basemap we flag if we already have done this step */
6135 }
6136 
gmtplot_map_tick_marks(struct GMT_CTRL * GMT)6137 GMT_LOCAL void gmtplot_map_tick_marks (struct GMT_CTRL *GMT) {
6138 	double w, e, s, n;
6139 	struct PSL_CTRL *PSL= GMT->PSL;
6140 
6141 	if (GMT->current.map.frame.order == GMT_BASEMAP_BEFORE && GMT->current.map.frame.basemap_flag & GMT_BASEMAP_ANNOT_AFTER) return;	/* Wrong order */
6142 	if (GMT->current.map.frame.order == GMT_BASEMAP_AFTER  && !(GMT->current.map.frame.basemap_flag & GMT_BASEMAP_ANNOT_AFTER)) return;	/* Wrong order */
6143 
6144 	w = GMT->common.R.wesn[XLO], e = GMT->common.R.wesn[XHI], s = GMT->common.R.wesn[YLO], n = GMT->common.R.wesn[YHI];
6145 
6146 	gmtplot_map_tickmarks (GMT, PSL, w, e, s, n);
6147 }
6148 
gmtplot_map_annotations(struct GMT_CTRL * GMT)6149 GMT_LOCAL void gmtplot_map_annotations (struct GMT_CTRL *GMT) {
6150 	double w, e, s, n;
6151 	struct PSL_CTRL *PSL= GMT->PSL;
6152 
6153 	if (GMT->current.map.frame.order == GMT_BASEMAP_BEFORE && GMT->current.map.frame.basemap_flag & GMT_BASEMAP_ANNOT_AFTER) return;	/* Wrong order */
6154 	if (GMT->current.map.frame.order == GMT_BASEMAP_AFTER  && !(GMT->current.map.frame.basemap_flag & GMT_BASEMAP_ANNOT_AFTER)) return;	/* Wrong order */
6155 
6156 	w = GMT->common.R.wesn[XLO], e = GMT->common.R.wesn[XHI], s = GMT->common.R.wesn[YLO], n = GMT->common.R.wesn[YHI];
6157 
6158 	gmtplot_map_annotate (GMT, PSL, w, e, s, n);
6159 }
6160 
gmtplot_title_breaks_decode(struct GMT_CTRL * GMT,const char * in_string,char * out_string)6161 void gmtplot_title_breaks_decode (struct GMT_CTRL *GMT, const char *in_string, char *out_string) {
6162 	/* Deal with long-form @^ or <break> strings in title and subtitle and replace with GMT_ASCII_GS */
6163 	unsigned int i, o, kl[2] = {2, 7}, id;
6164 	char *kw[2] = {"@^", "<break>"};
6165 	gmt_M_unused (GMT);
6166 	if (in_string[0] == '\0') return;	/* Got nothing */
6167 	if (strstr (in_string, kw[1]))
6168 		id = 1;
6169 	else if (strstr (in_string, kw[0]))
6170 		id = 0;
6171 	else {	/* No markers given */
6172 		strncpy (out_string, in_string, GMT_LEN256);
6173 		return;
6174 	}
6175 	/* Here we must replace kl[id] with GMT_ASCII_GS */
6176 	for (i = o = 0; i < strlen (in_string); i++) {
6177 		if (!strncmp (&in_string[i], kw[id], kl[id]))
6178 			out_string[o++] = GMT_ASCII_GS, i += (kl[id] - 1);	/* Skip one less, allowing for i++ */
6179 		else
6180 			out_string[o++] = in_string[i];
6181 	}
6182 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Converted %s to %s\n", in_string, out_string);
6183 }
6184 
gmtplot_place_latex_eps(struct GMT_CTRL * GMT,double x,double y,struct GMT_FONT * F,const char * string)6185 GMT_LOCAL double gmtplot_place_latex_eps (struct GMT_CTRL *GMT, double x, double y, struct GMT_FONT *F, const char *string) {
6186 	/* Detected LaTeX commands, i.e., "....@[LaTeX...@[ ..." or  "....<math>LaTeX...</math> ..." */
6187 	bool pos_set = (gmt_M_is_zero (x) && gmt_M_is_zero (y));
6188 	double w, h;
6189 	unsigned char *eps = NULL;
6190 	struct imageinfo header;
6191 
6192 	if ((eps = gmtplot_latex_eps (GMT, F, string, &header)) == NULL) {
6193 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Conversion of LaTeX string to EPS failed\n");
6194 		return 0;	/* Done */
6195 	}
6196 	/* Scale up EPS dimensions by the ratio of title font size to LaTeX default size of 10p */
6197 	w = (header.width  / 72.0) * (F->size / 10.0);
6198 	h = (header.height / 72.0) * (F->size / 10.0);
6199 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtplot_place_latex_eps: Conversion of LaTeX string gave dimensions %g x %g\n", w, h);
6200 	/* Place EPS file instead of text */
6201 	PSL_command (GMT->PSL, "V\n");	/* Keep the relative changes inside a save/restore block */
6202 	if (pos_set)
6203 		PSL_command (GMT->PSL, "currentpoint T\n");	/* Translate to currentpoint since already set by calling function */
6204 	PSL_plotlatexeps (GMT->PSL, x, y, w, h, PSL_BC, eps, F->fill.rgb, &header);
6205 	PSL_command (GMT->PSL, "U\n");
6206 	PSL_free (eps);
6207 	return h;
6208 }
6209 
gmt_map_title(struct GMT_CTRL * GMT,double x,double y)6210 void gmt_map_title (struct GMT_CTRL *GMT, double x, double y) {
6211 	/* Place plot title and optionally subtitle, which both may be a single line or multiple lines.
6212 	 * Note, when x = y = 0 it means current point has already been selected so we must store that and keep
6213 	 * moving up for each line in the multi-line title.
6214 	 */
6215 	bool pos_set = (gmt_M_is_zero (x) && gmt_M_is_zero (y)), head_latex = false;
6216 	double sign = (pos_set) ? -1.0 : +1.0, y_next = 0.0, line_spacing;
6217 	unsigned int n_breaks_T = 0, n_breaks_S, k, form;
6218 	char *word = NULL, title[GMT_LEN256] = {""}, subtitle[GMT_LEN256] = {""}, sep[2] = {""};
6219 	struct PSL_CTRL *PSL= GMT->PSL;
6220 
6221 	if (!GMT->current.map.frame.header[0]) return;	/* No title given, so cannot have subtitle either */
6222 
6223 	if ((head_latex = gmt_text_is_latex (GMT, GMT->current.map.frame.header)) && gmtplot_has_title_breaks (GMT, GMT->current.map.frame.header)) {
6224 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "LaTeX expressions are only allowed in single-line titles\n");
6225 		return;	/* Done */
6226 	}
6227 	if (GMT->current.map.frame.sub_header[0] && gmt_text_is_latex (GMT, GMT->current.map.frame.sub_header) && gmtplot_has_title_breaks (GMT, GMT->current.map.frame.sub_header)) {
6228 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "LaTeX expressions are only allowed in single-line subtitles\n");
6229 		return;	/* Done */
6230 	}
6231 
6232 	/* OK, here we know that if there is LaTeX it is only a single-line string, else it could be multi-line titles and subtitles */
6233 
6234 	gmtplot_title_breaks_decode (GMT, GMT->current.map.frame.header, title);
6235 	gmtplot_title_breaks_decode (GMT, GMT->current.map.frame.sub_header, subtitle);
6236 
6237 	n_breaks_T = gmt_char_count (title, GMT_ASCII_GS);		/* Is there a title spilling over several lines */
6238 	n_breaks_S = gmt_char_count (subtitle, GMT_ASCII_GS);	/* Is there a subtitle spilling over several lines */
6239 
6240 	if (!(n_breaks_T || n_breaks_S || subtitle[0])){	/* Just a single title string on one line */
6241 		if (gmt_text_is_latex (GMT, title)) {
6242 			/* Detected LaTeX commands, i.e., "....@[LaTeX...@[ ..." or  "....<math>LaTeX...</math> ..." */
6243 			(void)gmtplot_place_latex_eps (GMT, x, y, &GMT->current.setting.font_title, title);
6244 		}
6245 		else {
6246 			form = gmt_setfont (GMT, &GMT->current.setting.font_title);
6247 			PSL_plottext (PSL, x, y, sign * GMT->current.setting.font_title.size, GMT->current.map.frame.header, 0.0, -PSL_BC, form);
6248 			GMT->current.map.frame.plotted_header = true;
6249 		}
6250 		return;	/* Done */
6251 	}
6252 
6253 	sep[0] = GMT_ASCII_GS;
6254 	/* Must put everything inside a gsave/grestore block */
6255 	PSL_command (PSL, "V\n");	/* Keep the relative changes inside a save/restore block */
6256 	if (pos_set) PSL_command (PSL, "currentpoint /PSL_text_y edef /PSL_text_x edef\n");	/* Remember the position set for the text */
6257 
6258 	/* If there are subtitles then these must be placed first since we start from base and move up/backwards */
6259 
6260 	if (subtitle[0]) {	/* Plot a subtitle over one or more several lines */
6261 		if (gmt_text_is_latex (GMT, subtitle)) {
6262 			/* Detected LaTeX commands, i.e., "....@[LaTeX...@[ ..." or  "....<math>LaTeX...</math> ..." */
6263 			double h = gmtplot_place_latex_eps (GMT, x, y, &GMT->current.setting.font_subtitle, subtitle);
6264 			if (!head_latex) h += GMT->current.setting.map_title_offset;
6265 			if (pos_set) {	/* Must move up based on height above initial current point for every line since title will come later */
6266 				y_next += h;
6267 				PSL_command (PSL, "PSL_text_x PSL_text_y M 0 %d G\n", PSL->internal.y0 + (int)lrint (y_next * PSL->internal.y2iy));
6268 			}
6269 			else
6270 				y += h;
6271 		}
6272 		else {
6273 			form = gmt_setfont (GMT, &GMT->current.setting.font_subtitle);
6274 			line_spacing = 1.1 * GMT->current.setting.font_subtitle.size / PSL_POINTS_PER_INCH;
6275 			for (k = 0; k <= n_breaks_S; k++) {
6276 				word = gmt_get_word (subtitle, sep, n_breaks_S - k);	/* Pick from the end going forward */
6277 				PSL_plottext (PSL, x, y, sign * GMT->current.setting.font_subtitle.size, word, 0.0, -PSL_BC, form);
6278 				gmt_M_str_free (word);
6279 				if (pos_set) {	/* Must move up based on height above initial current point for every line since title will come later */
6280 					y_next += line_spacing;
6281 					PSL_command (PSL, "PSL_text_x PSL_text_y M 0 %d G\n", PSL->internal.y0 + (int)lrint (y_next * PSL->internal.y2iy));
6282 				}
6283 				else	/* Just increment the change in y location */
6284 					y += line_spacing;
6285 			}
6286 		}
6287 	}
6288 
6289 	if (gmt_text_is_latex (GMT, GMT->current.map.frame.header)) {
6290 		/* Detected LaTeX commands, i.e., "....@[LaTeX...@[ ..." or  "....<math>LaTeX...</math> ..." */
6291 		(void)gmtplot_place_latex_eps (GMT, x, y, &GMT->current.setting.font_title, GMT->current.map.frame.header);
6292 	}
6293 	else {
6294 		/* Now plot the title string(s), i.e., more than one line or LaTeX-free string */
6295 		form = gmt_setfont (GMT, &GMT->current.setting.font_title);
6296 		line_spacing = 1.1 * GMT->current.setting.font_title.size / PSL_POINTS_PER_INCH;
6297 		for (k = 0; k <= n_breaks_T; k++) {
6298 			word = gmt_get_word (title, sep, n_breaks_T - k);	/* Pick from the end going forward */
6299 			PSL_plottext (PSL, x, y, sign * GMT->current.setting.font_title.size, word, 0.0, -PSL_BC, form);
6300 			gmt_M_str_free (word);
6301 			if (k < n_breaks_T) {	/* If there are more lines above this one */
6302 				if (pos_set) {
6303 					y_next += line_spacing;
6304 					PSL_command (PSL, "PSL_text_x PSL_text_y M 0 %d G\n", PSL->internal.y0 + (int)lrint (y_next * PSL->internal.y2iy));
6305 				}
6306 				else	/* Just increment the change in y location */
6307 					y += line_spacing;
6308 			}
6309 		}
6310 	}
6311 	/* Since we are calling gmt_setfont inside a gsave/grestore block we must reset the font and stroke color to "not set" so it will be
6312 	 * correctly executed by the next gmt_setfont or gmt_setcolor calls elsewhere */
6313 	gmt_M_memcpy (PSL->current.rgb[PSL_IS_FONT], GMT->session.no_rgb, 3, double);	/* Reset to -1,-1,-1 since text setting must set the color desired */
6314 	gmt_M_memcpy (PSL->current.rgb[PSL_IS_STROKE], GMT->session.no_rgb, 3, double);	/* Reset to -1,-1,-1 since text setting must set the color desired */
6315 	PSL_command (PSL, "U\n");
6316 
6317 	GMT->current.map.frame.plotted_header = true;
6318 }
6319 
gmt_map_basemap(struct GMT_CTRL * GMT)6320 void gmt_map_basemap (struct GMT_CTRL *GMT) {
6321 	/* This function is usually called twice by modules: Once before data-plotting starts and
6322 	 * once after all data-plotting has ended.  This is because different modules have different
6323 	 * needs related to visibility and order of items. Each call will consult a bitflag that determines
6324 	 * if a particular aspect should be plotted this time.  The three items are:
6325 	 *
6326 	 * 1. The base frame (fancy or plain). Flag 0 means plot before data, 1 means after data
6327 	 * 2. The grid lines or grid ticks. Flag 0 means plot before data, 2 means after data
6328 	 * 3. Frame annotations and tick marks. Flag 0 means plot before data, 4 means after data
6329 	 *
6330 	 * In addition, your -B selections may not actually include all of those choices, of course.
6331 	 */
6332 
6333 	unsigned int side;
6334 	bool clip_on = false, too_crowded = false;
6335 	char *order[2] = {"before", "after"};
6336 	struct PSL_CTRL *PSL= GMT->PSL;
6337 
6338 	/* 0. Determine if we need to be here and set a few parameters */
6339 
6340 	/* If a user only gave -Bx<stuff> OR -By<stuff> then we override the -BWESN settings to turn off the unspecified axes */
6341 	if (!GMT->current.map.frame.set_frame[GMT_PRIMARY]) {
6342 		if (GMT->current.map.frame.set[GMT_Y] && !GMT->current.map.frame.set[GMT_X]) GMT->current.map.frame.side[S_SIDE] = GMT->current.map.frame.side[N_SIDE] = GMT_AXIS_NONE;
6343 		if (GMT->current.map.frame.set[GMT_X] && !GMT->current.map.frame.set[GMT_Y]) GMT->current.map.frame.side[W_SIDE] = GMT->current.map.frame.side[E_SIDE] = GMT_AXIS_NONE;
6344 	}
6345 
6346 	if (!GMT->common.B.active[GMT_PRIMARY] && !GMT->common.B.active[GMT_SECONDARY]) return;	/* No frame annotation/ticks/gridlines specified */
6347 
6348 	if (GMT->common.B.active[GMT_PRIMARY] && GMT->common.B.active[GMT_SECONDARY]) {
6349 		/* Make sure primary intervals are < than secondary intervals, otherwise we swap them */
6350 		gmtplot_check_primary_secondary (GMT);
6351 	}
6352 
6353 	if (GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_LON_HORIZONTAL) GMT->current.map.frame.horizontal = 2;
6354 	if (GMT->current.map.frame.horizontal == 2) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_LON_HORIZONTAL;
6355 	if (GMT->current.setting.map_frame_type & GMT_IS_GRAPH && gmt_M_is_geographic (GMT, GMT_IN)) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
6356 	if (GMT->current.setting.map_frame_type & GMT_IS_FANCY && !gmtplot_is_fancy_boundary(GMT)) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
6357 
6358 	PSL_comment (PSL, "Start of basemap (placed %s the plot contents)\n", order[GMT->current.map.frame.order]);
6359 
6360 	PSL_setdash (PSL, NULL, 0);	/* To ensure no dashed pens are set prior */
6361 
6362 	/* These three commands resets the memory of PSL regarding pen width, color, and outline */
6363 	gmt_M_memcpy (PSL->current.rgb[PSL_IS_STROKE], GMT->session.no_rgb, 3, double);	/* Reset to -1,-1,-1 so it can be reset below */
6364 	PSL->current.linewidth = GMT_NOTSET;	/* For a reset of internal setting in PSL */
6365 	PSL->current.outline = GMT_NOTSET;		/* Will now be changed by first PSL_setfill */
6366 
6367 	if (GMT->current.proj.three_D && GMT->current.map.frame.drawz) GMT->current.map.frame.plotted_header = true;	/* Just so it is not plotted by gmtplot_map_boundary first */
6368 
6369 	if (GMT->current.proj.got_azimuths) gmt_M_uint_swap (GMT->current.map.frame.side[E_SIDE], GMT->current.map.frame.side[W_SIDE]);	/* Temporary swap to trick justify machinery */
6370 
6371 	if (GMT->current.setting.map_frame_type & GMT_IS_INSIDE && !GMT->current.map.frame.header[0]) {
6372 		gmt_map_clip_on (GMT, GMT->session.no_rgb, 3);	/* Must clip to ensure things are inside */
6373 		clip_on = true;
6374 	}
6375 
6376 	/* 1. Lowest plot level is to place grid lines, ticks, crosses, if they are requested and expected at the current order.
6377 	 *    For some modules, gridlines are placed on top of the data (polygons, land-masses). */
6378 
6379 	gmtplot_map_griditems (GMT);
6380 
6381 	/* 2. Next is tick marks */
6382 
6383 	gmtplot_map_tick_marks (GMT);
6384 
6385 	/* 3. Next is map frame */
6386 
6387 	gmtplot_map_boundary (GMT);	/* This sets frame.side[] = true|false so MUST come before gmtplot_map_annotations */
6388 
6389 	/* 4. End with annotations, if requested in the current order */
6390 
6391 	gmtplot_map_annotations (GMT);
6392 
6393 	if (clip_on) gmt_map_clip_off (GMT);
6394 
6395 	PSL_setdash (PSL, NULL, 0);
6396 
6397 	/* 4. Undo various temporary changes */
6398 
6399 	if (GMT->current.proj.got_azimuths) gmt_M_uint_swap (GMT->current.map.frame.side[E_SIDE], GMT->current.map.frame.side[W_SIDE]);	/* Undo temporary swap */
6400 
6401 	if (GMT->current.proj.three_D && GMT->current.map.frame.drawz) GMT->current.map.frame.plotted_header = false;	/* Now we can plot the title [if selected via -B+t] */
6402 
6403 	if ((GMT->current.map.frame.order == GMT_BASEMAP_BEFORE && (GMT->current.plot.mode_3D & 1)) || (GMT->current.map.frame.order == GMT_BASEMAP_AFTER && (GMT->current.plot.mode_3D & 2)))
6404 		gmt_vertical_axis (GMT, GMT->current.plot.mode_3D);
6405 
6406 	PSL_comment (PSL, "End of basemap (placed %s the plot contents)\n", order[GMT->current.map.frame.order]);
6407 
6408 	for (side = 0; side < 4; side++) {	/* Reset annotation crowdedness arrays */
6409 		if (GMT_n_annotations[side]) {
6410 			gmt_M_free (GMT, GMT_x_annotation[side]);
6411 			gmt_M_free (GMT, GMT_y_annotation[side]);
6412 			GMT_n_annotations[side] = GMT_alloc_annotations[side] = 0;
6413 		}
6414 		if (GMT_n_annotations_skip[side]) {
6415 			static char *name[4] = {"bottom", "right", "top", "left"};
6416 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "%d annotations along the %s border were skipped due to crowding.\n", GMT_n_annotations_skip[side], name[side]);
6417 			too_crowded = true;
6418 			GMT_n_annotations_skip[side] = 0;
6419 		}
6420 	}
6421 	if (too_crowded) {
6422 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Crowding decisions is controlled by MAP_ANNOT_MIN_SPACING, currently set to %s.\n", GMT->current.setting.map_annot_min_spacing_txt);
6423 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Decrease or increase MAP_ANNOT_MIN_SPACING to see more or fewer annotations, with 0 showing all annotations.\n");
6424 	}
6425 	PSL_setcolor (PSL, GMT->current.setting.map_default_pen.rgb, PSL_IS_STROKE);
6426 
6427 	if (GMT->current.map.frame.order == GMT_BASEMAP_AFTER) {	/* Undo at end in case of multi processes */
6428 		GMT->current.map.frame.gridline_plotted = false;
6429 		GMT->current.map.frame.basemap_flag = GMT_BASEMAP_BEFORE;
6430 	}
6431 	else
6432 		GMT->current.map.frame.order = GMT_BASEMAP_AFTER;	/* Move to next order */
6433 }
6434 
gmt_set_basemap_orders(struct GMT_CTRL * GMT,unsigned int frame,unsigned int grid,unsigned int annot)6435 void gmt_set_basemap_orders (struct GMT_CTRL *GMT, unsigned int frame, unsigned int grid, unsigned int annot) {
6436 	/* Helper function to fill out the basemap flags for the calling module based on its peculiarities and initial settings */
6437 	/* First apply some general over-ruling depending on 3-D and inside annotations */
6438 	static char *place[2] = {"below", "above"};
6439 	if (GMT->current.proj.three_D && GMT->common.J.zactive) {
6440 		frame = GMT_BASEMAP_FRAME_BEFORE;	/* In true 3-D plots we must lay down the x-y frame first regardless of desire to place it at the end */
6441 		annot = GMT_BASEMAP_ANNOT_BEFORE;
6442 		grid  = GMT_BASEMAP_GRID_BEFORE;
6443 	}
6444 	else if (GMT->current.setting.map_frame_type == GMT_IS_INSIDE)	/* Must do annotations and ticks at end since inside the map */
6445 		annot = GMT_BASEMAP_ANNOT_AFTER;
6446 
6447 		/* Finally, since ticks overprint on top of the frame we make sure annot/ticks are never done after the frame */
6448 	if (annot == GMT_BASEMAP_ANNOT_AFTER && frame == GMT_BASEMAP_FRAME_BEFORE)
6449 		annot = GMT_BASEMAP_ANNOT_BEFORE;
6450 	GMT->current.map.frame.basemap_flag = frame + grid + annot;
6451 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Basemap order: Frame = %s  Grid = %s  Tick/Annot = %s\n", place[frame], place[grid/2], place[annot/4]);
6452 }
6453 
gmtplot_z_axis_side(struct GMT_CTRL * GMT,unsigned int axis,unsigned int quadrant)6454 GMT_LOCAL bool gmtplot_z_axis_side (struct GMT_CTRL *GMT, unsigned int axis, unsigned int quadrant) {
6455 	bool below;
6456 	gmt_M_unused(GMT);
6457 	axis++;	/* 1-4 */
6458 	switch (axis) {
6459 		case 1: below = (quadrant == 2);	break;
6460 		case 2: below = (quadrant != 3);	break;
6461 		case 3: below = (quadrant != 2);	break;
6462 		case 4: below = (quadrant == 3);	break;
6463 		default: below = true;	/* Just to avoid Coverity issues */
6464 	}
6465 	return below;
6466 }
6467 
gmt_vertical_axis(struct GMT_CTRL * GMT,unsigned int mode)6468 void gmt_vertical_axis (struct GMT_CTRL *GMT, unsigned int mode) {
6469 	/* Mode means: 1 = background walls and title, 2 = foreground walls and axis, 3 = all */
6470 	unsigned int fore, back, old_plane;
6471 	double nesw[4], old_level, xx, yy, az;
6472 	struct PSL_CTRL *PSL= GMT->PSL;
6473 
6474 	if (!GMT->current.proj.three_D) return;
6475 	if (!GMT->current.map.frame.drawz) return;
6476 
6477 	if (GMT->current.map.frame.axis[GMT_Z].item[GMT_GRID_UPPER].active || GMT->current.map.frame.axis[GMT_Z].item[GMT_GRID_LOWER].active) {
6478 		GMT->current.map.frame.draw_box |= GMT_3D_WALL;
6479 		GMT->current.map.frame.draw_wall = true;
6480 	}
6481 
6482 	nesw[0] = GMT->current.proj.rect[YHI], nesw[1] = GMT->current.proj.rect[XHI], nesw[2] = GMT->current.proj.rect[YLO], nesw[3] = GMT->current.proj.rect[XLO];
6483 
6484 	fore = mode & 2, back = mode & 1;
6485 
6486 	/* Since this routine messes with the perspective, we save the state first, then restore later */
6487 	old_plane = GMT->current.proj.z_project.plane;
6488 	old_level = GMT->current.proj.z_project.level;
6489 
6490 	/* Vertical walls */
6491 
6492 	if (GMT->current.map.frame.draw_box) {
6493 		PSL_setfill (PSL, GMT->session.no_rgb, 1);
6494 		gmt_setpen (GMT, &GMT->current.setting.map_grid_pen[GMT_PRIMARY]);
6495 		if (back) {	/* Draw back walls and cube boxes */
6496 			gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 1, nesw, true, GMT->current.map.frame.draw_box);
6497 			gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 2, nesw, true, GMT->current.map.frame.draw_box);
6498 			if (GMT->current.map.frame.draw_box & GMT_3D_BOX) {
6499 				gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant + 1, nesw);
6500 				gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant + 2, nesw);
6501 			}
6502 		}
6503 		if (fore) {	/* Draw front walls and cube boxes */
6504 			gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant + 3, nesw, false, GMT->current.map.frame.draw_box);
6505 			gmtplot_vertical_wall (GMT, PSL, GMT->current.proj.z_project.quadrant    , nesw, false, GMT->current.map.frame.draw_box);
6506 			if (GMT->current.map.frame.draw_box & GMT_3D_BOX) {
6507 				gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant + 3, nesw);
6508 				gmtplot_cube_box (GMT, PSL, GMT->current.proj.z_project.quadrant    , nesw);
6509 			}
6510 		}
6511 	}
6512 
6513 	/* Vertical axis */
6514 
6515 	if (fore && GMT->current.map.frame.side[Z_SIDE]) {
6516 		unsigned int k, n_z, quadrant, corner_to_quadrant[5] = {0, 2, 1, 4, 3}, z_axis[4];	/* Given corner ID 1-4, return quadrant, or vice versa (0 is unused) */
6517 		bool below;
6518 		gmt_M_memcpy (z_axis, GMT->current.map.frame.z_axis, 4, unsigned int);
6519 		for (k = n_z = 0; k < 4; k++)	/* Count # of vertical axes specified; if 0 then we do an auto-select */
6520 			if (z_axis[k]) n_z++;
6521 		if (n_z == 0) z_axis[corner_to_quadrant[GMT->current.proj.z_project.quadrant]-1] = 1;	/* Set the default corner given the quadrant */
6522 		gmt_plane_perspective (GMT, -1, 0.0);
6523 		for (k = 0; k < 4; k++) {
6524 			if (z_axis[k] == 0) continue;	/* Not drawing this vertical axis */
6525 			quadrant = corner_to_quadrant[k+1];	/* Given corner (k+1), return quadrant */
6526 			below = gmtplot_z_axis_side (GMT, k, GMT->current.proj.z_project.quadrant);
6527 			gmt_xyz_to_xy (GMT, nesw[(quadrant/2*2+1)%4], nesw[((quadrant+1)/2*2)%4], GMT->common.R.wesn[ZLO], &xx, &yy);
6528 			/* Restrict reduced azimuth to -45 to 45 range */
6529 			az = GMT->current.proj.z_project.view_azimuth - 90.0 - floor ((GMT->current.proj.z_project.view_azimuth - 45.0) / 90.0) * 90.0;
6530 			PSL_command (PSL, "/PSL_GPP matrix currentmatrix def [%.12g %.12g %.12g %.12g %.12g %.12g] concat\n",
6531 				cosd(az), sind(az) * GMT->current.proj.z_project.sin_el, 0.0, GMT->current.proj.z_project.cos_el, xx * PSL->internal.x2ix, yy * PSL->internal.y2iy);
6532 			gmt_xy_axis (GMT, 0.0, -GMT->common.R.wesn[ZLO], GMT->current.proj.zmax - GMT->current.proj.zmin, GMT->common.R.wesn[ZLO],
6533 				GMT->common.R.wesn[ZHI], &GMT->current.map.frame.axis[GMT_Z], below, GMT->current.map.frame.side[Z_SIDE]);
6534 			PSL_command (PSL, "PSL_GPP setmatrix\n");
6535 		}
6536 	}
6537 
6538 	/* Title */
6539 
6540 	if (back && GMT->current.map.frame.header[0] && !GMT->current.map.frame.plotted_header) {	/* No header today */
6541 		gmt_plane_perspective (GMT, -1, 0.0);
6542 		gmt_map_title (GMT, 0.5 * (GMT->current.proj.z_project.xmin + GMT->current.proj.z_project.xmax), GMT->current.proj.z_project.ymax + GMT->current.setting.map_title_offset);
6543 	}
6544 
6545 	gmt_plane_perspective (GMT, old_plane, old_level);
6546 }
6547 
gmt_map_clip_on(struct GMT_CTRL * GMT,double rgb[],unsigned int flag)6548 void gmt_map_clip_on (struct GMT_CTRL *GMT, double rgb[], unsigned int flag) {
6549 	/* This function sets up a clip path so that only plotting
6550 	 * inside the map area will be drawn on paper. map_setup
6551 	 * must have been called first.  If r >= 0, the map area will
6552 	 * first be painted in the r,g,b colors specified.  flag can
6553 	 * be 0-3, as described in PSL_beginclipping():
6554 	 * flag : 0 = continue adding pieces to the current clipping path
6555 	 *        1 = start new clipping path (more must follows)
6556 	 *        2 = end clipping path (this is the last segment added)
6557 	 *        3 = this is the complete clipping path (start to end)
6558 	 * 	  Add 4 to select even-odd clipping [nonzero-winding rule].
6559 	 */
6560 
6561 	uint64_t np;
6562 	bool donut;
6563 	double *work_x = NULL, *work_y = NULL;
6564 	struct PSL_CTRL *PSL= GMT->PSL;
6565 
6566 	np = gmt_map_clip_path (GMT, &work_x, &work_y, &donut);
6567 
6568 	PSL_comment (PSL, "Activate Map clip path\n");
6569 	if (donut) {
6570 		PSL_beginclipping (PSL, work_x, work_y, (int)np, rgb, 1);
6571 		PSL_beginclipping (PSL, &work_x[np], &work_y[np], (int)np, rgb, 2);
6572 	}
6573 	else
6574 		PSL_beginclipping (PSL, work_x, work_y, (int)np, rgb, flag);
6575 
6576 	gmt_M_free (GMT, work_x);
6577 	gmt_M_free (GMT, work_y);
6578 }
6579 
gmt_map_clip_off(struct GMT_CTRL * GMT)6580 void gmt_map_clip_off (struct GMT_CTRL *GMT) {
6581 	/* Restores the original clipping path for the plot */
6582 
6583 	PSL_comment (GMT->PSL, "Deactivate Map clip path\n");
6584 	PSL_endclipping (GMT->PSL, 1);		/* Reduce polygon clipping by one level */
6585 }
6586 
gmt_BB_clip_on(struct GMT_CTRL * GMT,double rgb[],unsigned int flag)6587 void gmt_BB_clip_on (struct GMT_CTRL *GMT, double rgb[], unsigned int flag) {
6588 	/* This function sets up a clip path so that only plotting
6589 	 * inside the bounding box rectangular area will be drawn on paper. map_setup
6590 	 * must have been called first.  If r >= 0, the map area will
6591 	 * first be painted in the r,g,b colors specified.  flag can
6592 	 * be 0-3, as described in PSL_beginclipping():
6593 	 * flag : 0 = continue adding pieces to the current clipping path
6594 	 *        1 = start new clipping path (more must follows)
6595 	 *        2 = end clipping path (this is the last segment added)
6596 	 *        3 = this is the complete clipping path (start to end)
6597 	 * 	  Add 4 to select even-odd clipping [nonzero-winding rule].
6598 	 */
6599 
6600 	double work_x[5], work_y[5];
6601 	struct PSL_CTRL *PSL= GMT->PSL;
6602 
6603 	work_x[0] = work_x[3] = work_x[4] = GMT->current.proj.rect[XLO];
6604 	work_x[1] = work_x[2] = GMT->current.proj.rect[XHI];
6605 	work_y[0] = work_y[1] = work_y[4] = GMT->current.proj.rect[YLO];
6606 	work_y[2] = work_y[3] = GMT->current.proj.rect[YHI];
6607 
6608 	PSL_comment (PSL, "Activate BoundingBox Map clip path\n");
6609 	PSL_beginclipping (PSL, work_x, work_y, 5, rgb, flag);
6610 }
6611 
gmt_setfill(struct GMT_CTRL * GMT,struct GMT_FILL * fill,int outline)6612 void gmt_setfill (struct GMT_CTRL *GMT, struct GMT_FILL *fill, int outline) {
6613 	struct PSL_CTRL *PSL= GMT->PSL;
6614 	if (!fill) /* NO fill pointer = no fill */
6615 		PSL_setfill (PSL, GMT->session.no_rgb, outline);
6616 	else if (fill->use_pattern) {
6617 		/* Fill with a pattern */
6618 		double rgb[4] = {-3.0, -3.0, -3.0, 0.0};
6619 		rgb[1] = (double)PSL_setimage (PSL, fill->pattern_no, fill->pattern, fill->image, fill->dpi, fill->dim, fill->f_rgb, fill->b_rgb);
6620 		if (rgb[1] < 0.0) {	/* Error in PSL_setimage */
6621 			gmt_M_memset (rgb, 4, double);
6622 			PSL_comment (GMT->PSL, "PSL_setimage failed: Setting fill to black\n");
6623 		}
6624 		PSL_setfill (PSL, rgb, outline);
6625 	}
6626 	else	/* Fill with a color */
6627 		PSL_setfill (PSL, fill->rgb, outline);
6628 }
6629 
gmt_setfont(struct GMT_CTRL * GMT,struct GMT_FONT * F)6630 unsigned int gmt_setfont (struct GMT_CTRL *GMT, struct GMT_FONT *F) {
6631 	/* Set all attributes of the selected font in the PS */
6632 	unsigned int outline;
6633 
6634 	PSL_setfont (GMT->PSL, F->id);	/* Set the current font ID */
6635 	if (F->form & 2) {	/* Outline font requested; set pen and fill (rgb[0] == -1 means no fill) */
6636 		gmt_setpen (GMT, &F->pen);		/* Stroke the text outline with this pen */
6637 		gmt_setfill (GMT, &F->fill, 1);	/* Use this color or pattern (if any) for the text fill */
6638 		outline = 1;	/* Indicates outline font is needed and should be stroked */
6639 		if (F->form & 8) outline = 3;	/* Indicates that the outline is allowed to overlap the font */
6640 	}
6641 	else if (F->form & 4) {	/* Want to use a pattern to fill the text but do not draw outline */
6642 		gmt_setfill (GMT, &F->fill, 0);
6643 		outline = 2;	/* Indicates outline font is needed for filling but will not be stroked */
6644 	}
6645 	else {	/* Regular, solid text fill is set via stroke color */
6646 		PSL_setcolor (GMT->PSL, F->fill.rgb, PSL_IS_FONT);
6647 		outline = 0;	/* Indicates we will fill text using "show" which takes current color (i.e., stroke color) */
6648 	}
6649 	return (outline);
6650 }
6651 
gmt_draw_map_inset(struct GMT_CTRL * GMT,struct GMT_MAP_INSET * B,bool clip)6652 void gmt_draw_map_inset (struct GMT_CTRL *GMT, struct GMT_MAP_INSET *B, bool clip) {
6653 	/* Place a rectangle on the map, as defined by center point and dimensions or w/e/s/n in geo or projected coordinates */
6654 	unsigned int k;
6655 	double rect[4], dim[3], s;
6656 	struct GMT_MAP_PANEL *panel = B->panel;
6657 
6658 	if (B->refpoint) gmt_set_refpoint (GMT, B->refpoint);	/* Finalize reference point plot coordinates, if needed */
6659 
6660 	/* First convert the information we have into the center and dimensions of a rectangle */
6661 
6662 	if (B->refpoint || B->unit || B->oblique) {	/* Dealing with projected coordinates and dimensions or got oblique box */
6663 		if (B->unit) {
6664 			if (gmt_init_distaz (GMT, B->unit, GMT_GREATCIRCLE, GMT_MAP_DIST) == GMT_NOT_A_VALID_TYPE)	/* Get scales for this unit */
6665 				return;
6666 		}
6667 		if (B->refpoint) {	/* Got a geographic center point and width/height for a rectangular box */
6668 			gmt_M_memcpy (dim, B->dim, 2, double);		/* Duplicate the width/height of rectangle */
6669 			if (B->unit) {	/* Gave dimensioned box */
6670 				for (k = 0; k < 2; k++) {
6671 					dim[k] /= GMT->current.map.dist[GMT_MAP_DIST].scale;	/* Convert units to meters */
6672 					dim[k] *= GMT->current.proj.scale[k];		/* Turns meters into inches on map */
6673 				}
6674 			}
6675 			/* Now in inches */
6676 			gmt_adjust_refpoint (GMT, B->refpoint, dim, B->off, B->justify, PSL_BL);	/* Adjust to bottom left corner */
6677 			rect[XLO] = B->refpoint->x;	rect[XHI] = B->refpoint->x + dim[GMT_X];	/* Get the min/max map coordinates of the rectangle */
6678 			rect[YLO] = B->refpoint->y;	rect[YHI] = B->refpoint->y + dim[GMT_Y];
6679 		}
6680 		else if (B->oblique) {	/* Got lower left and upper right coordinates of rectangular box */
6681 			gmt_geo_to_xy (GMT, B->wesn[XLO], B->wesn[YLO], &rect[XLO], &rect[YLO]);	/* Lower left corner in inches */
6682 			gmt_geo_to_xy (GMT, B->wesn[XHI], B->wesn[YHI], &rect[XHI], &rect[YHI]);	/* Lower left corner in inches */
6683 		}
6684 		else {	/* Got 4 sides in projected units or a rectangular box*/
6685 			gmt_M_memcpy (rect, B->wesn, 4, double);
6686 			for (k = 0; k < 4; k++) rect[k] /= GMT->current.map.dist[GMT_MAP_DIST].scale;	/* Turns units to meters */
6687 			/* Turns meters into inches on map */
6688 			rect[XLO] = rect[XLO] * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X];
6689 			rect[XHI] = rect[XHI] * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X];
6690 			rect[YLO] = rect[YLO] * GMT->current.proj.scale[GMT_Y] + GMT->current.proj.origin[GMT_Y];
6691 			rect[YHI] = rect[YHI] * GMT->current.proj.scale[GMT_Y] + GMT->current.proj.origin[GMT_Y];
6692 		}
6693 	}
6694 	else {	/* Got 4 geographic coordinates */
6695 		if (gmt_M_is_rect_graticule (GMT)) {	/* Will give rectangle */
6696 			gmt_geo_to_xy (GMT, B->wesn[XLO], B->wesn[YLO], &rect[XLO], &rect[YLO]);	/* Lower left corner in inches */
6697 			gmt_geo_to_xy (GMT, B->wesn[XHI], B->wesn[YHI], &rect[XHI], &rect[YHI]);	/* Lower left corner in inches */
6698 		}
6699 		else {	/* Curved map inset */
6700 			uint64_t np;
6701 			int outline = ((panel->mode & GMT_PANEL_OUTLINE) == GMT_PANEL_OUTLINE) ? 1 : 0;	/* Does the panel have an outline? */
6702 			double *lon = NULL, *lat = NULL;
6703 			struct GMT_DATASEGMENT *S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, 0, 2, NULL, NULL);	/* Just get empty array pointers */
6704 			np = gmt_graticule_path (GMT, &lon, &lat, 1, false, B->wesn[XLO], B->wesn[XHI], B->wesn[YLO], B->wesn[YHI]);
6705 			S->data[GMT_X] = lon;	S->data[GMT_Y] = lat;
6706 			S->n_rows = np;
6707 			if ((panel->mode & 1)) gmt_setfill (GMT, &panel->fill, outline);
6708 			if ((panel->mode & 2)) gmt_setpen (GMT, &panel->pen1);
6709 			gmt_geo_polygons (GMT, S);
6710 			gmt_free_segment (GMT, &S);
6711 			return;	/* Done here */
6712 		}
6713 	}
6714 
6715 	/* Deal with rectangular inset */
6716 	/* Determine panel dimensions */
6717 
6718 	dim[GMT_X] = rect[XHI] - rect[XLO];	dim[GMT_Y] = rect[YHI] - rect[YLO];
6719 	if (B->refpoint == NULL) {	/* Need to set the BL refpoint since needed in inset to set the temporary origin */
6720 		char fake[GMT_LEN64] = {""};
6721 		snprintf (fake, GMT_LEN64, "x%.16lgi/%.16lgi+jBL", rect[XLO], rect[YLO]);
6722 		if ((B->refpoint = gmt_get_refpoint (GMT, fake, 'D')) == NULL)
6723 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to parse arg %s\n", fake);
6724 	}
6725 
6726 	/* Report position and dimensions */
6727 	s = GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
6728 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Map inset lower left corner and dimensions (in %s): %g %g %g %g\n",
6729 		GMT->session.unit_name[GMT->current.setting.proj_length_unit], rect[XLO]*s, rect[YLO]*s, dim[GMT_X]*s, dim[GMT_Y]*s);
6730 	if (B->file) {	/* Save x0 y0 w h to file */
6731 		FILE *fp = fopen (B->file, "w");
6732 		if (fp) {
6733 			fprintf (fp, "%.12g %.12g %.12g %.12g\n", rect[XLO]*s, rect[YLO]*s, dim[GMT_X]*s, dim[GMT_Y]*s);
6734 			fclose (fp);
6735 		}
6736 		else
6737 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to create file %s\n", B->file);
6738 		gmt_M_str_free (B->file);
6739 	}
6740 	if (panel) {	/* Requested to draw a panel */
6741 		panel->width = dim[GMT_X];	panel->height = dim[GMT_Y];
6742 		if (!panel->clearance) gmt_M_memset (panel->padding, 4, double);	/* No clearance is default for map insets unless actually specified */
6743 		gmt_draw_map_panel (GMT, 0.5 * (rect[XHI] + rect[XLO]), 0.5 * (rect[YHI] + rect[YLO]), 3U, panel);
6744 	}
6745 	if (clip) {	/* Set up clip path for this inset */
6746 		double xc[4], yc[4];
6747 		/* Adjust for the padding so that clipping matches the panel rectangle which may be larger than inset */
6748 		xc[0] = xc[3] = rect[XLO];	xc[1] = xc[2] = rect[XHI];
6749 		yc[0] = yc[1] = rect[YLO];	yc[2] = yc[3] = rect[YHI];
6750 		if (panel) {	/* Adjust for clearance, if any */
6751 			xc[0] -= panel->padding[XLO]; xc[3] -= panel->padding[XLO];	xc[1] += panel->padding[XHI]; xc[2] += panel->padding[XHI];
6752 			yc[0] -= panel->padding[YLO]; yc[1] -= panel->padding[YLO];	yc[2] += panel->padding[YHI]; yc[3] += panel->padding[YHI];
6753 		}
6754 		PSL_comment (GMT->PSL, "Start of inset clip path\n");
6755 		PSL_command (GMT->PSL, "clipsave\n");
6756 		PSL_plotline (GMT->PSL, xc, yc, 4, PSL_MOVE | PSL_CLOSE);	/* Must not close path since first point not given ! */
6757 		PSL_command (GMT->PSL, "clip N\n");
6758 		PSL_command (GMT->PSL, "/PSL_inset_clip 1 def\n");	/* Remember that inset clipping is on */
6759 	}
6760 	else	/* No inset clipping set */
6761 		PSL_command (GMT->PSL, "/PSL_inset_clip 0 def\n");
6762 
6763 	if (B->translate)	/* Translate the plot origin */
6764 		PSL_setorigin (GMT->PSL, rect[XLO], rect[YLO], 0.0, PSL_FWD);
6765 }
6766 
gmt_draw_map_scale(struct GMT_CTRL * GMT,struct GMT_MAP_SCALE * ms)6767 int gmt_draw_map_scale (struct GMT_CTRL *GMT, struct GMT_MAP_SCALE *ms) {
6768 	/* unit_width is array of approximate widths of one space plus the various unit abbreviations in multiples of GMT_LET_WIDTH for Helvetica */
6769 	/* name_width is array of approximate widths of the various labels in multiples of GMT_LET_WIDTH for Helvetica */
6770 	unsigned int form, j;
6771 	double unit_width[GMT_N_UNITS] = {1.1, 1.5, 1.5, 1.5, 2.0, 2.0, 2.0, 1.1, 2.25};
6772 	double name_width[GMT_N_UNITS] = {1.0, 2.3, 4.02, 10.7, 3.2, 2.0, 2.0, 3.05, 8.6};
6773 	enum gmt_enum_units unit = GMT_IS_METER;
6774 	double x1, x2, y1, y2, tx, ty, dist_to_annot, scale_height, x_left, x_right, bar_length_km, dim[4];
6775 	double XL, YL, XR, YR, dist, scl, bar_width, dx, x0_scl, y0_scl;
6776 	char txt[GMT_LEN256] = {""}, format[GMT_LEN64] = {""}, *this_label = NULL;
6777 	/* inch, cm, pt is not used here but in the array since gmtlib_get_unit_number uses this sequence */
6778 	char *label[GMT_N_UNITS] = {"m", "km", "miles", "nautical miles", "inch", "cm", "pt", "feet", "survey feet"};
6779 	char *units[GMT_N_UNITS] = {"m", "km", "mi", "nm", "in", "cm", "pt", "ft", "usft"}, measure;
6780 	struct PSL_CTRL *PSL= GMT->PSL;
6781 	struct GMT_MAP_PANEL *panel = ms->panel;
6782 
6783 	if (!ms->plot) return GMT_OK;
6784 
6785 	gmt_set_refpoint (GMT, ms->refpoint);	/* Finalize reference point plot coordinates, if needed */
6786 
6787 	if (gmt_M_is_cartesian (GMT, GMT_IN))
6788 		bar_length_km = ms->length;	/* Just as is */
6789 	else {
6790 		measure = (ms->measure == 0) ? 'k' : ms->measure;	/* Km is default distance unit */
6791 		if ((unit = gmtlib_get_unit_number (GMT, measure)) == GMT_IS_NOUNIT) {
6792 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad distance unit %c\n", measure);
6793 			return GMT_PARSE_ERROR;
6794 		}
6795 		bar_length_km = 0.001 * GMT->current.proj.m_per_unit[unit] * ms->length;	/* Now in km */
6796 		if (ms->origin_mode == GMT_SCALE_ORIGIN_PLACE) {	/* Pick the lon/lat of the scale placement as map scale origin */
6797 			gmt_xy_to_geo (GMT, &ms->origin[GMT_X], &ms->origin[GMT_Y], ms->refpoint->x, ms->refpoint->y);
6798 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Map scale origin selected to be %g/%g\n", ms->origin[GMT_X], ms->origin[GMT_Y]);
6799 		}
6800 		else if (ms->origin_mode == GMT_SCALE_ORIGIN_MIDDLE) {	/* Pick middle of map as map scale origin */
6801 			gmt_xy_to_geo (GMT, &ms->origin[GMT_X], &ms->origin[GMT_Y], 0.5 * GMT->current.map.width, 0.5 * GMT->current.map.height);
6802 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Map scale origin selected to be %g/%g\n", ms->origin[GMT_X], ms->origin[GMT_Y]);
6803 		}
6804 	}
6805 
6806 	/* We will determine the scale at the point ms->origin[GMT_X], ms->origin[GMT_Y] */
6807 	if (gmt_M_is_dnan (ms->origin[GMT_X])) ms->origin[GMT_X] = GMT->current.proj.central_meridian;
6808 	gmt_geo_to_xy (GMT, ms->origin[GMT_X], ms->origin[GMT_Y], &x0_scl, &y0_scl);
6809 	/* 1. Pick a reasonably small dx, e.g., 5% of map width */
6810 	dx = 0.05 * GMT->current.map.width;
6811 	/* 2. Compute test x1, x2 to either side of ms->origin[GMT_X], ms->origin[GMT_Y] */
6812 	x1 = x0_scl - dx;	x2 = x0_scl + dx;
6813 	/* 3. Convert (x1,y0), (x2,y0) to lon,lat coordinates */
6814 	gmt_xy_to_geo (GMT, &XL, &YL, x1, y0_scl);
6815 	gmt_xy_to_geo (GMT, &XR, &YR, x2, y0_scl);
6816 	/* 4. Get distances between (XL,YL) and (XR,YR) */
6817 	if (gmt_M_is_cartesian (GMT, GMT_IN))
6818 		dist = hypot (XL - XR, YL - YR);
6819 	else	/* in km */
6820 		dist = 0.001 * gmt_great_circle_dist_meter (GMT, XL, YL, XR, YR);
6821 	/* Get local scale of desired length to this reference length */
6822 	scl = bar_length_km / dist;
6823 	/* Revise the selection of dx, x1, x2 using this scale.  Use center y as coordinates for a horizontal bar */
6824 	dx *= scl; x1 = ms->refpoint->x - dx;	x2 = ms->refpoint->x + dx;
6825 	y1 = y2 = ms->refpoint->y;
6826 
6827 	dim[GMT_X] = bar_width = hypot (x2 - x1, y2 - y1);		/* Width of scale bar in inches */
6828 	dim[GMT_Y] = scale_height = fabs (GMT->current.setting.map_scale_height);	/* Nominal scale bar height */
6829 	dist_to_annot = scale_height + 0.75 * GMT->current.setting.map_annot_offset[GMT_PRIMARY];	/* Dist from top of scalebar to top of annotations */
6830 
6831 	gmt_adjust_refpoint (GMT, ms->refpoint, dim, ms->off, ms->justify, PSL_TC);	/* Adjust refpoint to top center */
6832 
6833 	x_left  = ms->refpoint->x - 0.5 * bar_width;	/* x-coordinate of leftmost  scalebar point */
6834 	x_right = ms->refpoint->x + 0.5 * bar_width;	/* x-coordinate of rightmost scalebar point */
6835 
6836 	if (ms->fancy && gmt_M_is_geographic (GMT, GMT_IN)) {	/* Fancy scale */
6837 		unsigned int i, justify, form;
6838 		unsigned int n_f_ticks[10] = {5, 4, 6, 4, 5, 6, 7, 4, 3, 5};
6839 		unsigned int n_a_ticks[10] = {1, 2, 3, 2, 1, 3, 1, 2, 1, 1};
6840 		int js;
6841 		double base, d_base, dx_f, dx_a, bar_height, bar_tick_len;
6842 		PSL_comment (PSL, "Draw fancy map scale\n");
6843 		/* Based on magnitue of length, determine reasonable annotation spacings */
6844 		js = irint (floor (d_log10 (GMT, ms->length / 0.95)));
6845 		base = pow (10.0, (double)js);
6846 		i = urint (ms->length / base) - 1;
6847 		d_base = ms->length / n_a_ticks[i];
6848 		dx_f = bar_width / n_f_ticks[i];
6849 		dx_a = bar_width / n_a_ticks[i];
6850 		bar_height = 0.5 * fabs (GMT->current.setting.map_scale_height);	/* Height of the black/white checkered fancy bar */
6851 		bar_tick_len = 0.75 * fabs (GMT->current.setting.map_scale_height);	/* Length of tickmarks */
6852 		if (panel && panel->mode) {	/* Place rectangle behind the map scale */
6853 			double x_center, y_center, l_width = 0.0, l_height = 0.0, l_shift = 0.0;
6854 
6855 			/* Adjustment for size of largest annotation is ~half its length (= js+1) times approximate dimensions: */
6856 			dim[XLO] = dim[XHI] = 0.5 * (js+1) * GMT_DEC_WIDTH * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH;
6857 			if (ms->unit) {	/* Adjust for units at end of each annotation */
6858 				dim[XLO] += unit_width[unit] * GMT_LET_WIDTH * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH;
6859 				dim[XHI] += unit_width[unit] * GMT_LET_WIDTH * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH;
6860 			}
6861 			if (ms->do_label) {
6862 				/* We estimate the width of the label similarly; if no label given then we use the name_widths from above rather than strlen */
6863 				l_width = bar_tick_len + (ms->label[0] ? strlen (ms->label) : name_width[unit]) * GMT_LET_WIDTH * GMT->current.setting.font_label.size * GMT->session.u2u[GMT_PT][GMT_INCH];
6864 				/* When label is placed to left|right it sticks up by l_shift and we must ensure dim[YHI] is at least that large */
6865 				l_height = GMT_LET_HEIGHT * GMT->current.setting.font_label.size / PSL_POINTS_PER_INCH;	/* Approximate height of label */
6866 				l_shift  = l_height - scale_height;	/* Adjust for the shift in y-coordinate */
6867 			}
6868 			if (ms->alignment == 'l' && l_width > dim[XLO]) dim[XLO] = l_width;	/* Extend rectangle on the left to accommodate the label */
6869 			else if (ms->alignment == 'r' && l_width > dim[XHI]) dim[XHI] = l_width;	/* Extend rectangle on the right to accommodate the label */
6870 			/* Estimate approximate distance from anchor point down to base of annotations */
6871 			dim[YLO] = dist_to_annot + GMT_LET_HEIGHT * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH;
6872 			dim[YHI] = 0.0;	/* Normally nothing above the scale bar */
6873 			/* If label is above or below bar, add label offset and approximate label height to the space dim */
6874 			if (ms->do_label && ms->alignment == 'b') dim[YLO] += fabs (GMT->current.setting.map_label_offset[GMT_Y]) + l_height;
6875 			else if (ms->do_label && ms->alignment == 't') dim[YHI] += fabs (GMT->current.setting.map_label_offset[GMT_Y]) + l_height;
6876 			else if (ms->do_label && l_shift > dim[YHI]) dim[YHI] = l_shift;
6877 			/* Determine center of gravity for panel */
6878 			x_center = ms->refpoint->x + 0.5 * (dim[XHI] - dim[XLO]);
6879 			y_center = ms->refpoint->y + 0.5 * (dim[YHI] - dim[YLO]);
6880 			/* Determine panel dimensions */
6881 			panel->width = bar_width + dim[XHI] + dim[XLO];	panel->height = dim[YHI] + dim[YLO];
6882 			gmt_draw_map_panel (GMT, x_center, y_center, 3U, panel);
6883 		}
6884 		/* Draw bar scale ticks using tick pen as well as checkerboard using map_default_pen color [black] and page color [white] */
6885 		gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
6886 		PSL_plotsegment (PSL, x_left, ms->refpoint->y - bar_tick_len, x_left, ms->refpoint->y);
6887 		for (j = 0; j < n_f_ticks[i]; j++) {
6888 			PSL_setfill (PSL, (j%2) ? GMT->PSL->init.page_rgb : GMT->current.setting.map_default_pen.rgb, 1);
6889 			PSL_plotbox (PSL, x_left + j * dx_f, ms->refpoint->y, x_left + (j+1) * dx_f, ms->refpoint->y - bar_height);
6890 			PSL_plotsegment (PSL, x_left + (j+1) * dx_f, ms->refpoint->y - bar_tick_len, x_left + (j+1) * dx_f, ms->refpoint->y);
6891 		}
6892 		/* Place annotations */
6893 		ty = ms->refpoint->y - dist_to_annot;	/* The y-coordinate at the top of the annotations */
6894 		form = gmt_setfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]);
6895 		for (j = 0; j <= n_a_ticks[i]; j++) {
6896 			gmt_sprintf_float (GMT, format, GMT->current.setting.format_float_map, j * d_base);
6897 			if (ms->unit) /* Must append unit */
6898 				snprintf (txt, GMT_LEN256, "%s %s", format, units[unit]);
6899 			else
6900 				snprintf (txt, GMT_LEN256, "%s", format);
6901 			tx = x_left + j * dx_a;	/* center x-coordinate for this annotation */
6902 			PSL_plotsegment (PSL, tx, ms->refpoint->y - scale_height, tx, ms->refpoint->y);	/* Draw the tick mark */
6903 			PSL_plottext (PSL, tx, ty, GMT->current.setting.font_annot[GMT_PRIMARY].size, txt, 0.0, PSL_TC, form);	/* Place annotation */
6904 		}
6905 		if (ms->do_label) {	/* Label was requested */
6906 			/* Determine placement of the label */
6907 			switch (ms->alignment) {
6908 				case 'l':	/* Left */
6909 					tx = x_left - bar_tick_len;
6910 					ty = ms->refpoint->y - scale_height;
6911 					justify = PSL_BR;	/* Left side annotation are right-justified, etc. */
6912 					break;
6913 				case 'r':	/* Right */
6914 					tx = x_right + bar_tick_len;
6915 					ty = ms->refpoint->y - scale_height;
6916 					justify = PSL_BL;
6917 					break;
6918 				case 't':	/* Top */
6919 					tx = ms->refpoint->x;
6920 					ty = ms->refpoint->y + fabs (GMT->current.setting.map_label_offset[GMT_Y]);
6921 					justify = PSL_BC;
6922 					break;
6923 				default:	/* Bottom */
6924 					tx = ms->refpoint->x;
6925 					ty = ms->refpoint->y - dist_to_annot - GMT_LET_HEIGHT * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH - fabs (GMT->current.setting.map_label_offset[GMT_Y]);
6926 					justify = PSL_TC;
6927 					break;
6928 			}
6929 			this_label = (ms->label[0]) ? ms->label : label[unit];	/* Use user label or default to the unit names */
6930 			form = gmt_setfont (GMT, &GMT->current.setting.font_label);
6931 			PSL_plottext (PSL, tx, ty, GMT->current.setting.font_label.size, this_label, 0.0, justify, form);
6932 		}
6933 	}
6934 	else {	/* Simple scale has no annotation and just the length and unit centered beneath */
6935 		double xp[4], yp[4];	/* Line for simple scale */
6936 		PSL_comment (PSL, "Draw plain map scale\n");
6937 		if (panel && panel->mode) {	/* Place rectangle behind the map scale */
6938 			double x_center, y_center, dim[4];
6939 
6940 			/* Adjustment for size of largest annotation is half the length times dimensions: */
6941 			dim[XLO] = dim[XHI] = fabs (GMT->current.setting.map_annot_offset[GMT_PRIMARY]);
6942 			dim[YLO] = dist_to_annot + GMT_LET_HEIGHT * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH;
6943 			dim[YHI] = 0.0;
6944 			/* Determine center of gravity for panel */
6945 			x_center = ms->refpoint->x + 0.5 * (dim[XHI] - dim[XLO]);
6946 			y_center = ms->refpoint->y + 0.5 * (dim[YHI] - dim[YLO]);
6947 			/* Determine panel dimensions */
6948 			panel->width = bar_width + dim[XHI] + dim[XLO];	panel->height = dim[YHI] + dim[YLO];
6949 			gmt_draw_map_panel (GMT, x_center, y_center, 3U, panel);
6950 		}
6951 		/* Draw the simple scale bar using tick pen */
6952 		gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
6953 		xp[0] = xp[1] = x_left;	xp[2] = xp[3] = x_right;
6954 		yp[0] = yp[3] = ms->refpoint->y - scale_height;	yp[1] = yp[2] = ms->refpoint->y;
6955 		PSL_plotline (PSL, xp, yp, 4, PSL_MOVE|PSL_STROKE);
6956 		/* Make a basic label using the length and chosen unit and place below the scale */
6957 		gmt_sprintf_float (GMT, format, GMT->current.setting.format_float_map, ms->length);
6958 		if (gmt_M_is_geographic (GMT, GMT_IN))
6959 			snprintf (txt, GMT_LEN256, "%s %s", format, (ms->unit) ? units[unit] : label[unit]);
6960 		else {	/* No unit for Cartesian scales, only label */
6961 			if (ms->do_label)
6962 				snprintf (txt, GMT_LEN256, "%s %s", format, ms->label);
6963 			else
6964 				strcpy (txt, format);
6965 		}
6966 		form = gmt_setfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]);
6967 		PSL_plottext (PSL, ms->refpoint->x, ms->refpoint->y - dist_to_annot, GMT->current.setting.font_annot[GMT_PRIMARY].size, txt, 0.0, PSL_TC, form);
6968 	}
6969 	return GMT_OK;
6970 }
6971 
gmt_draw_vertical_scale(struct GMT_CTRL * GMT,struct GMT_MAP_SCALE * ms)6972 void gmt_draw_vertical_scale (struct GMT_CTRL *GMT, struct GMT_MAP_SCALE *ms) {
6973 	/* Draws a basic vertical scale bar at given refpoint and labels it as specified , */
6974 	double half_scale_length, scale, x0, y0, xx[4], yy[4], off, dim[2], sign = 1.0;
6975 	char txt[GMT_LEN256] = {""};
6976 	int form, just = PSL_ML;
6977 
6978 	gmt_set_refpoint (GMT, ms->refpoint);	/* Finalize reference point plot coordinates, if needed */
6979 	/* The ms->origin_mode checks only kick in for geographic plots */
6980 	if (ms->origin_mode == GMT_SCALE_ORIGIN_PLACE) {	/* Pick the lon/lat of the scale placement as map scale origin */
6981 		gmt_xy_to_geo (GMT, &ms->origin[GMT_X], &ms->origin[GMT_Y], ms->refpoint->x, ms->refpoint->y);
6982 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Vertical map scale origin selected to be %g/%g\n", ms->origin[GMT_X], ms->origin[GMT_Y]);
6983 	}
6984 	else if (ms->origin_mode == GMT_SCALE_ORIGIN_MIDDLE) {	/* Pick middle of map as map scale origin */
6985 		gmt_xy_to_geo (GMT, &ms->origin[GMT_X], &ms->origin[GMT_Y], 0.5 * GMT->current.map.width, 0.5 * GMT->current.map.height);
6986 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Vertical map scale origin selected to be %g/%g\n", ms->origin[GMT_X], ms->origin[GMT_Y]);
6987 	}
6988 	if (ms->label[0]) /* Append data unit to the scale length */
6989 		snprintf (txt, GMT_LEN256, "%g %s", ms->length, ms->label);
6990 	else
6991 		snprintf (txt, GMT_LEN256, "%g", ms->length);
6992 
6993 	if (ms->zdata) /* Append data unit to the scale length */
6994 		scale = ms->z_scale;
6995 	else
6996 		scale = GMT->current.proj.scale[GMT_Y];
6997 	half_scale_length = 0.5 * ms->length * scale;
6998 
6999 	off = ((GMT->current.setting.map_scale_height > 0.0) ? GMT->current.setting.map_tick_length[GMT_PRIMARY] : 0.0) + GMT->current.setting.map_annot_offset[GMT_PRIMARY];
7000 	dim[GMT_X] = strlen (txt) * GMT_DEC_WIDTH * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH + off;
7001 	dim[GMT_Y] = 2.0 * half_scale_length;
7002 
7003 	gmt_adjust_refpoint (GMT, ms->refpoint, dim, ms->off, ms->justify, PSL_ML);	/* Adjust refpoint to ML */
7004 
7005 	x0 = ms->refpoint->x;	y0 = ms->refpoint->y;
7006 	if (ms->panel && ms->panel->mode) {	/* Place rectangle behind the map scale */
7007 		double x_center, y_center;
7008 		x_center = x0 + 0.5 * dim[GMT_X]; y_center = y0;
7009 		ms->panel->width = dim[GMT_X];	ms->panel->height = dim[GMT_Y];
7010 		gmt_draw_map_panel (GMT, x_center, y_center, 3U, ms->panel);
7011 	}
7012 	/* Compute the 4 coordinates on the scale line */
7013 	if (ms->alignment == 'l') {	/* Flip scale with label on left */
7014 		x0 += dim[GMT_X];
7015 		just = PSL_MR;
7016 		sign = -1;
7017 	}
7018 	gmt_xyz_to_xy (GMT, x0 + sign * GMT->current.setting.map_scale_height, y0 - half_scale_length, 0.0, &xx[0], &yy[0]);
7019 	gmt_xyz_to_xy (GMT, x0, y0 - half_scale_length, 0.0, &xx[1], &yy[1]);
7020 	gmt_xyz_to_xy (GMT, x0, y0 + half_scale_length, 0.0, &xx[2], &yy[2]);
7021 	gmt_xyz_to_xy (GMT, x0 + sign * GMT->current.setting.map_scale_height, y0 + half_scale_length, 0.0, &xx[3], &yy[3]);
7022 	gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
7023 	PSL_plotline (GMT->PSL, xx, yy, 4, PSL_MOVE|PSL_STROKE);
7024 	form = gmt_setfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]);
7025 	PSL_plottext (GMT->PSL, x0 + sign * off, y0, GMT->current.setting.font_annot[GMT_PRIMARY].size, txt, 0.0, just, form);
7026 }
7027 
gmt_draw_vertical_scale_old(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL,double x0,double y0,double length,double zscale,int gave_xy,char * units)7028 void gmt_draw_vertical_scale_old (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL, double x0, double y0, double length, double zscale, int gave_xy, char *units) {
7029 	/* Draws a basic vertical scale bar at (x0,y0) and labels it as specified , struct GMT_MAP_SCALE *ms*/
7030 	int form;
7031 	double dy, off, xx[4], yy[4];
7032 	char txt[GMT_LEN256] = {""};
7033 
7034 	gmt_setpen (GMT, &GMT->current.setting.map_tick_pen[GMT_PRIMARY]);
7035 
7036 	if (!gave_xy) {	/* Project lon,lat to get position of scale */
7037 		gmt_geo_to_xy (GMT, x0, y0, &xx[0], &yy[0]);
7038 		x0 = xx[0];	y0 = yy[0];
7039 	}
7040 
7041 	if (units) /* Append data unit to the scale length */
7042 		snprintf (txt, GMT_LEN256, "%g %s", length, units);
7043 	else
7044 		snprintf (txt, GMT_LEN256, "%g", length);
7045 	dy = 0.5 * length * zscale;
7046 	/* Compute the 4 coordinates on the scale line */
7047 	gmt_xyz_to_xy (GMT, x0 + GMT->current.setting.map_scale_height, y0 - dy, 0.0, &xx[0], &yy[0]);
7048 	gmt_xyz_to_xy (GMT, x0, y0 - dy, 0.0, &xx[1], &yy[1]);
7049 	gmt_xyz_to_xy (GMT, x0, y0 + dy, 0.0, &xx[2], &yy[2]);
7050 	gmt_xyz_to_xy (GMT, x0 + GMT->current.setting.map_scale_height, y0 + dy, 0.0, &xx[3], &yy[3]);
7051 	PSL_plotline (PSL, xx, yy, 4, PSL_MOVE|PSL_STROKE);
7052 	off = ((GMT->current.setting.map_scale_height > 0.0) ? GMT->current.setting.map_tick_length[GMT_PRIMARY] : 0.0) + GMT->current.setting.map_annot_offset[GMT_PRIMARY];
7053 	form = gmt_setfont (GMT, &GMT->current.setting.font_annot[GMT_PRIMARY]);
7054 	PSL_plottext (PSL, x0 + off, y0, GMT->current.setting.font_annot[GMT_PRIMARY].size, txt, 0.0, PSL_ML, form);
7055 }
7056 
gmt_draw_map_rose(struct GMT_CTRL * GMT,struct GMT_MAP_ROSE * mr)7057 void gmt_draw_map_rose (struct GMT_CTRL *GMT, struct GMT_MAP_ROSE *mr) {
7058 	int tmp_join, tmp_limit;
7059 	struct PSL_CTRL *PSL= GMT->PSL;
7060 	struct GMT_MAP_PANEL *panel = mr->panel;
7061 	double dim[2];
7062 	if (!mr->plot) return;
7063 
7064 	dim[GMT_X] = dim[GMT_Y] = mr->size;
7065 	gmt_set_refpoint (GMT, mr->refpoint);	/* Finalize reference point plot coordinates, if needed */
7066 	gmt_adjust_refpoint (GMT, mr->refpoint, dim, mr->off, mr->justify, PSL_MC);	/* Adjust refpoint to MC */
7067 
7068 	if (panel && panel->mode) {	/* Place rectangle behind the map rose */
7069 		/* Determine panel dimensions */
7070 		panel->width = panel->height = mr->size;
7071 		gmt_draw_map_panel (GMT, mr->refpoint->x, mr->refpoint->y, 3U, panel);
7072 	}
7073 
7074 	/* Temporarily use miter to get sharp points to compass rose */
7075 	tmp_join = PSL->internal.line_join;	PSL_setlinejoin (PSL, 0);
7076 	tmp_limit = PSL->internal.miter_limit;	PSL_setmiterlimit (PSL, 0);
7077 
7078 	if (mr->type == GMT_ROSE_MAG)	/* Do magnetic compass rose */
7079 		gmtplot_draw_mag_rose (GMT, PSL, mr);
7080 	else
7081 		gmtplot_draw_dir_rose (GMT, PSL, mr);
7082 
7083 	/* Switch line join style back */
7084 	PSL_setlinejoin (PSL, tmp_join);
7085 	PSL_setmiterlimit (PSL, tmp_limit);
7086 }
7087 
gmt_draw_map_panel(struct GMT_CTRL * GMT,double x,double y,unsigned int mode,struct GMT_MAP_PANEL * P)7088 void gmt_draw_map_panel (struct GMT_CTRL *GMT, double x, double y, unsigned int mode, struct GMT_MAP_PANEL *P) {
7089 	/* Draw a rectangular backpanel behind things like logos, scales, legends, images.
7090 	 * Here, (x,y) is the center-point of the panel.
7091 	 * mode is a bit flag that can be 1,2, or 3:
7092 	 * mode = 1.  Lay down fills for background (if any fills) but no outlines
7093 	 * mode = 2.  Just draw any outlines requested
7094 	 * mode = 3.  Do both at the same time. */
7095 	double dim[3] = {0.0, 0.0, 0.0};
7096 	int outline;
7097 	struct GMT_FILL *fill = NULL;	/* Default is no fill */
7098 	if (!P) return;	/* No panel given */
7099 	outline = ((P->mode & GMT_PANEL_OUTLINE) == GMT_PANEL_OUTLINE) ? 1 : 0;	/* Does the panel have an outline? */
7100 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Place rectangular back panel\n");
7101 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Clearance: %g/%g/%g/%g\n", P->padding[XLO], P->padding[XLO], P->padding[YLO], P->padding[YHI]);
7102 	dim[GMT_X] = P->width  + P->padding[XLO] + P->padding[XHI];	/* Rectangle width */
7103 	dim[GMT_Y] = P->height + P->padding[YLO] + P->padding[YHI];	/* Rectangle height */
7104 	dim[GMT_Z] = P->radius;	/* Corner radius, or zero */
7105 	/* In case clearances are not symmetric we need to shift the symbol center accordingly */
7106 	x += 0.5 * (P->padding[XHI] - P->padding[XLO]);
7107 	y += 0.5 * (P->padding[YHI] - P->padding[YLO]);
7108 	if (mode == 1) outline = 0;	/* Do not draw outlines (even if requested) at this time since mode == 1*/
7109 	if ((mode & 1) && (P->mode & GMT_PANEL_SHADOW)) {	/* Draw offset background shadow first */
7110 		gmt_setfill (GMT, &P->sfill, 0);	/* The shadow has no outline */
7111 		PSL_plotsymbol (GMT->PSL, x + P->off[GMT_X], y + P->off[GMT_Y], dim, (P->mode & GMT_PANEL_ROUNDED) ? PSL_RNDRECT : PSL_RECT);
7112 	}
7113 	if ((mode & 2) && outline) gmt_setpen (GMT, &P->pen1);	/* Need to set frame outline pen */
7114 	if (mode & 1) fill = &P->fill;		/* Select fill (which may be NULL) unless we are just doing outlines */
7115 	if (fill || outline) gmt_setfill (GMT, fill, outline);	/* Activate frame fill, with optional outline */
7116 	if ((mode&1 && (P->mode & GMT_PANEL_FILL)) || (mode&2 && (P->mode & GMT_PANEL_OUTLINE))) PSL_plotsymbol (GMT->PSL, x, y, dim, (P->mode & GMT_PANEL_ROUNDED) ? PSL_RNDRECT : PSL_RECT);
7117 	if ((mode & 2) && (P->mode & GMT_PANEL_INNER)) {	/* Also draw secondary frame on the inside */
7118 		dim[GMT_X] -= 2.0 * P->gap;	/* Shrink dimension of panel by the uniform gap on all sides */
7119 		dim[GMT_Y] -= 2.0 * P->gap;
7120 		gmt_setpen (GMT, &P->pen2);	/* Set inner border pen */
7121 		gmt_setfill (GMT, NULL, 1);	/* Never fill for inner frame */
7122 		PSL_plotsymbol (GMT->PSL, x, y, dim, (P->mode & GMT_PANEL_ROUNDED) ? PSL_RNDRECT : PSL_RECT);
7123 	}
7124 	/* Reset color */
7125 	PSL_setcolor (GMT->PSL, GMT->current.setting.map_frame_pen.rgb, PSL_IS_STROKE);
7126 }
7127 
gmt_setpen(struct GMT_CTRL * GMT,struct GMT_PEN * pen)7128 void gmt_setpen (struct GMT_CTRL *GMT, struct GMT_PEN *pen) {
7129 	/* gmt_setpen issues PostScript code to set the specified pen. */
7130 
7131 	if (!pen) return;
7132 	GMT->current.setting.ps_penwidth = pen->width;	/* Remember current pen width */
7133 	PSL_setlinewidth (GMT->PSL, pen->width);
7134 	PSL_setdash (GMT->PSL, pen->style, pen->offset);
7135 	PSL_setcolor (GMT->PSL, pen->rgb, PSL_IS_STROKE);
7136 }
7137 
7138 #define GMT_N_COND_LEVELS	10	/* Number of max nesting level for conditionals */
7139 
gmtplot_get_the_pen(struct GMT_PEN * p,struct GMT_CUSTOM_SYMBOL_ITEM * s,struct GMT_PEN * cp,struct GMT_FILL * f)7140 GMT_LOCAL void gmtplot_get_the_pen (struct GMT_PEN *p, struct GMT_CUSTOM_SYMBOL_ITEM *s, struct GMT_PEN *cp, struct GMT_FILL *f) {
7141 	/* Returns the pointer to the pen we should use.  If this is an action pen then
7142 	 * we set restore to true so that we can reset it later.  If var_pen contains a -GMT_USE_FILL_RGB
7143 	 * then we overwrite the pen color with the current fill color */
7144 	if (s->pen) {
7145 		gmt_M_memcpy (p, s->pen, 1, struct GMT_PEN);
7146 	}
7147 	else if (cp) {
7148 		gmt_M_memcpy (p, cp, 1, struct GMT_PEN);
7149 	}
7150 	if (s->var_pen < 0 && (abs(s->var_pen) & GMT_USE_FILL_RGB) && f) gmt_M_rgb_copy (p->rgb, f->rgb);
7151 }
7152 
gmtplot_get_the_fill(struct GMT_FILL * f,struct GMT_CUSTOM_SYMBOL_ITEM * s,struct GMT_PEN * cp,struct GMT_FILL * cf)7153 GMT_LOCAL void gmtplot_get_the_fill (struct GMT_FILL *f, struct GMT_CUSTOM_SYMBOL_ITEM *s, struct GMT_PEN *cp, struct GMT_FILL *cf) {
7154 	/* Returns pointer to the chosen fill: The one specified by -G inside the macro or -G on command line.
7155 	 * If var_pen contains a -GMT_USE_PEN_RGB then we copy in the color of the current pen instead */
7156 	if (s->fill)
7157 		gmt_M_memcpy (f, s->fill, 1, struct GMT_FILL);
7158 	else if (cf)
7159 		gmt_M_memcpy (f, cf, 1, struct GMT_FILL);
7160 	else
7161 		f->rgb[0] = -1;	/* No fill */
7162 	if (f->rgb[0] >= 0.0 && s->var_pen < 0 && (abs(s->var_pen) & GMT_USE_PEN_RGB) && cp) gmt_M_rgb_copy (f->rgb, cp->rgb);
7163 }
7164 
gmtplot_draw_eps_symbol(struct GMT_CTRL * GMT,double x0,double y0,double size[],struct GMT_CUSTOM_SYMBOL_ITEM * symbol)7165 GMT_LOCAL void gmtplot_draw_eps_symbol (struct GMT_CTRL *GMT, double x0, double y0, double size[], struct GMT_CUSTOM_SYMBOL_ITEM *symbol) {
7166 	/* Special Encapsulated PostScript-only symbol */
7167 	struct GMT_CUSTOM_SYMBOL_EPS *E = symbol->eps;	/* SHorthand */
7168 	double off = 0.5*size[0], fy = (E->BB[3] - E->BB[2]) / (E->BB[1] - E->BB[0]);
7169 	struct PSL_CTRL *PSL= GMT->PSL;
7170 	if (!E->placed) {	/* First time we must dump the PS code definition */
7171 		double scl = 72.0 / (E->BB[1] - E->BB[0]);
7172 		PSL_comment (PSL, "Start of symbol %s\n", E->name);
7173 		PSL_command (PSL, "/Sk_%s {\nPSL_eps_begin\n", E->name);
7174 		/* We use the symbol's bounding box and scale its width to 1 inch since PSL uses inches */
7175 		PSL_command (PSL, "%.8f dup scale\n", scl);
7176 		if (!E->GMT_made)	/* Non-GMT-produced EPS macro - must scale points to GMT's unit */
7177 			PSL_command (PSL, "1200 72 div dup scale\n");
7178 		PSL_command (PSL, "%%%%BeginDocument: %s.eps\n", E->name);
7179 		PSL_copy (PSL, E->macro);	/* Since it may be quite large */
7180 		PSL_command (PSL, "%%%%EndDocument\n");
7181 		PSL_command (PSL, "PSL_eps_end } def\n");
7182 		PSL_comment (PSL, "End of symbol %s\n", E->name);
7183 		E->placed = true;	/* Flag now says we have dumped the EPS code */
7184 	}
7185 	PSL_command (PSL, "V ");
7186 	PSL_setorigin (PSL, x0-off, y0-fy*off, 0.0, PSL_FWD);
7187 	PSL_command (PSL, "%.12g dup scale ", size[0]);
7188 	PSL_command (PSL, "Sk_%s U\n", E->name);
7189 }
7190 
gmtplot_is_azimuth(struct GMT_CUSTOM_SYMBOL * symbol,struct GMT_CUSTOM_SYMBOL_ITEM * s,unsigned int var_no)7191 GMT_LOCAL bool gmtplot_is_azimuth (struct GMT_CUSTOM_SYMBOL *symbol, struct GMT_CUSTOM_SYMBOL_ITEM *s, unsigned int var_no) {
7192 	/* We either know a particular column has variable azimuths or we were given a constant angle flagged by a trailing 'a' */
7193     if (s->angular == GMT_IS_AZIMUTH) return true; /* Must convert a constant azimuth to Cartesian angle */
7194     if (s->is_var[var_no] && symbol->type[s->var[var_no]-1] == GMT_IS_AZIMUTH ) return true; /* Must convert variable azimuth to Cartesian angle */
7195 	return false;	/* Got Cartesian angle */
7196 }
7197 
gmt_draw_custom_symbol(struct GMT_CTRL * GMT,double x0,double y0,double size[],char * tr_text,struct GMT_CUSTOM_SYMBOL * symbol,struct GMT_PEN * pen,struct GMT_FILL * fill,unsigned int outline)7198 int gmt_draw_custom_symbol (struct GMT_CTRL *GMT, double x0, double y0, double size[], char *tr_text, struct GMT_CUSTOM_SYMBOL *symbol, struct GMT_PEN *pen, struct GMT_FILL *fill, unsigned int outline) {
7199 	int action, ifs, this_outline = 0, error = GMT_NOERROR;
7200 	unsigned int na, i, id = 0, level;
7201 	bool flush = false, skip[GMT_N_COND_LEVELS+1], done[GMT_N_COND_LEVELS+1];
7202 	uint64_t n = 0;
7203 	size_t n_alloc = 0;
7204 	double x, y, lon, lat, az, angle1, angle2, p_width = 0.0, *xx = NULL, *yy = NULL, *xp = NULL, *yp = NULL, dim[PSL_MAX_DIMS];
7205 	char user_text[GMT_LEN256] = {""};
7206 	struct GMT_CUSTOM_SYMBOL_ITEM *s = NULL;
7207 	struct GMT_FILL f, *current_fill = fill;
7208 	struct GMT_PEN save_pen, p, *current_pen = pen;
7209 	struct GMT_FONT font = GMT->current.setting.font_annot[GMT_PRIMARY];
7210 	struct PSL_CTRL *PSL= GMT->PSL;
7211 
7212 	/* Regular macro symbol */
7213 
7214 	/* Remember current settings as we wish to restore at the end */
7215 	gmtplot_savepen (GMT, &save_pen);
7216 	gmt_M_memset (&f, 1, struct GMT_FILL);
7217 	gmt_M_memset (&p, 1, struct GMT_PEN);
7218 	gmt_M_memset (skip, GMT_N_COND_LEVELS+1, bool);
7219 	gmt_M_memset (done, GMT_N_COND_LEVELS+1, bool);
7220 
7221 	if (symbol->text) {	/* This symbol places text, so we must set macros for fonts and fontsizes outside the gsave/grestore around each symbol */
7222 		symbol->text = 0;	/* Only do this formatting once */
7223 		s = symbol->first;	/* Start at first item */
7224 		while (s) {		/* Examine all items for possible text */
7225 			if (s->action == GMT_SYMBOL_TEXT || s->action == GMT_SYMBOL_VARTEXT) {	/* Text item found */
7226 				gmtplot_format_symbol_string (GMT, s, size, user_text);
7227 				if (s->p[0] < 0.0)	/* Fixed point size for text */
7228 					s->font.size = -s->p[0];
7229 				else	/* Fractional size that depends on symbol size */
7230 					s->font.size = s->p[0] * size[0] * PSL_POINTS_PER_INCH;
7231 				/* Set PS macro for fetching this font and size */
7232 				gmtplot_encodefont (PSL, s->font.id, symbol->name, id++);
7233 			}
7234 			s = s->next;
7235 		}
7236 	}
7237 
7238 	/* We encapsulate symbol with gsave and translate origin to (x0, y0) first */
7239 	PSL_command (PSL, "V ");
7240 	PSL_setorigin (PSL, x0, y0, 0.0, PSL_FWD);
7241 	gmt_set_meminc (GMT, GMT_SMALL_CHUNK);
7242 
7243 	s = symbol->first;
7244 	id = level = 0;
7245 	while (s) {
7246 		if (s->conditional > GMT_BEGIN_SINGLE_IF) {	/* Process if/elseif/else and endif by updating level and skip array, then go to next item */
7247 			/* We keep track of all the nested levels of tests via the skip array.  If a higher level test fails, then we will skip anything inside
7248 			 * it (e.g., lower-level nested test) since those tests don't matter since the upper test failed.  Hence skip is set to true for all deeper
7249 			 * tests (regardless of their actual test result) since we will not get there anyway if the earlier test failed. Finally, when we have
7250 			 * a series if if,elseif,else at the same level then we consult the done[level] array.  This is set to true if we pass a test and actually
7251 			 * draw something and once that is done none of the other tests at the same level can pass. */
7252 			if (s->conditional == GMT_BEGIN_BLOCK_IF) {	/* Beginning of a new if branch. If we are inside an earlier branch whose test was false then all inside shall be false */
7253 				if (level > 0 && skip[level])
7254 					skip[level+1] = true;
7255 				else {
7256 					if ((error = gmtplot_custum_failed_bool_test (GMT, s, size, tr_text, &skip[level+1]))) return (error);
7257 				}
7258 				level++;
7259 				if (level == GMT_N_COND_LEVELS) {
7260 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Symbol macro (%s) logical nesting too deep [> %d]\n", symbol->name, GMT_N_COND_LEVELS);
7261 					return GMT_DIM_TOO_LARGE;
7262 				}
7263 				done[level] = false;	/* Have not yet taken any action at this level */
7264 			}
7265 			else if (s->conditional == GMT_END_IF) {	/* Simply reduce indent */
7266 				if (level == 0) {
7267 					GMT_Report (GMT->parent, GMT_MSG_ERROR, "Symbol macro (%s) logical nesting error\n", symbol->name);
7268 					return GMT_RUNTIME_ERROR;
7269 				}
7270 				level--;
7271 			}
7272 			else if (s->conditional == GMT_END_IF_ELSE)	/* else branch */
7273 				skip[level] = (!done[level] && skip[level] && !skip[level-1]) ? false : true;
7274 			else if (s->conditional == GMT_BEGIN_ELSEIF) {	/* Skip if prior if/elseif was true, otherwise evaluate test at this level */
7275 				if (!done[level] && skip[level] && !skip[level-1]) {
7276 					if ((error = gmtplot_custum_failed_bool_test (GMT, s, size, tr_text, &skip[level]))) return (error);
7277 				}
7278 				else
7279 					skip[level] = true;
7280 			}
7281 			s = s->next;
7282 			continue;
7283 		}
7284 		if (level && skip[level]) {	/* We are inside an if-block but the block test was false, so we skip */
7285 			s = s->next;
7286 			continue;
7287 		}
7288 		/* Finally, check for 1-line if tests */
7289 		if (s->conditional == GMT_BEGIN_SINGLE_IF) {
7290 			bool retval;
7291 			if ((error = gmtplot_custum_failed_bool_test (GMT, s, size, tr_text, &retval))) return (error);
7292 			if (retval) {
7293 				s = s->next;	/* Done here, move to next item */
7294 				continue;
7295 			}
7296 		}
7297 		done[level] = true;	/* Here we will actually draw something */
7298 
7299 		/* Scale coordinates and size parameters by the scale in size[0] */
7300 
7301 		x = s->x * size[0];
7302 		y = s->y * size[0];
7303 		gmt_M_memset (dim, PSL_MAX_DIMS, double);
7304 		dim[0] = s->p[0] * size[0];
7305 		dim[1] = s->p[1] * size[0];
7306 		dim[2] = s->p[2] * size[0];
7307 		if (s->pen) {	/* This action has a pen setting */
7308 			p_width = s->pen->width;	/* Remember what it was before messing with it below */
7309 			if (s->pen->width < 0.0)	/* Convert the normalized pen width to points given current size */
7310 				s->pen->width = fabs (s->pen->width * size[0] * GMT->session.u2u[GMT_INCH][GMT_PT]);
7311 			else if (s->var_pen > 0)	/* Convert the specified variable to points */
7312 				s->pen->width = size[s->var_pen] * GMT->session.u2u[GMT_INCH][GMT_PT];
7313 		}
7314 		if (s->action == '?')	/* Reset to what is last in the input record */
7315 			action = GMT->current.io.curr_trailing_text[strlen(GMT->current.io.curr_trailing_text)-1];
7316 		else
7317 			action = s->action;
7318 
7319 		switch (action) {
7320 			case GMT_SYMBOL_MOVE:	/* Flush existing polygon and start a new path */
7321 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7322 				n = 0;
7323 				if (n >= n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
7324 				xx[n] = x, yy[n] = y, n++;
7325 				gmtplot_get_the_pen (&p, s, current_pen, current_fill);
7326 				gmtplot_get_the_fill (&f, s, current_pen, current_fill);
7327 				this_outline = (p.rgb[0] == -1) ? false : outline;
7328 				break;
7329 
7330 			case GMT_SYMBOL_STROKE:	/* To force the drawing of a line (outline == 2), not a closed polygon */
7331 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, 2, &flush);
7332 				n = 0;
7333 				break;
7334 
7335 			case GMT_SYMBOL_DRAW:	/* Append another point to the path */
7336 				flush = true;
7337 				if (n >= n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
7338 				xx[n] = x, yy[n] = y, n++;
7339 				break;
7340 
7341 			case GMT_SYMBOL_ARC:	/* Append a circular arc to the path */
7342 				flush = true;
7343 				angle1 = (s->is_var[1]) ? size[s->var[1]] : s->p[1];
7344 				angle2 = (s->is_var[2]) ? size[s->var[2]] : s->p[2];
7345 
7346 				if (gmtplot_is_azimuth (symbol, s, 1) || gmtplot_is_azimuth (symbol, s, 2)) {
7347 					angle1 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle1);
7348 					angle2 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle2);
7349 				}
7350 				na = gmtlib_get_arc (GMT, x, y, 0.5 * s->p[0] * size[0], angle1, angle2, &xp, &yp);
7351 				for (i = 0; i < na; i++) {
7352 					if (n >= n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
7353 					xx[n] = xp[i], yy[n] = yp[i], n++;
7354 				}
7355 				gmt_M_free (GMT, xp);
7356 				gmt_M_free (GMT, yp);
7357 				break;
7358 
7359 			case GMT_SYMBOL_ROTATE:		/* Rotate the symbol coordinate system by a fixed amount */
7360 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7361 				PSL_setorigin (PSL, 0.0, 0.0, s->p[0], PSL_FWD);
7362 				break;
7363 
7364 			case GMT_SYMBOL_AZIMROTATE:	/* Rotate the symbol y-axis to the a fixed azimuth */
7365 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7366 				/* Need to recover actual lon,lat location of symbol first */
7367 				gmt_xy_to_geo (GMT, &lon, &lat, x0, y0);
7368 				angle1 = gmt_azim_to_angle (GMT, lon, lat, 0.1, 90.0 - s->p[0]);
7369 				PSL_setorigin (PSL, 0.0, 0.0, angle1, PSL_FWD);
7370 				break;
7371 
7372 			case GMT_SYMBOL_VARROTATE:	/* Rotate the symbol coordinate system by a variable amount */
7373 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7374 				if (symbol->type[s->var[0]-1] == GMT_IS_AZIMUTH) {	/* Must convert azimuth to map angle then  rotatate by -pseudoazimuth */
7375 					gmt_xy_to_geo (GMT, &lon, &lat, x0, y0);
7376 					az = 90.0 - gmt_azim_to_angle (GMT, lon, lat, 0.1, s->var_sign[0] * size[s->var[0]]);
7377 					PSL_setorigin (PSL, 0.0, 0.0, -az, PSL_FWD);
7378 				}
7379 				else	/* Use rotation angle as given */
7380 					PSL_setorigin (PSL, 0.0, 0.0, s->var_sign[0] * size[s->var[0]], PSL_FWD);
7381 				break;
7382 
7383 			case GMT_SYMBOL_TEXTURE:	/* Change the current pen/fill settings */
7384 				if (s->fill) current_fill = s->fill;
7385 				if (s->pen) current_pen = s->pen;
7386 				break;
7387 
7388 			case (int)'C':
7389 				if (gmt_M_compat_check (GMT, 4)) {	/* Warn and purposefully fall through to assign the rest of the statements */
7390 					GMT_Report (GMT->parent, GMT_MSG_COMPAT, "Circle macro symbol C is deprecated; use c instead\n");
7391 					action = s->action = PSL_CIRCLE;	/* Backwards compatibility, circles are now 'c' */
7392 				}
7393 				else {
7394 					GMT_Report (GMT->parent, GMT_MSG_ERROR,
7395 									"Unrecognized symbol code (%d = '%c') passed to gmt_draw_custom_symbol\n", action, (char)action);
7396 					return GMT_PARSE_ERROR;
7397 					break;
7398 				}
7399 				/* Intentionally fall through - to assign the rest of the statements */
7400 			case PSL_CROSS:
7401 			case PSL_CIRCLE:
7402 			case PSL_SQUARE:
7403 			case PSL_TRIANGLE:
7404 			case PSL_DIAMOND:
7405 			case PSL_STAR:
7406 			case PSL_DOT:
7407 			case PSL_HEXAGON:
7408 			case PSL_OCTAGON:
7409 			case PSL_PENTAGON:
7410 			case PSL_PLUS:
7411 			case PSL_INVTRIANGLE:
7412 			case PSL_RECT:
7413 			case PSL_RNDRECT:
7414 			case PSL_XDASH:
7415 			case PSL_YDASH:
7416 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7417 				gmtplot_get_the_fill (&f, s, current_pen, current_fill);
7418 				gmtplot_get_the_pen (&p, s, current_pen, current_fill);
7419 				this_outline = (p.rgb[0] == -1) ? 0 : outline;
7420 				if (this_outline) gmt_setpen (GMT, &p);
7421 				gmt_setfill (GMT, &f, this_outline);
7422 				PSL_plotsymbol (PSL, x, y, dim, action);
7423 				break;
7424 
7425 			case PSL_ELLIPSE:
7426 			case PSL_ROTRECT:
7427 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7428 				gmtplot_get_the_fill (&f, s, current_pen, current_fill);
7429 				gmtplot_get_the_pen (&p, s, current_pen, current_fill);
7430 				this_outline = (p.rgb[0] == -1) ? 0 : outline;
7431 				if (this_outline) gmt_setpen (GMT, &p);
7432 				gmt_setfill (GMT, &f, this_outline);
7433 				angle1 = (s->is_var[0]) ? size[s->var[0]] : s->p[0];
7434 				if (gmtplot_is_azimuth (symbol, s, 0))
7435 					angle1 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle1);
7436 				dim[0] = angle1;
7437 				if (s->is_var[1]) dim[1] = size[s->var[1]];
7438 				PSL_plotsymbol (PSL, x, y, dim, PSL_ELLIPSE);
7439 				break;
7440 
7441 			case PSL_MARC:
7442 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7443 				gmtplot_get_the_fill (&f, s, current_pen, current_fill);
7444 				gmtplot_get_the_pen (&p, s, current_pen, current_fill);
7445 				this_outline = (p.rgb[0] == -1) ? 0 : outline;
7446 				if (this_outline) gmt_setpen (GMT, &p);
7447 				gmt_setfill (GMT, &f, this_outline);
7448 				angle1 = (s->is_var[1]) ? size[s->var[1]] : s->p[1];
7449 				angle2 = (s->is_var[2]) ? size[s->var[2]] : s->p[2];
7450 				if (gmtplot_is_azimuth (symbol, s, 1) || gmtplot_is_azimuth (symbol, s, 2)) {
7451 					angle1 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle1);
7452 					angle2 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle2);
7453 				}
7454 				dim[0] *= 0.5;	/* Give diameter */
7455 				dim[1] = angle1;
7456 				dim[2] = angle2;
7457 				dim[5] = p.width * GMT->session.u2u[GMT_PT][GMT_INCH];
7458 				PSL_plotsymbol (PSL, x, y, dim, PSL_MARC);
7459 				break;
7460 
7461 			case PSL_WEDGE:
7462 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7463 				gmtplot_get_the_fill (&f, s, current_pen, current_fill);
7464 				gmtplot_get_the_pen (&p, s, current_pen, current_fill);
7465 				this_outline = (p.rgb[0] == -1) ? 0 : outline;
7466 				if (this_outline) gmt_setpen (GMT, &p);
7467 				gmt_setfill (GMT, &f, this_outline);
7468 				angle1 = (s->is_var[1]) ? size[s->var[1]] : s->p[1];
7469 				angle2 = (s->is_var[2]) ? size[s->var[2]] : s->p[2];
7470 				if (gmtplot_is_azimuth (symbol, s, 1) || gmtplot_is_azimuth (symbol, s, 2)) {
7471 					angle1 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle1);
7472 					angle2 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle2);
7473 				}
7474 				dim[0] *= 0.5;	/* Give diameter */
7475 				dim[1] = angle1;
7476 				dim[2] = angle2;
7477 				dim[3] = 0;
7478 				dim[7] = 3;
7479 				PSL_plotsymbol (PSL, x, y, dim, PSL_WEDGE);
7480 				break;
7481 
7482 			case PSL_VECTOR:
7483 				/* Sets sensible vector head size derived from current stem pen thickness (-W). Head outline is always drawn, fill is optional */
7484 				gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7485 				gmtplot_get_the_fill (&f, s, current_pen, current_fill);
7486 				gmtplot_get_the_pen (&p, s, current_pen, current_fill);
7487 				this_outline = (p.rgb[0] == -1) ? 0 : outline;
7488 				if (this_outline) gmt_setpen (GMT, &p);
7489 				gmt_setfill (GMT, &f, this_outline);
7490 				gmt_xy_to_geo (GMT, &lon, &lat, x0, y0);
7491 				angle1 = (s->is_var[0]) ? size[s->var[0]] : s->p[0];
7492 				if (gmtplot_is_azimuth (symbol, s, 0))
7493 					angle1 = gmt_azim_to_angle (GMT, lon, lat, 0.1, angle1);
7494 				PSL_setorigin (PSL, 0.0, 0.0, angle1, PSL_FWD);
7495 				dim[PSL_VEC_XTIP]        = 2.0 * s->p[1] * size[0];	/* Must undo a 0.5 in psxy */
7496 				dim[PSL_VEC_YTIP]        = 0.0;
7497 				dim[PSL_VEC_TAIL_WIDTH]  = current_pen->width / PSL_POINTS_PER_INCH;
7498 				dim[PSL_VEC_HEAD_LENGTH] = 8.0 * current_pen->width / PSL_POINTS_PER_INCH;
7499 				dim[PSL_VEC_HEAD_WIDTH]  = dim[PSL_VEC_HEAD_LENGTH] * 0.5358983848621;	/* 2*tan (15) as default apex is 30 */
7500 				dim[PSL_VEC_HEAD_SHAPE]  = 0.5;
7501 				dim[PSL_VEC_HEAD_PENWIDTH]   = 0.5 * current_pen->width;
7502 				dim[PSL_VEC_STATUS]      = PSL_VEC_END | PSL_VEC_FILL | PSL_VEC_OUTLINE;
7503 				PSL_defpen (PSL, "PSL_vecheadpen", current_pen->width, current_pen->style, current_pen->offset, current_pen->rgb);
7504 				PSL_plotsymbol (PSL, x, y, dim, PSL_VECTOR);
7505 				PSL_setorigin (PSL, 0.0, 0.0, -angle1, PSL_INV);
7506 				break;
7507 
7508 			case GMT_SYMBOL_TEXT:
7509 			case GMT_SYMBOL_VARTEXT:
7510 				if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7511 				gmtplot_get_the_fill (&f, s, current_pen, current_fill);
7512 				gmtplot_get_the_pen (&p, s, current_pen, current_fill);
7513 				this_outline = (p.rgb[0] == -1) ? 0 : outline;
7514 				if (this_outline) gmt_setpen (GMT, &p);
7515 				gmtplot_format_symbol_string (GMT, s, size, user_text);
7516 				if (s->p[0] < 0.0)	/* Fixed point size */
7517 					font.size = -s->p[0];
7518 				else	/* Fractional size */
7519 					font.size = s->p[0] * size[0] * PSL_POINTS_PER_INCH;
7520 				ifs = (int)lrint (font.size * PSL->internal.dpp);	/* Basically psl_ip */
7521 				gmt_setfont (GMT, &s->font);
7522 				if (f.rgb[0] >= 0.0 && this_outline)
7523 					gmt_setfill (GMT, &f, this_outline);
7524 				else if (f.rgb[0] >= 0.0)
7525 					PSL_setcolor (PSL, f.rgb, PSL_IS_FONT);
7526 				else
7527 					PSL_setfill (PSL, GMT->session.no_rgb, this_outline);
7528 				PSL_command (PSL, "%d PSL_symbol_%s_setfont_%d\n", ifs, symbol->name, id++);
7529 				PSL_plottext (PSL, x, y, font.size, user_text, 0.0, s->justify, this_outline);
7530 				break;
7531 
7532 			case GMT_SYMBOL_EPS:
7533 				/* Special Encapsulated PostScript-only symbol */
7534 				gmtplot_draw_eps_symbol (GMT, x, y, dim, s);
7535 				break;
7536 
7537 			default:
7538 				GMT_Report (GMT->parent, GMT_MSG_ERROR,
7539 								"Unrecognized symbol code (%d = '%c') passed to gmt_draw_custom_symbol\n", action, (char)action);
7540 				return GMT_PARSE_ERROR;
7541 				break;
7542 		}
7543 		if (s->pen) s->pen->width = p_width;	/* Reset to what it was before scaling */
7544 		s = s->next;
7545 	}
7546 	if (flush) gmtplot_flush_symbol_piece (GMT, PSL, xx, yy, &n, &p, &f, this_outline, &flush);
7547 	PSL_command (PSL, "U\n");
7548 	PSL_comment (PSL, "End of symbol %s\n", symbol->name);
7549 	gmt_reset_meminc (GMT);
7550 
7551 	/* Restore settings */
7552 	gmt_setpen (GMT, &save_pen);
7553 	if (xx) gmt_M_free (GMT, xx);
7554 	if (yy) gmt_M_free (GMT, yy);
7555 
7556 	return (GMT_OK);
7557 }
7558 
7559 /* Plotting functions related to contours */
7560 
gmt_add_label_record(struct GMT_CTRL * GMT,struct GMT_DATASET * T,double x,double y,double angle,char * label)7561 void gmt_add_label_record (struct GMT_CTRL *GMT, struct GMT_DATASET *T, double x, double y, double angle, char *label) {
7562 	/* Add one record to the output file */
7563 	double geo[2];
7564 	uint64_t col, rec = T->table[0]->segment[0]->n_rows;	/* Current record */
7565 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (T->table[0]->segment[0]);
7566 	/* Convert lon/lat and save x/y */
7567 	gmt_xy_to_geo (GMT, &geo[GMT_X], &geo[GMT_Y], x, y);
7568 	T->table[0]->segment[0]->data[GMT_X][rec] = geo[GMT_X];
7569 	T->table[0]->segment[0]->data[GMT_Y][rec] = geo[GMT_Y];
7570 	/* Also output the label angle */
7571 	T->table[0]->segment[0]->data[GMT_Z][rec] = angle;
7572 	/* Then save the label */
7573 	T->table[0]->segment[0]->text[rec] = strdup (label);
7574 	/* Increment counter */
7575 	T->table[0]->segment[0]->n_rows++;
7576 	if (T->table[0]->segment[0]->n_rows == SH->n_alloc) {
7577 		SH->n_alloc <<= 1;
7578 		T->table[0]->segment[0]->text = gmt_M_memory (GMT, T->table[0]->segment[0]->text, SH->n_alloc, char *);
7579 		for (col = 0; col < T->n_columns; col++)
7580 			T->table[0]->segment[0]->data[col] = gmt_M_memory (GMT, T->table[0]->segment[0]->data[col], SH->n_alloc, double);
7581 	}
7582 }
7583 
gmt_contlabel_save_begin(struct GMT_CTRL * GMT,struct GMT_CONTOUR * G)7584 int gmt_contlabel_save_begin (struct GMT_CTRL *GMT, struct GMT_CONTOUR *G) {
7585 	int kind;
7586 	uint64_t k, seg, dim[4] = {1, 1, GMT_SMALL_CHUNK, 3};	/* Single table with 1 segment and unknown rows, each with 3 columns and trailing text */
7587 	char record[GMT_BUFSIZ] = {""};
7588 	char *xname[2] = {"x", "lon"}, *yname[2] = {"y", "lat"};
7589 	double angle = 0.0;
7590 	struct GMT_CONTOUR_LINE *L = NULL;
7591 
7592 	/* Save the lon, lat, angle, text for each annotation to specified file */
7593 
7594 	kind = gmt_M_is_geographic (GMT, GMT_IN);
7595 	if ((G->Out = GMT_Create_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_POINT, GMT_WITH_STRINGS, dim, NULL, NULL, 0, 0, NULL)) == NULL) {
7596 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to create a dataset\n");
7597 		return (GMT_MEMORY_ERROR);	/* Establishes data output */
7598 	}
7599 	/* Write lon, lat, angle, label record */
7600 	snprintf (record, GMT_BUFSIZ, "# %s%s%s%sangle%slabel", xname[kind], GMT->current.setting.io_col_separator, yname[kind],
7601 		GMT->current.setting.io_col_separator, GMT->current.setting.io_col_separator);
7602 	GMT_Set_Comment (GMT->parent, GMT_IS_DATASET, GMT_COMMENT_IS_TEXT | GMT_COMMENT_IS_COMMAND, record, G->Out);
7603 	G->Out->table[0]->segment[0]->n_rows = 0;
7604 	for (seg = 0; seg < G->n_segments; seg++) {
7605 		L = G->segment[seg];	/* Pointer to current segment */
7606 		if (!L->annot || L->n_labels == 0) continue;
7607 		for (k = 0; k < L->n_labels; k++) {
7608 			angle = fmod (2.0 * (L->L[k].angle + 360.0), 360.0) / 2.0;		/* Get text line in 0-180 range */
7609 			gmt_add_label_record (GMT, G->Out, L->L[k].x, L->L[k].y, angle, L->L[k].label);	/* Store text record */
7610 		}
7611 	}
7612 	gmtlib_finalize_dataset (GMT, G->Out);
7613 	return (GMT_NOERROR);
7614 	/* To finish and close the file, call gmt_contlabel_save_end */
7615 }
7616 
gmt_contlabel_save_end(struct GMT_CTRL * GMT,struct GMT_CONTOUR * G)7617 int gmt_contlabel_save_end (struct GMT_CTRL *GMT, struct GMT_CONTOUR *G) {
7618 	/* Finalize this dataset and write it out */
7619 	gmt_set_dataset_minmax (GMT, G->Out);
7620 	if (GMT_Write_Data (GMT->parent, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_WRITE_SET, NULL, G->label_file, G->Out) != GMT_NOERROR) {
7621 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to create/write to file %s\n", G->label_file);
7622 		return (GMT_ERROR_ON_FOPEN);	/* Establishes data output */
7623 	}
7624 	GMT_Destroy_Data (GMT->parent, &(G->Out));
7625 	return (GMT_NOERROR);
7626 }
7627 
gmt_textpath_init(struct GMT_CTRL * GMT,struct GMT_PEN * BP,double Brgb[])7628 void gmt_textpath_init (struct GMT_CTRL *GMT, struct GMT_PEN *BP, double Brgb[]) {
7629 	PSL_comment (GMT->PSL, "Pen and fill for text boxes (if enabled):\n");
7630 	PSL_defpen (GMT->PSL, "PSL_setboxpen", BP->width, BP->style, BP->offset, BP->rgb);
7631 	PSL_defcolor (GMT->PSL, "PSL_setboxrgb", Brgb);
7632 }
7633 
gmt_contlabel_plot(struct GMT_CTRL * GMT,struct GMT_CONTOUR * G)7634 void gmt_contlabel_plot (struct GMT_CTRL *GMT, struct GMT_CONTOUR *G) {
7635 	unsigned int i, mode;
7636 	bool no_labels;
7637 	struct PSL_CTRL *PSL= GMT->PSL;
7638 
7639 	if (!G->n_segments) return;	/* Northing to do here */
7640 
7641 	/* See if there are labels at all */
7642 	for (i = 0, no_labels = true; i < G->n_segments && no_labels; i++)
7643 		if (G->segment[i]->n_labels) no_labels = false;
7644 
7645 	if (!G->delay) PSL_command (GMT->PSL, "V\n");	/* Plotting something, so protect current graphics state */
7646 
7647 	if (G->debug) gmtplot_contlabel_debug (GMT, PSL, G);		/* Debugging lines and points */
7648 
7649 	if (no_labels) {	/* No labels, just draw lines; no clipping required */
7650 		gmtplot_contlabel_drawlines (GMT, PSL, G, 0);
7651 		PSL_command (GMT->PSL, "U\n");	/* Restore to where we were */
7652 		return;
7653 	}
7654 
7655 	PSL_settextmode (PSL, PSL_TXTMODE_MINUS);	/* Replace hyphens with minus signs */
7656 	gmt_setfont (GMT, &G->font_label);
7657 
7658 	if (G->must_clip) {		/* Transparent boxes means we must set up plot text, then set up clip paths, then draw lines, then deactivate clipping */
7659 		/* Place PSL variables, plot labels, set up clip paths, draw lines */
7660 		mode = PSL_TXT_INIT | PSL_TXT_SHOW | PSL_TXT_CLIP_ON;
7661 		if (G->draw) mode |= PSL_TXT_DRAW;
7662 		if (!G->delay) mode |= PSL_TXT_CLIP_OFF;	/* Also turn off clip path when done */
7663 		gmtplot_contlabel_plotlabels (GMT, PSL, G, mode);	/* Take the above actions */
7664 	}
7665 	else {	/* Opaque text boxes */
7666 		mode = PSL_TXT_INIT;	/* Plot text */
7667 		if (G->draw) mode |= PSL_TXT_DRAW;	/* Draw lines */
7668 		gmtplot_contlabel_plotlabels (GMT, PSL, G, mode);	/* Place PSL variables and draw lines */
7669 		mode = PSL_TXT_SHOW;				/* Plot text */
7670 		if (G->delay) mode |= PSL_TXT_CLIP_ON;		/* Also turn on clip path after done */
7671 		gmtplot_contlabel_plotlabels (GMT, PSL, G, mode);	/* Plot labels and possibly turn on clipping if delay */
7672 	}
7673 	PSL_command (GMT->PSL, "[] 0 B\n");	/* Ensure no pen textures remain in effect */
7674 	PSL_settextmode (PSL, PSL_TXTMODE_HYPHEN);	/* Back to leave as is */
7675 
7676 	if (!G->delay) PSL_command (GMT->PSL, "U\n");	/* Restore to where we were */
7677 }
7678 
7679 #if 0        	// We have no current need for this anymore
7680 GMT_LOCAL void gmtplot_wipe_substr(char *str1, char *str2) {
7681 	/* Set the substring str2 of str1 to blanks */
7682 	return;
7683 	char *pch;
7684 	if ((pch = strstr(str1, str2)) != NULL) {
7685 		size_t k;
7686 		for (k = 0; k < strlen(str2); k++)
7687 			pch[k] = ' ';
7688 	}
7689 }
7690 #endif
7691 
7692 #ifdef HAVE_GDAL
gmt_importproj4(struct GMT_CTRL * GMT,char * pStr,int * scale_pos)7693 char *gmt_importproj4 (struct GMT_CTRL *GMT, char *pStr, int *scale_pos) {
7694 	/* Take a PROJ.4 projection string or EPSG code and try to find the equivalent -J syntax
7695 		scale_pos is position on the return string where starts the scale sub-string.
7696 	*/
7697 	unsigned int pos = 0;
7698 	//bool got_lonlat = false;
7699 	char opt_J[GMT_LEN256] = {""}, szProj4[GMT_LEN256] = {""}, prjcode[16] = {""};
7700 	char token[GMT_LEN256] = {""}, scale_c[GMT_LEN32] = {""}, *pch = NULL, *pStrOut = NULL;
7701 	char lon_0[32] = {""}, lat_0[32] = {""}, lat_1[32] = {""}, lat_2[32] = {""}, lat_ts[32] = {""};
7702 
7703 	snprintf(szProj4, GMT_LEN256-1, "%s", pStr);
7704 
7705 	if ((pch = strchr(szProj4, '/')) != NULL) {		/* Get the scale factor and chop it out of proj string */
7706 		strncpy(scale_c, &pch[1], GMT_LEN32-1);
7707 		pch[0] = '\0';
7708 	}
7709 	else {
7710 		GMT->current.ps.active ? sprintf(scale_c, "14c") : sprintf(scale_c, "1:1");
7711 	}
7712 
7713 	if (isdigit(szProj4[1])) {		/* A EPSG code. By looking at 2nd char instead of 1st both +epsg and epsg work */
7714 		int   EPSGID;
7715 		char *pszResult = NULL;
7716 		OGRSpatialReferenceH  hSRS;
7717 		OGRErr eErr = OGRERR_NONE;
7718 #if 0
7719 		bool found = false;
7720 		char buffer [GMT_LEN256] = {""};
7721 		FILE *fp = NULL;
7722 #endif
7723 
7724 		if ((pch = strstr(szProj4, "+width=")) != NULL || (pch = strstr(szProj4, "+scale=")) != NULL) {
7725 			snprintf (scale_c, GMT_LEN32-1, "%s", pch);
7726 			pch[0] = '\0';			/* Strip it */
7727 		}
7728 
7729 		if (szProj4[0] == '+')		/* Let it both work: -J+epsg or -Jepsg */
7730 			EPSGID = atoi(&szProj4[1]);
7731 		else
7732 			EPSGID = atoi(szProj4);
7733 
7734 		/* Rely on GDAL to tell us the proj4 string of this EPSG code */
7735 		hSRS = OSRNewSpatialReference(NULL);
7736 		if ((eErr = OSRImportFromEPSG(hSRS, EPSGID)) != OGRERR_NONE) {
7737 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Did not get the SRS from input EPSG  %d\n", EPSGID);
7738 			return (pStrOut);
7739 		}
7740 		if ((eErr = OSRExportToProj4(hSRS, &pszResult)) != OGRERR_NONE) {
7741 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Failed to convert the SRS to proj4 syntax\n");
7742 			return (pStrOut);
7743 		}
7744 		snprintf(szProj4, GMT_LEN256-1, "%s", pszResult);
7745 		if (scale_c[0] != '\0') strcat (szProj4, scale_c);		/* Add the width/scale found above */
7746 		CPLFree(pszResult);
7747 		OSRDestroySpatialReference(hSRS);
7748 
7749 #if 0
7750 		gmt_getsharepath (GMT, "", "epsg", ".txt", buffer, R_OK);
7751 		if ((fp = fopen (buffer, "r")) == NULL) {
7752 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open %s file\n", buffer);
7753 			return (pStrOut);
7754 		}
7755 		while (fgets (buffer, GMT_LEN256, fp)) {
7756 			if (buffer[0] == '#') continue;
7757 			if ((pch = strstr(buffer, "+proj")) != NULL) {
7758 				pch[0] = '\0';			/* Break the line before the +proj=... */
7759 				if (EPSGID == atoi(buffer)) {
7760 					pch[0] = '+';		/* Undo previous break */
7761 					snprintf(szProj4, GMT_LEN256-1, "%s", pch);
7762 					found = true;
7763 					break;
7764 				}
7765 			}
7766 		}
7767 		fclose (fp);
7768 		if (!found) {
7769 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not find the EPGS code %d in database\n", EPSGID);
7770 			return (pStrOut);
7771 		}
7772 #endif
7773 	}
7774 
7775 	if (gmt_strtok(szProj4, " \t+", &pos, token)) {
7776 		snprintf(prjcode, 16, "%s",(token[0] == '+' ? &token[6] : &token[5]));	/* PROJ4 projection code. */
7777 		//gmtplot_wipe_substr(szProj4, token);	/* Consumed, clear it from list */
7778 	}
7779 
7780 	if (!strcmp(prjcode, "longlat") || !strcmp(prjcode, "latlong")) {
7781 		strcat (opt_J, "X");
7782 		GMT->current.proj.projection_GMT = GMT_LINEAR;
7783 		//got_lonlat = true;			/* At the end we need to append a 'd' to the scale */
7784 	}
7785 	/* Cylindrical projections */
7786 	else if (!strcmp(prjcode, "cea") || !strcmp(prjcode, "eqc") || !strcmp(prjcode, "tmerc") ||
7787 			  !strcmp(prjcode, "mill") || !strcmp(prjcode, "merc") || !strcmp(prjcode, "cass")) {
7788 		if (!strcmp(prjcode, "tmerc"))     strcat (opt_J, "T");
7789 		else if (!strcmp(prjcode, "cea"))  strcat (opt_J, "Y");
7790 		else if (!strcmp(prjcode, "eqc"))  strcat (opt_J, "Q");
7791 		else if (!strcmp(prjcode, "merc")) strcat (opt_J, "M");
7792 		else if (!strcmp(prjcode, "mill")) strcat (opt_J, "J");
7793 		else strcat (opt_J, "C");
7794 		while (gmt_strtok (szProj4, " \t+", &pos, token)) {
7795 			if ((pch = strstr(token, "lon_0=")) != NULL) {
7796 				strncat(lon_0, &token[6], 31);
7797 				//gmtplot_wipe_substr(szProj4, token);
7798 			}
7799 			else if ((pch = strstr(token, "lat_0=")) != NULL) {
7800 				strncat(lat_0, &token[6], 31);
7801 				//gmtplot_wipe_substr(szProj4, token);
7802 			}
7803 		}
7804 		if (!strcmp(prjcode, "cea") || !strcmp(prjcode, "eqc") || !strcmp(prjcode, "cass") ||
7805 			 !strcmp(prjcode, "tmerc") || !strcmp(prjcode, "merc")) {
7806 			if (!lon_0[0]) strcat(lon_0, "0");
7807 			if (!lat_0[0]) strcat(lat_0, "0");
7808 			strcat(opt_J, lon_0);	strcat (opt_J, "/");
7809 			strcat(opt_J, lat_0);	strcat (opt_J, "/");
7810 		}
7811 		else {		// "mill"
7812 			if (lon_0[0]) strcat(opt_J, lon_0),	strcat (opt_J, "/");
7813 		}
7814 	}
7815 	else if (!strcmp(prjcode, "omerc")) {
7816 		char lon_1[32] = {""}, lon_2[32] = {""}, lonc[32] = {""}, alpha[32] = {""};
7817 		strcat (opt_J, "OC");
7818 		while (gmt_strtok (szProj4, " \t+", &pos, token)) {
7819 			if ((pch = strstr(token, "lon_1=")) != NULL)
7820 				strncat(lon_1, &token[6], 31);
7821 			else if ((pch = strstr(token, "lat_1=")) != NULL)
7822 				strncat(lat_1, &token[6], 31);
7823 			else if ((pch = strstr(token, "lon_2=")) != NULL)
7824 				strncat(lon_2, &token[6], 31);
7825 			else if ((pch = strstr(token, "lat_2=")) != NULL)
7826 				strncat(lat_2, &token[6], 31);
7827 			else if ((pch = strstr(token, "lat_0=")) != NULL)
7828 				strncat(lat_0, &token[6], 31);
7829 			else if ((pch = strstr(token, "lonc=")) != NULL)
7830 				strncat(lonc, &token[5], 31);
7831 			else if ((pch = strstr(token, "alpha=")) != NULL)
7832 				strncat(alpha, &token[6], 31);
7833 		}
7834 		if (lon_2[0] && lat_2[0]) {
7835 			if (!lon_1[0]) strcat(lon_1, "0");
7836 			if (!lat_1[0]) strcat(lat_1, "0");
7837 			strcat(opt_J, lon_1);	strcat (opt_J, "/");	strcat(opt_J, lat_1);	strcat (opt_J, "/");
7838 			strcat(opt_J, lon_2);	strcat (opt_J, "/");	strcat(opt_J, lat_2);	strcat (opt_J, "/");
7839 		}
7840 		else if (lonc[0] && alpha[0]) {
7841 			if (!lat_0[0]) strcat(lat_0, "0");
7842 			strcat(opt_J, lonc);	strcat (opt_J, "/");	strcat(opt_J, lat_0);	strcat (opt_J, "/");
7843 			strcat(opt_J, alpha);	strcat (opt_J, "/");
7844 			opt_J[1] = 'A';
7845 		}
7846 		else {
7847 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "In projection %s proj parameters\n", prjcode);
7848 			return (pStrOut);
7849 		}
7850 	}
7851 	else if (!strcmp(prjcode, "utm")) {
7852 		int zone = 100;
7853 		strcat (opt_J, "U");
7854 		while (gmt_strtok (szProj4, " \t+", &pos, token)) {
7855 			if ((pch = strstr(token, "zone=")) != NULL) {
7856 				zone = atoi(&token[5]);
7857 				//gmtplot_wipe_substr(szProj4, token);
7858 			}
7859 			else if ((pch = strstr(token, "south=")) != NULL) {
7860 				zone *= -1;
7861 				//gmtplot_wipe_substr(szProj4, token);
7862 			}
7863 			else if ((pch = strstr(token, "lon_0=")) != NULL) {
7864 				double x;
7865 				gmt_scanf (GMT, &token[6], GMT_IS_GEO, &x);
7866 				if (x > 180) x -= 360;
7867 				zone = (int)(x + 180) / 6 + 1;
7868 				//gmtplot_wipe_substr(szProj4, token);
7869 			}
7870 		}
7871 		if (zone == 100) {
7872 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "UTM proj selected but no info about utm zone.\n");
7873 			return (pStrOut);
7874 		}
7875 		else if (zone > 64) {
7876 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "UTM proj The lon_0 argument was not correctly parsed.\n");
7877 		}
7878 		else {
7879 			char t[4];
7880 			sprintf(t, "%d/", zone);
7881 			strcat(opt_J, t);
7882 		}
7883 	}
7884 
7885 	/* Conic projections */
7886 	else if (!strcmp(prjcode, "aea") || !strcmp(prjcode, "eqdc") || !strcmp(prjcode, "lcc") || !strcmp(prjcode, "poly")) {
7887 		if (!strcmp(prjcode, "aea")) strcat (opt_J, "B");
7888 		else if (!strcmp(prjcode, "eqdc")) strcat (opt_J, "D");
7889 		else if (!strcmp(prjcode, "lcc")) strcat (opt_J, "L");
7890 		else strcat (opt_J, "Poly/");
7891 		while (gmt_strtok (szProj4, " \t+", &pos, token)) {
7892 			if ((pch = strstr(token, "lon_0=")) != NULL) {
7893 				strncat(lon_0, &token[6], 31);
7894 				//gmtplot_wipe_substr(szProj4, token);
7895 			}
7896 			else if ((pch = strstr(token, "lat_0=")) != NULL) {
7897 				strncat(lat_0, &token[6], 31);
7898 				//gmtplot_wipe_substr(szProj4, token);
7899 			}
7900 			else if ((pch = strstr(token, "lat_1=")) != NULL) {
7901 				strncat(lat_1, &token[6], 31);
7902 				//gmtplot_wipe_substr(szProj4, token);
7903 			}
7904 			else if ((pch = strstr(token, "lat_2=")) != NULL) {
7905 				strncat(lat_2, &token[6], 31);
7906 				//gmtplot_wipe_substr(szProj4, token);
7907 			}
7908 		}
7909 		/* Check if user errors */
7910 		if (!lon_0[0]) strcat(lon_0, "0");
7911 		if (!lat_0[0]) strcat(lat_0, "0");
7912 		if (strcmp(prjcode, "poly")) {			/* i.e. if NOT Poly */
7913 			if (!lat_1[0] || !lat_2[0]) {
7914 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Projection %s needs the lat_1 & lat_2 proj parameters\n", prjcode);
7915 				return (pStrOut);
7916 			}
7917 			strcat(opt_J, lon_0);	strcat (opt_J, "/");	strcat(opt_J, lat_0);	strcat (opt_J, "/");
7918 			strcat(opt_J, lat_1);	strcat (opt_J, "/");	strcat(opt_J, lat_2);	strcat (opt_J, "/");
7919 		}
7920 		else {
7921 			strcat(opt_J, lon_0);	strcat (opt_J, "/");	strcat(opt_J, lat_0);	strcat (opt_J, "/");
7922 		}
7923 	}
7924 
7925 	/* Azimuthal projections */
7926 	else if (!strcmp(prjcode, "stere") || !strcmp(prjcode, "laea") || !strcmp(prjcode, "aeqd") || !strcmp(prjcode, "gnom") ||
7927 				!strcmp(prjcode, "sterea") || !strcmp(prjcode, "stere")) {
7928 		if (!strcmp(prjcode, "stere")) strcat (opt_J, "S");
7929 		else if (!strcmp(prjcode, "laea")) strcat (opt_J, "A");
7930 		else if (!strcmp(prjcode, "aeqd")) strcat (opt_J, "E");
7931 		else if (!strcmp(prjcode, "gnom")) strcat (opt_J, "F");
7932 		else strcat (opt_J, "S");
7933 		while (gmt_strtok (szProj4, " \t+", &pos, token)) {
7934 			if ((pch = strstr(token, "lon_0=")) != NULL) {
7935 				strncat(lon_0, &token[6], 31);
7936 				//gmtplot_wipe_substr(szProj4, token);
7937 			}
7938 			else if ((pch = strstr(token, "lat_0=")) != NULL) {
7939 				strncat(lat_0, &token[6], 31);
7940 				//gmtplot_wipe_substr(szProj4, token);
7941 			}
7942 			else if ((pch = strstr(token, "lat_ts=")) != NULL) {
7943 				strncat(lat_ts, &token[7], 31);
7944 				//gmtplot_wipe_substr(szProj4, token);
7945 			}
7946 		}
7947 		//if (!strcmp(prjcode, "ups")) lon_0[0] = lat_0[0] = '\0';
7948 		if (!strcmp(prjcode, "stere")) {
7949 			if (!lat_0[0]) strcat(lat_0, "90");		/* ptoj4 says lat_0 = 90 but what if in southerm hemisphere? */
7950 		}
7951 		if (!lon_0[0]) strcat(lon_0, "0");
7952 		if (!lat_0[0]) strcat(lat_0, "0");
7953 		strcat(opt_J, lon_0);	strcat (opt_J, "/");	strcat(opt_J, lat_0);	strcat (opt_J, "/");
7954 		if (!strcmp(prjcode, "stere")) strcat(opt_J, "75/");
7955 	}
7956 
7957 	/* Misc projections */
7958 	else if (!strcmp(prjcode, "moll") || !strcmp(prjcode, "sinu") || !strcmp(prjcode, "vandg") ||
7959 				!strcmp(prjcode, "robin") || !strcmp(prjcode, "wintri") || !strcmp(prjcode, "hammer") ||
7960 				!strcmp(prjcode, "eck4") || !strcmp(prjcode, "eck6")) {
7961 		if (!strcmp(prjcode, "moll")) strcat (opt_J, "W");
7962 		else if (!strcmp(prjcode, "sinu"))   strcat (opt_J, "I");
7963 		else if (!strcmp(prjcode, "vandg"))  strcat (opt_J, "V");
7964 		else if (!strcmp(prjcode, "robin"))  strcat (opt_J, "N");
7965 		else if (!strcmp(prjcode, "wintri")) strcat (opt_J, "R");
7966 		else if (!strcmp(prjcode, "hammer")) strcat (opt_J, "H");
7967 		else if (!strcmp(prjcode, "eck4"))   strcat (opt_J, "Kf");
7968 		else strcat (opt_J, "Ks");
7969 		while (gmt_strtok (szProj4, " \t+", &pos, token)) {
7970 			if ((pch = strstr(token, "lon_0=")) != NULL) {
7971 				strncat(opt_J, &token[6], GMT_LEN256-1);	strcat (opt_J, "/");
7972 				//gmtplot_wipe_substr(szProj4, token);
7973 			}
7974 			else if ((pch = strstr(token, "lun_0=")) != NULL) {
7975 				strncat(opt_J, &token[6], GMT_LEN256-1);	strcat (opt_J, "/");
7976 			}
7977 		}
7978 		//if (opt_J[strlen(opt_J)-1] != '/')		/* Not strictly needed by GMT but needed in gmt_parse_common_options() */
7979 			//strcat(opt_J, "0/");
7980 	}
7981 
7982 	else	/* We don't return yet because we may have a +width/+scale to parse */
7983 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "This projection '%s' is not natively supported in GMT\n", prjcode);
7984 
7985 #if 0
7986 	bool got_a = false, got_b = false;
7987 	char ename[GMT_LEN16] = {""};
7988 	/* Work on common flags */
7989 	if ((pch = strstr(szProj4, "+a=")) != NULL) {	/* Check for major axis +a=xxxx */
7990 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
7991 		GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius = atof(&token[2]);
7992 		got_a = true;
7993 		gmtplot_wipe_substr(szProj4, token);	/* Set the token part in szProj4 to blanks */
7994 	}
7995 	if ((pch = strstr(szProj4, "+b=")) != NULL) {	/* Check for minor axis +b=xxxx */
7996 		double f;
7997 		if (!got_a) {
7998 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "+b= in proj4 string without the companion +a\n");
7999 			return (pStrOut);
8000 		}
8001 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8002 		/* f =  1 - b / a; */
8003 		f =  1 - atof(&token[2]) / GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius;
8004 		GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening = f;
8005 		got_b = true;
8006 		gmtplot_wipe_substr(szProj4, token);
8007 	}
8008 	if ((pch = strstr(szProj4, "+k_0=")) != NULL) {		/* Check for scale factor */
8009 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8010 		GMT->current.setting.proj_scale_factor = atof (&token[4]);
8011 		gmtplot_wipe_substr(szProj4, token);
8012 	}
8013 	if ((pch = strstr(szProj4, "+k=")) != NULL) {		/* Check for scale factor */
8014 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8015 		GMT->current.setting.proj_scale_factor = atof (&token[2]);
8016 		gmtplot_wipe_substr(szProj4, token);
8017 	}
8018 
8019 	if ((pch = strstr(szProj4, "+ellps=")) != NULL) {	/* Check for ellipsoids */
8020 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8021 		gmtplot_ellipsoid_name_convert2(&token[6], ename);
8022 		if (ename[0] != '\0')
8023 			;//sprintf(GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].name, "%s", ename);
8024 		else {
8025 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "could not translate the ellipsoid name %s\n", &token[6]);
8026 			return (pStrOut);
8027 		}
8028 		gmtplot_wipe_substr(szProj4, token);
8029 	}
8030 
8031 	if ((pch = strstr(szProj4, "+datum=")) != NULL) {	/* Check for datum */
8032 		char t[128] = {""};
8033 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8034 
8035 		if (!strcmp(&token[6], "WGS84"))
8036 			gmtplot_ellipsoid_name_convert2("WGS84", ename);
8037 		else if (!strcmp(&token[6], "GGRS87")) {
8038 			gmtplot_ellipsoid_name_convert2("GRS80", ename);
8039 			sprintf(t, " +towgs84=-199.87,74.79,246.62");
8040 		}
8041 		else if (!strcmp(&token[6], "NAD83"))
8042 			gmtplot_ellipsoid_name_convert2("WGS84", ename);
8043 		else if (!strcmp(&token[6], "potsdam")) {
8044 			gmtplot_ellipsoid_name_convert2("bessel", ename);
8045 			sprintf(t, " +towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7");
8046 		}
8047 		else {
8048 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "this datum %s is not yet ported to GMT\n", &token[6]);
8049 			return (pStrOut);
8050 		}
8051 
8052 		if (ename[0] != '\0')
8053 			sprintf(GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].name, "%s", ename);
8054 		else {
8055 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "could not translate the ellipsoid name %s\n", &token[6]);
8056 			return (pStrOut);
8057 		}
8058 		strcat(szProj4, t);			/* Append to the proj4 string so that the +towgs84 case below will handle this */
8059 		gmtplot_wipe_substr(szProj4, token);
8060 	}
8061 
8062 	if ((pch = strstr(szProj4, "+towgs84=")) != NULL) {
8063 		char *txt, t[128] = {""};
8064 		struct GMT_DATUM to, from;
8065 
8066 		memset(&to, 0, sizeof(struct GMT_DATUM));
8067 		memset(&from, 0, sizeof(struct GMT_DATUM));
8068 
8069 		if (ename[0] == '\0' && (!got_a || !got_b)) {
8070 			GMT_Report (GMT->parent, GMT_MSG_ERROR,
8071 							" Cannot convert to WGS84 if you don't tell me the ellipsoid of origin\n"
8072 						 "(miss +ellips=xxxx OR +a=xxxx +b=xxxx)\n");
8073 			return (pStrOut);
8074 		}
8075 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8076 		txt = strdup (&token[8]);
8077 		if (ename[0] != '\0')                   /* We know the ellipsoid name */
8078 			sprintf(t, "%s:%s", ename, txt);    /* Create an ellip:dx,dy,dz string */
8079 		else                                    /* Need to pass a,1/f */
8080 			sprintf(t, "%12g,%.10f:%s",
8081 					  GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius,
8082 					1 / GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening,
8083 					txt);
8084 
8085 		// Need to create and fill 2 GMT_DATUM structs (from & to). See gmt_set_datum() & gmt_datum_init()
8086 		gmt_set_datum (GMT, "-", &from);		/* 'to' is WG84 */
8087 		gmt_set_datum (GMT, t, &to);
8088 		gmt_datum_init(GMT, &from, &to, false);
8089 
8090 		for (k = 0; k < 7; k++)
8091 			GMT->current.proj.datum.bursa[k] = GMT->current.proj.datum.to.xyz[k];
8092 
8093 		free(txt);
8094 		gmtplot_wipe_substr(szProj4, token);
8095 	}
8096 
8097 	if ((pch = strstr(szProj4, "+x_0=")) != NULL) {
8098 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8099 		GMT->current.proj.proj4_x0 = atof(&token[4]);
8100 		gmtplot_wipe_substr(szProj4, token);
8101 	}
8102 	if ((pch = strstr(szProj4, "+y_0=")) != NULL) {
8103 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8104 		GMT->current.proj.proj4_y0 = atof(&token[4]);
8105 		gmtplot_wipe_substr(szProj4, token);
8106 	}
8107 
8108 	if ((pch = strstr(szProj4, "+units=")) != NULL) {
8109 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8110 		if (token[6] != 'm') {
8111 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "using units other than meters is not yet implemented\n");
8112 		}
8113 		gmtplot_wipe_substr(szProj4, token);
8114 	}
8115 
8116 	/* Remove known unused fields in proj4 string */
8117 	if ((pch = strstr(szProj4, "+no_defs")) != NULL) {
8118 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8119 		gmtplot_wipe_substr(szProj4, token);
8120 	}
8121 	if ((pch = strstr(szProj4, "+wktext")) != NULL) {	/* +wktext is used by GDAL to capture the proj.4 string of unsupported projs */
8122 		pos = 0;	gmt_strtok (pch, " \t+", &pos, token);
8123 		gmtplot_wipe_substr(szProj4, token);
8124 	}
8125 #endif
8126 
8127 	/* Override the /1:xxx scale set at the beginning of this function if a +scale=scale is found */
8128 	if ((pch = strstr(szProj4, "+scale=")) != NULL) {
8129 		pos = 0;
8130 		if (gmt_strtok (pch, " \t+", &pos, token)) sprintf(scale_c, "%s", &token[6]);
8131 		//gmtplot_wipe_substr(szProj4, token);
8132 		GMT->current.proj.gave_map_width = 0;
8133 		GMT->current.proj.proj4_scl = gmt_M_to_inch (GMT, scale_c);
8134 	}
8135 	/* If a +width=xx is given, append it a 'W' so that we identify this in gmt_parse_common_options() and act */
8136 	if ((pch = strstr(szProj4, "+width=")) != NULL) {
8137 		pos = 0;
8138 		if (gmt_strtok (pch, " \t+", &pos, token)) sprintf(scale_c, "%sW", &token[6]);
8139 		//gmtplot_wipe_substr(szProj4, token);
8140 		GMT->current.proj.gave_map_width = 1;
8141 		GMT->current.proj.proj4_scl = gmt_M_to_inch (GMT, &token[6]);
8142 	}
8143 
8144 	*scale_pos = (int)strlen(opt_J);		/* The position at which the scale string will be appended */
8145 
8146 	if (!opt_J[0])		/* No corresponding GMT proj found but we need the scale separated with a slash */
8147 		sprintf(opt_J, "/%s", scale_c);
8148 	else
8149 		strcat(opt_J, scale_c);	/* Append the scale */
8150 
8151 	if (strchr(scale_c, ':')) {	/* If we have a scale in the 1:xxxx form use lower case codes */
8152 		opt_J[0] = (char)tolower(opt_J[0]);
8153 		if (!strcmp(prjcode, "omerc")) opt_J[1] = (char)tolower(opt_J[1]);
8154 	}
8155 
8156 #if 0
8157 	/* This part is now not particularly useful but let it in case we may need it in future */
8158 	k = 0;
8159 	while (szProj4[k] && (szProj4[k] == ' ' || szProj4[k] == '+')) k++;
8160 	if (k < strlen(szProj4))
8161 		GMT_Report (GMT->parent, GMT_MSG_WARNING, "Original proj4 string was not all consumend. Remaining options:\n\t%s\n", szProj4);
8162 #endif
8163 
8164 	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Converted to -J syntax = -J%s\n", opt_J);
8165 	GMT->current.proj.is_proj4 = true;		/* Used so far in map|grdproject to set local -C */
8166 
8167 	pStrOut = strdup(opt_J);
8168 	return pStrOut;
8169 }
8170 #endif
8171 
gmt_export2proj4(struct GMT_CTRL * GMT)8172 char *gmt_export2proj4 (struct GMT_CTRL *GMT) {
8173 	char *pStrOut = NULL;
8174 	char szProj4[GMT_LEN512], proj4_ename[GMT_LEN16];
8175 	double scale_factor, false_easting = 0.0, false_northing = 0.0, a, b, f;
8176 
8177 	/* If we already have a proj.4 string, obviously use it and return right away */
8178 	if (GMT->common.J.proj4string[0] == '+') {
8179 		pStrOut = strdup (GMT->common.J.proj4string);
8180 		return pStrOut;
8181 	}
8182 
8183 	scale_factor = GMT->current.setting.proj_scale_factor;
8184 	if (scale_factor < 0) scale_factor = 1;
8185 	szProj4[0] = 0;
8186 
8187 	switch (GMT->current.proj.projection_GMT) {
8188 	/* Cylindrical projections */
8189 	case GMT_UTM:
8190 		snprintf (szProj4, GMT_LEN512, "+proj=utm +zone=%d +units=m", (int)GMT->current.proj.pars[0]);
8191 		if (GMT->current.proj.utm_hemisphere < 0) strcat (szProj4, " +south");
8192 		break;
8193 	case GMT_MERCATOR:
8194 		snprintf (szProj4, GMT_LEN512, "+proj=merc +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8195 			GMT->current.proj.pars[0] >= -360 ? GMT->current.proj.pars[0] : 0, scale_factor, false_easting, false_northing);
8196 		break;
8197 	case GMT_CYL_EQ:
8198 		snprintf (szProj4, GMT_LEN512, "+proj=cea +lon_0=%.16g +lat_ts=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8199 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8200 		break;
8201 	case GMT_CYL_EQDIST:
8202 		snprintf (szProj4, GMT_LEN512, "+proj=eqc +lat_ts=%.16g +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8203 			GMT->current.proj.pars[1], 0.0, GMT->current.proj.pars[0], false_easting, false_northing);
8204 		break;
8205 	case GMT_CYL_STEREO:
8206 		break;
8207 	case GMT_MILLER:
8208 		snprintf (szProj4, GMT_LEN512, "+proj=mill +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +R_A +units=m",
8209 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8210 		break;
8211 	case GMT_TM:
8212 		snprintf (szProj4, GMT_LEN512, "+proj=tmerc +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8213 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], scale_factor, false_easting, false_northing);
8214 		break;
8215 	case GMT_CASSINI:
8216 		snprintf (szProj4, GMT_LEN512, "+proj=cass +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8217 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8218 		break;
8219 	case GMT_OBLIQUE_MERC:
8220 		snprintf (szProj4, GMT_LEN512, "+unavailable");
8221 		/*snprintf (szProj4, GMT_LEN512, "+proj=omerc +lat_0=%.16g +lonc=%.16g +alpha=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g",
8222 		0.0,0.0,0.0,0.0,0.0,0.0 );*/
8223 		break;
8224 	case GMT_OBLIQUE_MERC_POLE:
8225 		sprintf (szProj4, "+unavailable");
8226 		break;
8227 
8228 	/* Conic projections */
8229 	case GMT_ALBERS:
8230 		snprintf (szProj4, GMT_LEN512, "+proj=aea +lat_1=%.16g +lat_2=%.16g +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8231 			GMT->current.proj.pars[2], GMT->current.proj.pars[3], GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8232 		break;
8233 	case GMT_ECONIC:
8234 		snprintf (szProj4, GMT_LEN512, "+proj=eqdc +lat_1=%.16g +lat_2=%.16g +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8235 			GMT->current.proj.pars[2], GMT->current.proj.pars[3], GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8236 		break;
8237 	case GMT_LAMBERT:
8238 		snprintf (szProj4, GMT_LEN512, "+proj=lcc +lat_1=%.16g +lat_2=%.16g +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8239 			GMT->current.proj.pars[2], GMT->current.proj.pars[3], GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8240 		break;
8241 	case GMT_POLYCONIC:
8242 		snprintf (szProj4, GMT_LEN512, "+proj=poly +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8243 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8244 		break;
8245 
8246 	/* Azimuthal projections */
8247 	case GMT_STEREO:
8248 		snprintf (szProj4, GMT_LEN512, "+proj=stere +lat_0=%.16g +lon_0=%.16g +k=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8249 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], scale_factor, false_easting, false_northing);
8250 		break;
8251 	case GMT_LAMB_AZ_EQ:
8252 		snprintf (szProj4, GMT_LEN512, "+proj=laea +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8253 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8254 		break;
8255 	case GMT_ORTHO:
8256 		sprintf (szProj4, "+unavailable");
8257 		break;
8258 	case GMT_AZ_EQDIST:
8259 		snprintf (szProj4, GMT_LEN512, "+proj=aeqd +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8260 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8261 		break;
8262 	case GMT_GNOMONIC:
8263 		snprintf (szProj4, GMT_LEN512, "+proj=gnom +lat_0=%.16g +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8264 			GMT->current.proj.pars[1], GMT->current.proj.pars[0], false_easting, false_northing);
8265 		break;
8266 	case GMT_GENPER:
8267 		sprintf (szProj4, "+unavailable");
8268 		break;
8269 	case GMT_POLAR:
8270 		sprintf (szProj4, "+unavailable");
8271 		break;
8272 
8273 	/* Misc projections */
8274 	case GMT_MOLLWEIDE:
8275 		snprintf (szProj4, GMT_LEN512, "+proj=moll +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8276 			GMT->current.proj.pars[0], false_easting, false_northing);
8277 		break;
8278 	case GMT_HAMMER:
8279 		sprintf (szProj4, "+unavailable");
8280 		break;
8281 	case GMT_SINUSOIDAL:
8282 		snprintf (szProj4, GMT_LEN512, "+proj=sinu +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8283 			GMT->current.proj.pars[0], false_easting, false_northing);
8284 		break;
8285 	case GMT_VANGRINTEN:
8286 		snprintf (szProj4, GMT_LEN512, "+proj=vandg +lon_0=%.16g +x_0=%.16g +y_0=%.16g +R_A +units=m",
8287 			GMT->current.proj.pars[0], false_easting, false_northing);
8288 		break;
8289 	case GMT_ROBINSON:
8290 		snprintf (szProj4, GMT_LEN512, "+proj=robin +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8291 			GMT->current.proj.pars[0], false_easting, false_northing);
8292 		break;
8293 	case GMT_ECKERT4:
8294 		snprintf (szProj4, GMT_LEN512, "+proj=eck4 +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8295 			GMT->current.proj.pars[0], false_easting, false_northing);
8296 		break;
8297 	case GMT_ECKERT6:
8298 		snprintf (szProj4, GMT_LEN512, "+proj=eck6 +lon_0=%.16g +x_0=%.16g +y_0=%.16g +units=m",
8299 			GMT->current.proj.pars[0], false_easting, false_northing);
8300 		break;
8301 	case GMT_WINKEL:
8302 		 printf (szProj4, "+unavailable");
8303 		break;
8304 	default:
8305 		if (gmt_M_is_geographic (GMT, GMT_IN))
8306 			sprintf (szProj4, "+proj=latlong");
8307 		else
8308 			sprintf (szProj4, "+xy");	/* Probably useless as an info, but put there something */
8309 	}
8310 
8311 	if (strcmp(szProj4, "+xy")) {
8312 		size_t len = strlen (szProj4);
8313 		a = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius;
8314 		f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
8315 		b = a * (1 - f);
8316 		snprintf (szProj4+len, GMT_LEN512-len, " +a=%.3f +b=%.3f", a, b);
8317 		len = strlen (szProj4);
8318 		if (fabs(a - b) > 1) {		/* WGS84 is not spherical */
8319 			gmtlib_ellipsoid_name_convert (GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].name, proj4_ename);
8320 			snprintf(szProj4+len, GMT_LEN512-len, " +ellps=%s", proj4_ename);
8321 			len = strlen (szProj4);
8322 			if (!strcmp(proj4_ename, "WGS84"))
8323 				snprintf(szProj4+strlen(szProj4), GMT_LEN512-len, " +datum=WGS84");
8324 		}
8325 		len = strlen (szProj4);
8326 		snprintf(szProj4+len, GMT_LEN512-len, " +units=m +no_defs");
8327 	}
8328 
8329 	pStrOut = strdup(szProj4);
8330 	return (pStrOut);
8331 }
8332 
gmtplot_just_f_xy(int justify,double * fx,double * fy)8333 GMT_LOCAL void gmtplot_just_f_xy (int justify, double *fx, double *fy) {
8334 	/* Factors to multiply feature width/height and add to x,y that updates its mid-point location */
8335 	*fy = -0.5 * ((justify / 4) - 1);
8336 	*fx = -0.5 * ((justify % 4) - 2);
8337 }
8338 
8339 /* Here are 6 (a-f) different progress indicator plotting functions.  See the movie documentation for how they look */
8340 
gmtplot_prog_indicator_A(struct GMT_CTRL * GMT,double x,double y,double t,double w,int justify,char * F1,char * F2)8341 GMT_LOCAL void gmtplot_prog_indicator_A (struct GMT_CTRL *GMT, double x, double y, double t, double w, int justify, char *F1, char *F2) {
8342 	/* Place default progress indicator pie */
8343 	double dim[PSL_MAX_DIMS], fx, fy;
8344 	struct GMT_FILL fill;
8345 	gmt_M_memset (dim, PSL_MAX_DIMS, double);
8346 	gmtplot_just_f_xy (justify, &fx, &fy);
8347 	x += fx * w;	y += fy * w;	/* Move to center of circle */
8348 	dim[0] = w;
8349 	if (t < 1.0) {	/* Need the background full circle since partly visible */
8350 		gmt_getfill (GMT, F2, &fill);	/* Want to paint background full circle*/
8351 		PSL_setfill (GMT->PSL, fill.rgb, 0);	/* Full circle color */
8352 		PSL_plotsymbol (GMT->PSL, x, y, dim, PSL_CIRCLE);	/* Plot full circle */
8353 	}
8354 	gmt_getfill (GMT, F1, &fill);	/* Want to paint inside of tag box */
8355 	PSL_setfill (GMT->PSL, fill.rgb, 0);	/* Wedge color */
8356 	if (doubleAlmostEqual (t, 1.0)) {
8357 		PSL_plotsymbol (GMT->PSL, x, y, dim, PSL_CIRCLE);	/* Plot full circle */
8358 	}
8359 	else {
8360 		dim[0] = 0.5 * w;	/* Apparently we take radius for wedge */
8361 		dim[1] = 90.0 - 360 * t;	/* Go clockwise. Convert t to 0-360 degrees */
8362 		dim[2] = 90.0;	/* Start is 12 'oclock */
8363 		dim[7] = 1;	/* Lay down filled wedge */
8364 		PSL_command (GMT->PSL, "/PSL_spiderpen {} def\n");	/* So wedge won't fuss about not being set (like in psxy) */
8365 		PSL_plotsymbol (GMT->PSL, x, y, dim, PSL_WEDGE);	/* Plot wedge */
8366 	}
8367 }
8368 
gmtplot_prog_indicator_B(struct GMT_CTRL * GMT,double x,double y,double t,double w,int justify,char * P1,char * P2,char * label,char kind,double fsize)8369 GMT_LOCAL void gmtplot_prog_indicator_B (struct GMT_CTRL *GMT, double x, double y, double t, double w, int justify, char *P1, char *P2, char *label, char kind, double fsize) {
8370 	/* Place growing ring */
8371 	double fx, fy, dr2;
8372 	struct GMT_PEN pen;
8373 
8374 	gmtplot_just_f_xy (justify, &fx, &fy);
8375 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8376 	gmt_getpen (GMT, P2, &pen);	/* Want to draw full circle */
8377 	dr2 = pen.width / PSL_POINTS_PER_INCH;	/* Half pen width */
8378 	x += fx * (w+dr2);	y += fy * (w+dr2);	/* Move to center of circle */
8379 	PSL_command (GMT->PSL, "FQ %% Force turn off any prior fill\n");
8380 	if (kind == 'B') {
8381 		if (fsize == 0.0) fsize = 0.3 * w * PSL_POINTS_PER_INCH;
8382 		PSL_plottext (GMT->PSL, x, y, fsize, label, 0.0, PSL_MC, 0);
8383 	}
8384 	if (t < 1.0) {	/* Need to plot the background full circle since partly visible */
8385 		gmt_setpen (GMT, &pen);	/* Full circle pen */
8386 		PSL_setfill (GMT->PSL, GMT->session.no_rgb, 1);
8387 		PSL_plotsymbol (GMT->PSL, x, y, &w, PSL_CIRCLE);	/* Plot full circle */
8388 	}
8389 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8390 	gmt_getpen (GMT, P1, &pen);	/* Always draw foreground circle */
8391 	gmt_setpen (GMT, &pen);	/* Full circle pen */
8392 	PSL_setfill (GMT->PSL, GMT->session.no_rgb, 1);
8393 	if (doubleAlmostEqual (t, 1.0))
8394 		PSL_plotsymbol (GMT->PSL, x, y, &w, PSL_CIRCLE);	/* Plot full circle */
8395 	else
8396 		PSL_plotarc (GMT->PSL, x, y, 0.5*w, 90.0 - 360 * t, 90.0, PSL_MOVE | PSL_STROKE);	/* Draw the arc */
8397 }
8398 
gmtplot_prog_indicator_C(struct GMT_CTRL * GMT,double x,double y,double t,double w,int justify,char * P1,char * P2,char * label,char kind,double fsize)8399 GMT_LOCAL void gmtplot_prog_indicator_C (struct GMT_CTRL *GMT, double x, double y, double t, double w, int justify, char *P1, char *P2, char *label, char kind, double fsize) {
8400 	/* Place growing math arrow */
8401 	double fx, fy, dim[PSL_MAX_DIMS];
8402 	struct GMT_PEN pen;
8403 
8404 	gmt_M_memset (dim, PSL_MAX_DIMS, double);
8405 	gmtplot_just_f_xy (justify, &fx, &fy);
8406 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8407 	gmt_getpen (GMT, P2, &pen);	/* Want to draw full circle */
8408 	x += fx * 1.15 * w;	y += fy * 1.15 * w;	/* Move to center of circle, add 15% to get more space so arrow head won't clip at canvas edge */
8409 	PSL_command (GMT->PSL, "FQ %% Force turn off any prior fill\n");
8410 	if (kind == 'C') {	/* Place center label */
8411 		if (fsize == 0.0) fsize = 0.3 * w * PSL_POINTS_PER_INCH;
8412 		PSL_plottext (GMT->PSL, x, y, fsize, label, 0.0, PSL_MC, 0);
8413 	}
8414 	if (t < 1.0) {	/* Need to plot the background full circle */
8415 		gmt_setpen (GMT, &pen);	/* Full circle pen */
8416 		PSL_setfill (GMT->PSL, GMT->session.no_rgb, 1);
8417 		PSL_plotsymbol (GMT->PSL, x, y, &w, PSL_CIRCLE);	/* Plot full circle */
8418 	}
8419 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8420 	gmt_getpen (GMT, P1, &pen);	/* Always draw foreground circle */
8421 	gmt_setpen (GMT, &pen);	/* Front circle pen */
8422 	PSL_setfill (GMT->PSL, pen.rgb, 1);
8423 	dim[PSL_MATHARC_RADIUS]          = 0.5 * w;	/* Apparently we take radius for wedge */
8424 	dim[PSL_MATHARC_ANGLE_BEGIN]     = 90.0 - 360 * t;	/* Go clockwise. Convert t to 0-360 degrees */
8425 	dim[PSL_MATHARC_ANGLE_END]       = 90.0;	/* Start is 12 'oclock */
8426 	dim[PSL_MATHARC_HEAD_LENGTH]     = 0.2 * w;
8427 	dim[PSL_MATHARC_HEAD_WIDTH]      = 0.2 * w;
8428 	dim[PSL_MATHARC_ARC_PENWIDTH]    = pen.width / PSL_POINTS_PER_INCH;
8429 	dim[PSL_MATHARC_HEAD_SHAPE]      = 0.75;	/* Fixed shape setting */
8430 	dim[PSL_MATHARC_STATUS]          = (double)(PSL_VEC_BEGIN | PSL_VEC_FILL);
8431 	dim[PSL_MATHARC_HEAD_TYPE_BEGIN] = (double)PSL_VEC_ARROW;
8432 	dim[PSL_MATHARC_HEAD_TYPE_END]   = (double)PSL_VEC_ARROW;
8433 	dim[PSL_MATHARC_HEAD_PENWIDTH]   = 0.5 * pen.width;
8434 	PSL_command (GMT->PSL, "/PSL_vecheadpen {} def\n");	/* So wedge won't fuss about not being set (like in psxy) */
8435 	PSL_plotsymbol (GMT->PSL, x, y, dim, PSL_MARC);
8436 }
8437 
gmtplot_prog_indicator_D(struct GMT_CTRL * GMT,double x,double y,double t,double w,int justify,char * P1,char * P2,char * label,char kind,double fsize)8438 GMT_LOCAL void gmtplot_prog_indicator_D (struct GMT_CTRL *GMT, double x, double y, double t, double w, int justify, char *P1, char *P2, char *label, char kind, double fsize) {
8439 	/* Place rounded line indicator with a single crossmark line and optional label.
8440 	 * If label is in percent then we strip off the percent and place it at end of line insteadl */
8441 	int text_justify, just_p;
8442 	char *p = NULL;
8443 	double fx, fy, dy, dy2, xt, del_x = 0.0, del_y = 0.0, angle = 0.0, xp;
8444 	struct GMT_PEN pen;
8445 
8446 	gmtplot_just_f_xy (justify, &fx, &fy);
8447 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8448 	gmt_getpen (GMT, P2, &pen);	/* Pen for the background fixed line */
8449 	dy = pen.width / PSL_POINTS_PER_INCH;	/* Pen width in inches */
8450 	dy2 = dy / 2.0;	/* Half pen-width */
8451 	x += fx * (w + dy);	y += fy * dy;	/* Adjust center (x,y) depending on justification */
8452 	xt = w * (t - 0.5);	/* Location of label and tick along x-axis */
8453 	w /= 2;	/* From here on, w is half-length of fixed line */
8454 	if (kind == 'D') {	/* Want a label at the current crossmark */
8455 		if (fsize == 0.0) fsize = 2.0 * dy * PSL_POINTS_PER_INCH;	/* Set a scaled font size */
8456 		if ((p = strchr (label, '%'))) p[0] = '\0';	/* Remove % here and add separately later */
8457 		switch (justify) {	/* Deal with justification of text and possibly rotation */
8458 			case PSL_BL: case PSL_BC: case PSL_BR:
8459 				text_justify = PSL_BC; del_y = dy; break;
8460 			case PSL_TL: case PSL_TC: case PSL_TR: case PSL_MC:
8461 				text_justify = PSL_TC; del_y = -dy; break;
8462 			case PSL_ML: text_justify = PSL_ML; angle = 90.0; x -= w; del_y = -dy; break;
8463 			case PSL_MR: text_justify = PSL_MR; angle = 90.0; x += w; del_y = +dy; break;
8464 			default:
8465 				text_justify = PSL_BC; del_y = dy; break;
8466 		}
8467 		if (p) {	/* Got percentages; must plot it separately */
8468 			if (justify == PSL_ML || justify == PSL_MR)
8469 				just_p = PSL_BC, xp = w + dy;
8470 			else {
8471 				just_p = (justify % 4 == 3) ? PSL_MR : PSL_ML;
8472 				xp = (just_p == PSL_MR) ? -dy-w : w+dy;
8473 			}
8474 		}
8475 	}
8476 	PSL_setorigin (GMT->PSL, x, y, angle, PSL_FWD);	/* Origin is now mid-point of rounded line */
8477 	if (kind == 'D') {
8478 		PSL_plottext (GMT->PSL, xt+del_x, del_y, fsize, label, -angle, text_justify, 0);
8479 		if (p) {
8480 			PSL_plottext (GMT->PSL, xp, 0.0, fsize, "%", -angle, just_p, 0);
8481 			p[0] = '%';	/* Restore it */
8482 		}
8483 	}
8484 	PSL_command (GMT->PSL, "V\n");	/* Do a save/restore around the line cap change */
8485 	gmt_setpen (GMT, &pen);
8486 	PSL_setlinecap (GMT->PSL, PSL_ROUND_CAP);	/* Want the rounded line effect */
8487 	PSL_plotsegment (GMT->PSL, -w, 0.0, +w, 0.0);	/* Draw thick rounded line */
8488 	PSL_command (GMT->PSL, "U\n");	/* grestore to restore to whatever line cap was set to before */
8489 	gmt_M_memset (&pen, 1, struct GMT_PEN);	/* Wipe pen */
8490 	gmt_getpen (GMT, P1, &pen);	/* Get pen for crossmark */
8491 	gmt_setpen (GMT, &pen);
8492 	PSL_plotsegment (GMT->PSL, xt, -dy2, xt, dy2);	/* Draw foreground crossmark at label time */
8493 	PSL_setorigin (GMT->PSL, -x, -y, -angle, PSL_INV);	/* Undo coordinate transformation */
8494 	PSL_setlinecap (GMT->PSL, PSL_BUTT_CAP);	/* Want the rounded line effect */
8495 }
8496 
gmtplot_prog_indicator_E(struct GMT_CTRL * GMT,double x,double y,double t,double w,int justify,char * P1,char * P2,char * label,char kind,double fsize)8497 GMT_LOCAL void gmtplot_prog_indicator_E (struct GMT_CTRL *GMT, double x, double y, double t, double w, int justify, char *P1, char *P2, char *label, char kind, double fsize) {
8498 	/* Place solid line indicator with optional start/end labels */
8499 	int text_justify, just_p;
8500 	double fx, fy, dy, dy2, xt, xp, del_x = 0.0, del_y = 0.0, angle = 0.0;
8501 	struct GMT_PEN pen;
8502 
8503 	gmtplot_just_f_xy (justify, &fx, &fy);
8504 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8505 	gmt_getpen (GMT, P2, &pen);	/* Pen for the background fixed line */
8506 	dy = pen.width / PSL_POINTS_PER_INCH;	/* Pen width in inches */
8507 	dy2 = dy / 2.0;	/* Half pen-width */
8508 	x += fx * (w + dy2);	y += fy * dy;	/* Adjust center (x,y) depending on justification */
8509 	xt = w * (t - 0.5);	/* Point of current time on axis */
8510 	w /= 2;	/* From here on, w is half-length of fixed line length */
8511 	if (kind == 'E') {	/* Want a label at the current crossmark */
8512 		if (fsize == 0.0) fsize = 2.0 * dy * PSL_POINTS_PER_INCH;	/* Set a scaled font size */
8513 		switch (justify) {	/* Deal with justification of text and possibly rotation */
8514 			case PSL_BL: case PSL_BC: case PSL_BR:
8515 				text_justify = PSL_BC; del_y = dy; break;
8516 			case PSL_TL: case PSL_TC: case PSL_TR: case PSL_MC:
8517 				text_justify = PSL_TC; del_y = -dy; break;
8518 			case PSL_ML: text_justify = PSL_ML; angle = 90.0; x -= w; del_y = -dy; break;
8519 			case PSL_MR: text_justify = PSL_MR; angle = 90.0; x += w; del_y = +dy; break;
8520 			default:
8521 				text_justify = PSL_BC; del_y = dy; break;
8522 		}
8523 	}
8524 	PSL_setorigin (GMT->PSL, x, y, angle, PSL_FWD);	/* Origin is now mid-point of rounded line and we have possibly rotated 90 degrees */
8525 	if (kind == 'E') {					/* Place the two labels */
8526 		char *c = strchr (label, ';'), *p = NULL;
8527 		if (c) c[0] = '\0';				/* Chop of label at t == 1 and place the first at t == 0 */
8528 		if ((p = strchr (label, '%')))	/* Watch for percentage sign; if found place 0 and add % to axis label */
8529 			PSL_plottext (GMT->PSL, del_x-w, del_y, fsize, "0", -angle, text_justify, 0);
8530 		else
8531 			PSL_plottext (GMT->PSL, del_x-w, del_y, fsize, label, -angle, text_justify, 0);
8532 		if ((p = strchr (&c[1], '%')))	/* Watch for percentage sign; if found place 100 and add % to axis label */
8533 			PSL_plottext (GMT->PSL, del_x+w, del_y, fsize, "100", -angle, text_justify, 0);
8534 		else
8535 			PSL_plottext (GMT->PSL, del_x+w, del_y, fsize, &c[1], -angle, text_justify, 0);
8536 		if (p) {	/* Since we found percentage we add it as an axis label */
8537 			if (justify == PSL_ML || justify == PSL_MR)
8538 				just_p = PSL_BC, xp = w + dy;
8539 			else {
8540 				just_p = (justify % 4 == 3) ? PSL_MR : PSL_ML;
8541 				xp = (just_p == PSL_MR) ? -dy-w : w+dy;
8542 			}
8543 			PSL_plottext (GMT->PSL, xp, 0.0, fsize, "%", -angle, just_p, 0);
8544 		}
8545 		c[0] = ';';	/* Restore the semi-colon separator */
8546 	}
8547 	gmt_setpen (GMT, &pen);
8548 	PSL_plotsegment (GMT->PSL, -w, 0.0, +w, 0.0);	/* Draw thick rounded line */
8549 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8550 	gmt_getpen (GMT, P1, &pen);	/* Pen for time-variable line */
8551 	gmt_setpen (GMT, &pen);
8552 	PSL_plotsegment (GMT->PSL, w, 0.0, xt, 0.0);
8553 	PSL_setorigin (GMT->PSL, -x, -y, -angle, PSL_INV);	/* Undo coordinate transformation */
8554 }
8555 
gmtplot_prog_indicator_F(struct GMT_CTRL * GMT,double x,double y,double t,double w,int justify,char * P1,char * F1,char * label,char kind,double width,double fsize,struct GMT_FONT * F)8556 GMT_LOCAL void gmtplot_prog_indicator_F (struct GMT_CTRL *GMT, double x, double y, double t, double w, int justify, char *P1, char *F1, char *label, char kind, double width, double fsize, struct GMT_FONT *F) {
8557 	/* Place time axis indicator via call to basemap plus adding triangle here */
8558 	int symbol = PSL_INVTRIANGLE;	/* Time marker when labels are below the line */
8559 	char cmd[GMT_LEN256] = {""}, region[GMT_LEN64] = {""}, unit[4] = {""}, axis = 0;
8560 	bool was = GMT->current.map.frame.init;
8561 	double fx, fy, dy, dy2, xt, s = 1.0, angle = 0.0, h;
8562 	struct GMT_PEN pen;
8563 	struct GMT_FILL fill;
8564 
8565 	gmtplot_just_f_xy (justify, &fx, &fy);
8566 	gmt_M_memset (&pen, 1, struct GMT_PEN);
8567 	gmt_getpen (GMT, P1, &pen);	/* Pen for the time axis */
8568 	dy = pen.width / PSL_POINTS_PER_INCH;	/* Need to get pen width to determine triangle size */
8569 	dy2 = dy / 2.0;	/* Half pen width */
8570 	x += fx * w;	y += fy * dy;	/* Adjust the (x,y) to be mid-point of axis */
8571 	xt = w * t;	/* Relative fraction of w along the axis from left point */
8572 	/* Must allow for the space needed for tick-mark, annot-offset, and possibly annotations */
8573 	h = GMT->current.setting.map_tick_length[GMT_PRIMARY] + MAX (0.0, GMT->current.setting.map_annot_offset[GMT_PRIMARY]);
8574 	if (kind == 'F') h += GMT_LET_HEIGHT * GMT->current.setting.font_annot[GMT_PRIMARY].size / PSL_POINTS_PER_INCH;
8575 	x -= 0.5 * w;	/* Let x be start position of axis rather than middle */
8576 	switch (justify) {	/* Deal with justification and adjust x,y for starting point of axis */
8577 		case PSL_BL: case PSL_BC: case PSL_BR:	/* At bottom we need to move up to give space for annotations below axis */
8578 			axis = 'S'; y += h; break;
8579 		case PSL_TL: case PSL_TC: case PSL_TR: case PSL_MC:	/* At top (and middle) we likewise need space above, plus switch to TRIANGLE */
8580 			axis = 'N'; y-= h; s = -1; symbol = PSL_TRIANGLE; break;
8581 		case PSL_ML: angle = 90.0; y -= 0.5*w; axis = 'N'; x += h; s = -1; symbol = PSL_TRIANGLE; break;	/* Left is like TC but rotated 90 */
8582 		case PSL_MR: angle = 90.0; y -= 0.5*w; axis = 'S'; x += w - h; break;	/* Right is like BC but rotated 90 */
8583 	}
8584 	if (strchr (label, '%')) {	/* Cannot have percentages in the -R string */
8585 		sprintf (region, "-R0/100/0/1");	/* Always 0-100 % */
8586 		strcpy (unit, "+u%");
8587 	}
8588 	else if (label[2] == ' ') { /* Eliminate leading spaces... */
8589 		int k = 2; while (label[k] && label[k] == ' ') k++;	/* Find first non-space character */
8590 		sprintf (region, "-R%s", &label[k]);
8591 	}
8592 	else	/* Use as is */
8593 		strncpy (region, label, GMT_LEN64-1);
8594 	PSL_setorigin (GMT->PSL, x, y, angle, PSL_FWD);	/* Origin (0,0) is now at left end-point of time axis */
8595 		GMT->common.R.active[RSET] = GMT->common.J.active = false;
8596 	if (fsize == 0.0)
8597 			F->size = 3.0 * dy * PSL_POINTS_PER_INCH;	/* Set a scaled font size */
8598 	if (kind == 'F')	/* Want annotations so add -Baf and set annotation font */
8599 		sprintf (cmd, "%s -JX%gi/0.0001i -Baf%s -B%c -X0 -Y0 --MAP_FRAME_PEN=%s --FONT_ANNOT_PRIMARY=+%s --GMT_HISTORY=false", region, width, unit, axis, P1, gmt_putfont (GMT, F));
8600 	else	/* Only axis with ticks so only -Bf is used */
8601 		sprintf (cmd, "%s -JX%gi/0.0001i -Bf%s -B%c -X0 -Y0 --MAP_FRAME_PEN=%s --GMT_HISTORY=false", region, width, unit, axis, P1);
8602 	GMT->current.map.frame.init = false;	/* To enable more -B parsing */
8603 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Call basemap from gmtplot_prog_indicator_F with args %s\n", cmd);
8604 	if (GMT_Call_Module (GMT->parent, "basemap", GMT_MODULE_CMD, cmd) != GMT_NOERROR) {
8605 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failed to call basemap with args %s ???\n", cmd);
8606 	}
8607 	GMT->current.map.frame.init = was;	/* Reset how we found it */
8608 	if (gmt_getfill (GMT, F1, &fill))	/* Get and set color for the triangle; no outline */
8609 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad fill argument %s\n", F1);
8610 	PSL_setfill (GMT->PSL, fill.rgb, 0);
8611 	w = dy * 2;	/* Set triangle size to 2 times the axis width */
8612 	PSL_plotsymbol (GMT->PSL, xt, 1.2*s*dy2, &w, symbol);
8613 }
8614 
gmtplot_reset_PSL(struct GMT_CTRL * GMT,struct PSL_CTRL * PSL)8615 GMT_LOCAL void gmtplot_reset_PSL (struct GMT_CTRL *GMT, struct PSL_CTRL *PSL) {
8616 	/* Because PSL_*_completion procedures are called at the end of the module but crafted at the start, we must forget we used any fonts, fills, pens in creating them,
8617 	 * otherwise the next PSL call requesting a fill or pen will not be properly set since PSL may think it is already the current choice. */
8618 	if (PSL->current.font_no != GMT_NOTSET) PSL->internal.font[PSL->current.font_no].encoded = 0;	/* Forget about font encoding */
8619 	PSL->current.font_no   = GMT_NOTSET;		/* Forget what the current font is */
8620 	PSL->current.linewidth = GMT_NOTSET;		/* Forget we ever set a line width */
8621 	PSL->current.outline   = GMT_NOTSET;		/* Forget we requested polygon outline */
8622 	gmt_M_memcpy (PSL->current.rgb[PSL_IS_FONT],   GMT->session.no_rgb, 3, double);	/* Reset to -1,-1,-1 since text setting must set the color desired */
8623 	gmt_M_memcpy (PSL->current.rgb[PSL_IS_FILL],   GMT->session.no_rgb, 3, double);	/* Reset to -1,-1,-1 since fill setting must set the color desired */
8624 	gmt_M_memcpy (PSL->current.rgb[PSL_IS_STROKE], GMT->session.no_rgb, 3, double);	/* Reset to -1,-1,-1 since stroke setting must set the color desired */
8625 }
8626 
gmt_plotinit(struct GMT_CTRL * GMT,struct GMT_OPTION * options)8627 struct PSL_CTRL *gmt_plotinit (struct GMT_CTRL *GMT, struct GMT_OPTION *options) {
8628 	/* Shuffles parameters and calls PSL_beginplot, issues PS comments regarding the GMT options
8629 	 * and places a time stamp, if selected */
8630 
8631 	int k, id, fno[PSL_MAX_EPS_FONTS], n_fonts, last, form, justify;
8632 	bool O_active = GMT->common.O.active, auto_media = false, do_paint = false;
8633 	unsigned int this_proj, write_to_mem = 0, switch_charset = 0, n_movie_items[2] = {0, 0};
8634 	char *mode[2] = {"w","a"}, *movie_item_arg[2][GMT_LEN32], not_used[GMT_LEN32] = {""};
8635 	static char *ps_mode[2] = {"classic", "modern"}, *F_name[2] = {"label", "prog_indicator"};
8636 	double media_size[2], plot_x, plot_y;
8637 	FILE *fp = NULL;	/* Default which means stdout in PSL */
8638 	struct GMT_FILL fill;
8639 	struct GMT_OPTION *Out = NULL;
8640 	struct PSL_CTRL *PSL = NULL;
8641 	struct GMT_SUBPLOT *P = NULL;
8642 	struct GMT_INSET *I = &(GMT->current.plot.inset);	/* I->active == 1 if an inset */
8643 
8644 	PSL = GMT->PSL;	/* Shorthand */
8645 
8646 	gmt_set_undefined_defaults (GMT, sqrt (GMT->current.map.width * GMT->current.map.height), false);	/* We must change any undefined defaults given max plot dimension */
8647 
8648 	if (GMT->current.map.frame.paint[GMT_Z]) {	/* Must squirrel this away for now since we may call psbasemap during the movie-indicators below */
8649 		do_paint = true;
8650 		gmt_M_memcpy (&fill, &GMT->current.map.frame.fill[GMT_Z], 1U, struct GMT_FILL);
8651 		GMT->current.map.frame.paint[GMT_Z] = false;	/* Turn off for now */
8652 	}
8653 
8654 	PSL->internal.verbose = GMT->current.setting.verbose;		/* Inherit verbosity level from GMT */
8655 	if (gmt_M_compat_check (GMT, 4) && GMT->current.setting.ps_copies > 1) PSL->init.copies = GMT->current.setting.ps_copies;
8656 	PSL_setdefaults (PSL, GMT->current.setting.ps_magnify, GMT->current.setting.ps_page_rgb, GMT->current.setting.ps_encoding.name);
8657 	GMT->current.ps.memory = false;
8658 	if (doubleAlmostEqual (GMT->current.setting.ps_page_size[GMT_X], GMT_PAPER_DIM) && doubleAlmostEqual (GMT->current.setting.ps_page_size[GMT_Y], GMT_PAPER_DIM))
8659 		auto_media = true;
8660 	gmt_M_memcpy (media_size, GMT->current.setting.ps_page_size, 2, double);
8661 
8662 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Running in PS mode %s\n", ps_mode[GMT->current.setting.run_mode]);
8663 	if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Write PS to hidden gmt_#.ps- file.  No -O -K allowed */
8664 		char *verb[2] = {"Create", "Append to"};
8665 		bool wants_PS;
8666 		double paper_margin = GMT_PAPER_MARGIN_AUTO;
8667 
8668 		if (gmtlib_fixed_paper_size (GMT->parent)) {	/* Must honor paper size and regular margin */
8669 			paper_margin = GMT_PAPER_MARGIN_FIXED;
8670 			gmt_M_memcpy (media_size, GMT->current.setting.ps_def_page_size, 2, double);
8671 		}
8672 
8673 		if ((k = gmt_set_psfilename (GMT)) == GMT_NOTSET) {	/* Get hidden file name for PS */
8674 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "No workflow directory\n");
8675 			GMT->parent->error = GMT_ERROR_ON_FOPEN;
8676 			return NULL;
8677 		}
8678 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s hidden PS file %s\n", verb[k], GMT->current.ps.filename);
8679 		if ((fp = PSL_fopen (PSL, GMT->current.ps.filename, mode[k])) == NULL) {	/* Must open inside PSL DLL */
8680 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open %s with mode %s\n", GMT->current.ps.filename, mode[k]);
8681 			GMT->parent->error = GMT_ERROR_ON_FOPEN;
8682 			return NULL;
8683 		}
8684 		O_active = (k) ? true : false;	/* -O is determined by presence or absence of hidden PS file */
8685 		/* Determine paper size */
8686 		wants_PS = gmtlib_fig_is_ps (GMT);	/* True if we have requested a PostScript output format */
8687 		if (wants_PS && !O_active) {	/* Requesting a new PostScript file in modern mode */
8688 			if (auto_media) {	/* Cannot use "auto" if requesting a PostScript file */
8689 				GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "You should specify a paper size when requesting a PostScript file.\n");
8690 				if (GMT->current.setting.proj_length_unit == GMT_INCH) {	/* Use US settings */
8691 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Changing paper size to US Letter, but we cannot know if this is adequate for your plot; better to use PS_MEDIA explicitly.\n");
8692 					media_size[GMT_X] = 612.0; media_size[GMT_Y] = 792.0;
8693 				}
8694 				else {	/* Use SI settings */
8695 					GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Changing paper size to A4, but we cannot know if this is adequate for your plot; better to use PS_MEDIA explicitly.\n");
8696 					media_size[GMT_X] = 595.0; media_size[GMT_Y] = 842.0;
8697 				}
8698 			}
8699 			if ((GMT->current.map.width > GMT->current.map.height) && (((GMT->current.map.width + GMT->current.setting.map_origin[GMT_X]) * 72) > media_size[GMT_X]) && GMT->current.setting.ps_orientation == PSL_PORTRAIT) {
8700 				GMT->current.setting.ps_orientation = PSL_LANDSCAPE;
8701 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Changing to PostScript landscape orientation based on your plot and paper dimensions, but we cannot be 100%% sure.\n");
8702 				GMT_Report (GMT->parent, GMT_MSG_WARNING, "Use PS_MEDIA and/or PS_PAGE_ORIENTATION to specify correct paper dimensions and/or orientation if our guesses are inadequate.\n");
8703 			}
8704 		}
8705 		else if (!O_active) {	/* Not desiring PS output so we can add safety margin of paper_margin inches for initial layer unless PS_MEDIA was set */
8706 			if (!(GMT->common.X.active || GMT->common.Y.active) && auto_media)
8707 				GMT->current.setting.map_origin[GMT_X] = GMT->current.setting.map_origin[GMT_Y] = paper_margin;
8708 		}
8709 		if (!O_active) {	/* See if special movie labeling file exists under modern mode */
8710 			char file[PATH_MAX] = {""}, record[GMT_LEN256] = {""};
8711 			FILE *fpl = NULL;
8712 			for (k = 0; k < 2; k++) {
8713 				snprintf (file, PATH_MAX, "%s/gmt.movie%ss", GMT->parent->gwf_dir, F_name[k]);
8714 				if (!access (file, R_OK) && (fpl = fopen (file, "r"))) {	/* File exists and could be opened for reading */
8715 					while (fgets (record, GMT_LEN256, fpl)) {
8716 						if (record[0] == '#') continue;	/* Skip header */
8717 						if (n_movie_items[k] == GMT_LEN32) {
8718 							GMT_Report (GMT->parent, GMT_MSG_ERROR, "Number of movie %ss exceed capacity [%d] - skipped.\n", F_name[k], n_movie_items[k]);
8719 							continue;
8720 						}
8721 						gmt_chop (record);		/* Chop off LF or CR/LF */
8722 						GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Found MOVIE_LABEL_ARG%d = %s.\n", n_movie_items[k], record);
8723 						movie_item_arg[k][n_movie_items[k]++] = strdup (record);
8724 					}
8725 					fclose (fpl);
8726 					if (gmt_remove_file (GMT, file)) {	/* Now remove the file since done */
8727 						GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot delete file %s\n", file);
8728 						GMT->parent->error = GMT_ERROR_ON_FOPEN;
8729 						return NULL;
8730 					}
8731 				}
8732 			}
8733 		}
8734 	}
8735 	else if ((Out = GMT_Find_Option (GMT->parent, '>', options))) {	/* Want to use a specific output file */
8736 		k = (Out->arg[0] == '>') ? 1 : 0;	/* Are we appending (k = 1) or starting a new file (k = 0) */
8737 		if (O_active && k == 0 && !gmt_M_file_is_memory (Out->arg)) {
8738 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "-O given but append-mode not selected for file %s\n", &(Out->arg[k]));
8739 		}
8740 		if (gmt_M_file_is_memory (&(Out->arg[k]))) {
8741 			write_to_mem = 2;
8742 			GMT->current.ps.memory = true;
8743 			strncpy (GMT->current.ps.memname, &(Out->arg[k]), GMT_VF_LEN-1);
8744 		}
8745 		else {
8746 			if ((fp = PSL_fopen (PSL, &(Out->arg[k]), mode[k])) == NULL) {	/* Must open inside PSL DLL */
8747 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open %s with mode %s\n", &(Out->arg[k]), mode[k]);
8748 				GMT->parent->error = GMT_ERROR_ON_FOPEN;
8749 				return NULL;
8750 			}
8751 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Opened PS file %s\n", &(Out->arg[k]));
8752 		}
8753 	}
8754 
8755     if (GMT->common.P.active) GMT->current.setting.ps_orientation = PSL_PORTRAIT;
8756 
8757     if (GMT->current.setting.run_mode == GMT_CLASSIC && !O_active) {  /* Warn if plot dimensions are larger than current paper size */
8758 		unsigned int X = (GMT->current.setting.ps_orientation == PSL_LANDSCAPE);
8759 		unsigned int Y = (GMT->current.setting.ps_orientation == PSL_PORTRAIT);
8760 		if ((((GMT->current.map.width + GMT->current.setting.map_origin[GMT_X]) * 72) > GMT->current.setting.ps_def_page_size[X]) ||
8761 			(((GMT->current.map.height + GMT->current.setting.map_origin[GMT_Y]) * 72) > GMT->current.setting.ps_def_page_size[Y])) {
8762 			double OX = GMT->current.setting.map_origin[GMT_X] * GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
8763 			double OY = GMT->current.setting.map_origin[GMT_Y] * GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
8764 			double W = GMT->current.map.width * GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
8765 			double H = GMT->current.map.height * GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
8766 			double PW = GMT->current.setting.ps_def_page_size[X] * GMT->session.u2u[GMT_PT][GMT->current.setting.proj_length_unit];
8767 			double PH = GMT->current.setting.ps_def_page_size[Y] * GMT->session.u2u[GMT_PT][GMT->current.setting.proj_length_unit];
8768 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your plot (WxH = %.2lf x %.2lf %s) placed at (%.2lf, %.2lf %s) may exceed your PS_MEDIA (WxH = %.2lf x %.2lf %s)\n",
8769 				W, H, GMT->session.unit_name[GMT->current.setting.proj_length_unit], OX, OY, GMT->session.unit_name[GMT->current.setting.proj_length_unit], PW, PH, GMT->session.unit_name[GMT->current.setting.proj_length_unit]);
8770 		}
8771 	}
8772 
8773 	/* Initialize the plot header and settings */
8774 
8775 	/* Default for overlay plots is no shifting */
8776 
8777 	if (!GMT->common.X.active && O_active) GMT->current.setting.map_origin[GMT_X] = 0.0;
8778 	if (!GMT->common.Y.active && O_active) GMT->current.setting.map_origin[GMT_Y] = 0.0;
8779 
8780 	/* Adjust offset when centering plot on center of page (PS does the rest) */
8781 
8782 	if (GMT->current.ps.origin[GMT_X] == 'c') GMT->current.setting.map_origin[GMT_X] -= 0.5 * GMT->current.map.width;
8783 	if (GMT->current.ps.origin[GMT_Y] == 'c') GMT->current.setting.map_origin[GMT_Y] -= 0.5 * GMT->current.map.height;
8784 
8785 	/* Get font names used */
8786 
8787 	id = 0;
8788 	if (GMT->common.U.active) fno[id++] = GMT->current.setting.font_logo.id;	/* Add GMT logo font */
8789 	/* Add title font if a title was used */
8790 	if (GMT->current.map.frame.header[0]) fno[id++] = GMT->current.setting.font_title.id;
8791 	/* Add the label font if labels were used */
8792 	if (GMT->current.map.frame.axis[GMT_X].label[0] || GMT->current.map.frame.axis[GMT_Y].label[0] || GMT->current.map.frame.axis[GMT_Z].label[0])
8793 		fno[id++] = GMT->current.setting.font_label.id;
8794 	/* Always add annotation fonts */
8795 	fno[id++] = GMT->current.setting.font_annot[GMT_PRIMARY].id;
8796 	fno[id++] = GMT->current.setting.font_annot[GMT_SECONDARY].id;
8797 
8798 	gmt_sort_array (GMT, fno, id, GMT_INT);
8799 
8800 	last = GMT_NOTSET;
8801 	for (k = n_fonts = 0; k < id; k++) {
8802 		if (fno[k] != last) last = fno[n_fonts++] = fno[k]; /* To avoid duplicates */
8803 	}
8804 	for (k = n_fonts; k < PSL_MAX_EPS_FONTS; k++) fno[k] = GMT_NOTSET;	/* Terminate */
8805 
8806 	P = &(GMT->current.plot.panel);
8807 
8808 	if (P->active) {	/* Subplot panel mode is in effect */
8809 		/* Consider offsets required to center the plot on the subplot panel [0/0] */
8810 		GMT->current.setting.map_origin[GMT_X] += (P->dx + P->gap[XLO]);
8811 		GMT->current.setting.map_origin[GMT_Y] += (P->dy + P->gap[YLO]);
8812 		if (P->first && O_active)	/* Run completion script, if any */
8813 			PSL_setexec (PSL, 1);
8814 	}
8815 	if (I->active && I->first) {
8816 		GMT->current.setting.map_origin[GMT_X] += (I->dx);
8817 		GMT->current.setting.map_origin[GMT_Y] += (I->dy);
8818 		I->active = I->first = false;	/* For MATLAB mostly */
8819 	}
8820 
8821 	if (O_active && GMT->current.ps.switch_set) {	/* User used --PS_CHAR_ENCODING=<encoding> to change it */
8822 		GMT->current.ps.switch_set = false;
8823 		switch_charset = 4;
8824 	}
8825 
8826 	/* Get title */
8827 
8828 	if (GMT->current.setting.run_mode == GMT_MODERN)
8829 		snprintf (GMT->current.ps.title, GMT_LEN256, "GMT v%s Document from %s", GMT_VERSION, gmt_current_name (GMT->init.module_name, not_used));
8830 	else
8831 		snprintf (GMT->current.ps.title, GMT_LEN256, "GMT v%s Document from %s", GMT_VERSION, GMT->init.module_name);
8832 
8833 	PSL_beginplot (PSL, fp, GMT->current.setting.ps_orientation|write_to_mem|switch_charset, O_active, GMT->current.setting.ps_color_mode,
8834 		GMT->current.ps.origin, GMT->current.setting.map_origin, media_size, GMT->current.ps.title, fno);
8835 
8836 	/* Issue the comments that allow us to trace down what command created this layer */
8837 
8838 	gmtplot_echo_command (GMT, PSL, options);
8839 
8840 	if (GMT->common.p.do_z_rotation) {	/* Need a rotation about z of the whole page */
8841 		double x0 = 0.0, y0 = 0.0;	/* Default is to rotate around plot origin */
8842 		if (GMT->current.proj.z_project.view_given) {	/* Rotation is about another z-axis than through the origin */
8843 			x0 = GMT->current.proj.z_project.view_x;
8844 			y0 = GMT->current.proj.z_project.view_y;
8845 		}
8846 		else if (GMT->current.proj.z_project.world_given)	/* Rotation is about another lon/lat pole than the origin */
8847 			gmt_geo_to_xy (GMT, GMT->current.proj.z_project.world_x, GMT->current.proj.z_project.world_y, &x0, &y0);
8848 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Transrot: Rotating plot by %g degrees about (%g, %g)\n", GMT->common.p.z_rotation, x0, y0);
8849 		PSL_comment (GMT->PSL, "Possibly translate then rotate whole page\n");
8850 		PSL_setorigin (PSL, x0, y0, GMT->common.p.z_rotation, PSL_FWD);
8851 		PSL_setorigin (PSL, -x0, -y0, 0.0, PSL_FWD);
8852 	}
8853 
8854 	/* Create %%PROJ tag that psconvert can use to prepare a ESRI world file */
8855 
8856 	this_proj = GMT->current.proj.projection_GMT;
8857 	for (k = 0, id = GMT_NOTSET; id == GMT_NOTSET && k < GMT_N_PROJ4; k++)
8858 		if (GMT->current.proj.proj4[k].id == this_proj) id = k;
8859 	if (id >= 0) {			/* Valid projection for creating world file info */
8860 		double Cartesian_m[4];	/* WESN equivalents in projected meters */
8861 		char *pstr = NULL, proj4name[16] = {""};
8862 		Cartesian_m[0] = (GMT->current.proj.rect[YLO] - GMT->current.proj.origin[GMT_Y]) * GMT->current.proj.i_scale[GMT_Y];
8863 		Cartesian_m[1] = (GMT->current.proj.rect[XHI] - GMT->current.proj.origin[GMT_X]) * GMT->current.proj.i_scale[GMT_X];
8864 		Cartesian_m[2] = (GMT->current.proj.rect[YHI] - GMT->current.proj.origin[GMT_Y]) * GMT->current.proj.i_scale[GMT_Y];
8865 		Cartesian_m[3] = (GMT->current.proj.rect[XLO] - GMT->current.proj.origin[GMT_X]) * GMT->current.proj.i_scale[GMT_X];
8866 		/* It woul be simpler if we had a cleaner way of telling when data is lon-lat */
8867 		if (GMT->current.proj.projection_GMT == GMT_LINEAR && gmt_M_is_geographic (GMT, GMT_IN))
8868 			strcpy (proj4name, "latlong");
8869 		else
8870 			strncpy (proj4name, GMT->current.proj.proj4[id].name, 15U);
8871 
8872 		pstr = gmt_export2proj4 (GMT);
8873 		PSL_command (PSL, "%%@PROJ: %s %.8f %.8f %.8f %.8f %.3f %.3f %.3f %.3f %s\n", proj4name,
8874 			GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI],
8875 			Cartesian_m[3], Cartesian_m[1], Cartesian_m[0], Cartesian_m[2], pstr);
8876 		gmt_M_str_free (pstr);
8877 	}
8878 	if (!O_active) {	/* New plot, set GMT bounding box and reset layer counter */
8879 		/* Add GMT bounding box comment which gives dimensions of inner frame */
8880 		PSL_command (PSL, "%%GMTBoundingBox: %.12g %.12g %.12g %.12g\n",
8881 			GMT->current.setting.map_origin[GMT_X] * 72.0, GMT->current.setting.map_origin[GMT_Y] * 72.0,
8882 			GMT->current.map.width * 72.0, GMT->current.map.height * 72.0);
8883 		GMT->current.ps.layer = 0;
8884 	}
8885 
8886 	PSL_beginlayer (GMT->PSL, ++GMT->current.ps.layer);
8887 	/* Set layer transparency, if requested. Note that PSL_transp actually sets the opacity alpha, which is (1 - transparency) */
8888 	if (GMT->common.t.active) {
8889 		if (GMT->common.t.value[GMT_FILL_TRANSP] == 0.0 && GMT->common.t.value[GMT_PEN_TRANSP] == 0.0) {
8890 			GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "A transparency of 0/0 is the same as opaque. Skipped\n");
8891 			GMT->common.t.active = false;
8892 		}
8893 		else 	/* Place both fill and stroke transparencies in 0-1 normalized range, plus the blend mode name */
8894 			PSL_command (PSL, "%.12g %.12g /%s PSL_transp\n", 1.0 - 0.01 * GMT->common.t.value[GMT_FILL_TRANSP], 1.0 - 0.01 * GMT->common.t.value[GMT_PEN_TRANSP], GMT->current.setting.ps_transpmode);
8895 	}
8896 	/* If requested, place the timestamp */
8897 
8898 	if (GMT->current.ps.logo_cmd) {
8899 		char txt[4] = {' ', '-', 'X', 0}, not_used[GMT_LEN32] = {""};
8900 		struct GMT_OPTION *opt = NULL;
8901 		size_t len;
8902 		/* -Uc was given as shorthand for "plot current command line" */
8903 		if (GMT->current.setting.run_mode == GMT_MODERN)
8904 			strncpy (GMT->current.ps.map_logo_label, gmt_current_name (GMT->init.module_name, not_used), GMT_LEN256-1);
8905 		else
8906 			strncpy (GMT->current.ps.map_logo_label, GMT->init.module_name, GMT_LEN256-1);
8907 		len = strlen (GMT->current.ps.map_logo_label);
8908 		for (opt = options; opt; opt = opt->next) {
8909 			if (opt->option == GMT_OPT_INFILE || opt->option == GMT_OPT_OUTFILE) continue;	/* Skip file names */
8910 			txt[2] = opt->option;
8911 			strncat (GMT->current.ps.map_logo_label, txt, GMT_LEN256-len);
8912 			len += 3;
8913 			strncat (GMT->current.ps.map_logo_label, opt->arg, GMT_LEN256-len);
8914 		}
8915 		GMT->current.ps.logo_cmd = false;	/* Mission accomplished */
8916 	}
8917 	if (GMT->current.setting.map_logo)
8918 		gmtplot_timestamp (GMT, PSL, GMT->current.setting.map_logo_pos[GMT_X], GMT->current.setting.map_logo_pos[GMT_Y], GMT->current.setting.map_logo_justify, GMT->current.ps.map_logo_label);
8919 
8920 	PSL_settransparencymode (PSL, GMT->current.setting.ps_transpmode);	/* Set PDF transparency mode, if used */
8921 	/* Enforce chosen line parameters */
8922 	k = GMT->PSL->internal.line_cap;	GMT->PSL->internal.line_cap = GMT_NOTSET; PSL_setlinecap (PSL, k);
8923 	k = GMT->PSL->internal.line_join;	GMT->PSL->internal.line_join = GMT_NOTSET; PSL_setlinejoin (PSL, k);
8924 	k = GMT->PSL->internal.miter_limit;	GMT->PSL->internal.miter_limit = GMT_NOTSET; PSL_setmiterlimit (PSL, k);
8925 
8926 	if (P->active && P->first) {
8927 		/* Place the panel tag, once per panel (if requested), then update the gmt.panel file to say we have been there */
8928 		if (strcmp (P->tag, "-")) {	/* Place the panel tag */
8929 			int form, refpoint, justify;
8930 			refpoint = gmt_just_decode (GMT, P->refpoint, PSL_NO_DEF);	/* Convert XX refpoint code to PSL number */
8931 			gmtlib_refpoint_to_panel_xy (GMT, refpoint, P, &plot_x, &plot_y);	/* Convert just code to panel location */
8932 			/* Undo any offsets above that was required to center the plot on the subplot panel */
8933 			plot_x -= (P->dx);
8934 			plot_y -= (P->dy);
8935 			PSL_command (PSL, "/PSL_plot_completion {\nV\n");
8936 			PSL_comment (PSL, "Start of panel tag for panel (%d,%d)\n", P->row, P->col);
8937 			PSL_comment (PSL, "Will not execute until end of panel\n");
8938 			PSL_setorigin (PSL, GMT->current.setting.map_origin[GMT_X] - P->gap[XLO], GMT->current.setting.map_origin[GMT_Y] - P->gap[YLO], 0.0, PSL_FWD);
8939 			justify = gmt_just_decode (GMT, P->justify, PSL_NO_DEF);	/* Convert XX refpoint code to PSL number */
8940 			gmt_smart_justify (GMT, justify, 0.0, P->off[GMT_X], P->off[GMT_Y], &plot_x, &plot_y, 1);	/* Shift as requested */
8941 			form = gmt_setfont (GMT, &GMT->current.setting.font_tag);	/* Set the tag font */
8942 			/* Because this is run later we must force the correct font color here */
8943 			PSL_command (PSL, PSL_makecolor (PSL, GMT->current.setting.font_tag.fill.rgb));
8944 			PSL_command (PSL, " ");
8945 			PSL_setfont (PSL, GMT->current.setting.font_tag.id);
8946 			if (P->pen[0] || P->fill[0] || P->shade[0]) {	/* Must deal with textbox fill/outline/shade */
8947 				/* All fills and pens have already gone through a syntax check in subplot -A */
8948 				int outline = 0;
8949 				struct GMT_FILL fill;
8950 				struct GMT_PEN pen;
8951 				gmt_M_memset (&pen, 1, struct GMT_PEN);
8952 				gmt_init_fill (GMT, &fill, -1.0, -1.0, -1.0);	/* No fill */
8953 				PSL_command (PSL, "FQ O0\n");	/* Ensure fill/pen have been reset */
8954 				if (P->shade[0] && !gmt_getfill (GMT, P->shade, &fill)) {  /* Want to paint an offset, shaded rectangle behind the tag box */
8955 					PSL_setfill (PSL, fill.rgb, 0);  /* Shade color */
8956 					PSL_plottextbox (PSL, plot_x+P->soff[GMT_X], plot_y+P->soff[GMT_Y], GMT->current.setting.font_tag.size, P->tag, 0.0, justify, P->clearance, 0);
8957 				}
8958 				if (P->pen[0] && !gmt_getpen (GMT, P->pen, &pen)) {	/* Want to draw the outline of the tag box */
8959 					gmt_setpen (GMT, &pen);
8960 					outline = 1;
8961 				}
8962 				if (P->fill[0] && !gmt_getfill (GMT, P->fill, &fill)) {  /* Want to paint inside of tag box */
8963 					PSL_setfill (PSL, fill.rgb, outline);	/* Box color and possible outline */
8964 					PSL_plottextbox (PSL, plot_x, plot_y, GMT->current.setting.font_tag.size, P->tag, 0.0, justify, P->clearance, 0);
8965 				}
8966 				form = gmt_setfont (GMT, &GMT->current.setting.font_tag);	/* Set the tag font */
8967 				/* Finally place the tag text in the box */
8968 				PSL_plottext (PSL, plot_x, plot_y, GMT->current.setting.font_tag.size, NULL, 0.0, justify, form);
8969 			}
8970 			else   /* Just place the tag text */
8971 				PSL_plottext (PSL, plot_x, plot_y, GMT->current.setting.font_tag.size, P->tag, 0.0, justify, form);
8972 			gmtplot_reset_PSL (GMT, PSL);	/* Reset anything we may have set in building the completion PS procedure */
8973 			PSL_comment (PSL, "End of panel tag for panel (%d,%d)\n", P->row, P->col);
8974 			PSL_command (PSL, "U\n}!\n");
8975 		}
8976 		/* Store first = 0 since we are done with -B and the optional tag */
8977 		if (gmt_set_current_panel (GMT->parent, GMT->current.ps.figure, P->row, P->col, P->gap, P->tag, 0))
8978 			return NULL;	/* Should never happen */
8979 	}
8980 	if (n_movie_items[MOVIE_ITEM_IS_LABEL]) {	/* Obtained movie frame labels, implement them via a completion PostScript procedure */
8981 		/* Decode x/y/just/clearance_x/clearance_Y|offX|offY//pen/-/fill/-/font/txt in MOVIE_LABEL_ARG */
8982 		double clearance[2] = {0.0, 0.0}, soff[2] = {0.0, 0.0};
8983 		char FF[GMT_LEN64] = {""}, FS[GMT_LEN64] = {""}, PP[GMT_LEN64] = {""}, font[GMT_LEN64] = {""}, label[GMT_LEN256] = {""};
8984 		int kk, nc, box;
8985 		unsigned int T;
8986 		struct GMT_FONT Tfont;
8987 		k = MOVIE_ITEM_IS_LABEL;	/* Just a short hand to avoid repeating that long constant */
8988 		/* Create special PSL_movie_label_completion PostScript procedure and define it here in the output PS.
8989 		 * It will be called at the end of all plotting in PSL_endplot. It always exist but is NULL by default.
8990 		 * If not NULL then it will plot 1-32 labels set via movie.c's -L option */
8991 
8992 		PSL_command (PSL, "/PSL_movie_label_completion {\nV\n");
8993 		PSL_comment (PSL, "Start of movie labels\n");
8994 		PSL_comment (PSL, "Will not execute until end of plot\n");
8995 
8996 		for (T = 0; T < n_movie_items[k]; T++) {
8997 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%d:  %s\n", T, movie_item_arg[k][T]);
8998 			/* Parse -|x|y|-|-|just|clearance_x|clearance_Y|offX|offY|pen|-|fill|-|box|box_X|box_Y|sfill|font|txt in MOVIE_LABEL_ARG# strings (- means we don't parse but skip) */
8999 			/* Replace the 19 leading slashes first with spaces */
9000 			for (kk = nc = 0; movie_item_arg[k][T][kk] && nc < 19; kk++) if (movie_item_arg[k][T][kk] == '|') { movie_item_arg[k][T][kk] = ' '; nc++;}
9001 			if ((kk = sscanf (movie_item_arg[k][T], "%*c %lg %lg %*s %*s %d %lg %lg %s %*s %s %*s %d %lg %lg %s %s %[^\n]", &plot_x, &plot_y, &justify, &clearance[GMT_X], &clearance[GMT_Y], PP, FF, &box, &soff[GMT_X], &soff[GMT_Y], FS, font, label)) != 13) {
9002 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to parse MOVIE_LABEL_ARG %s for 9 required items [only made %d of 13 conversions]\n", movie_item_arg[k][T], kk);
9003 				return NULL;	/* Should never happen */
9004 			}
9005 			/* Because this PostScript procedure runs outside the main gsave/grestore block the origin is at (0,0) */
9006 			if (font[0] == '+')	/* User provided a specific font/size which we must honor */
9007 				gmt_getfont (GMT, &font[1], &Tfont);	/* We already parsed the font string in movie.c for correctness */
9008 			else	/* Pick up the default font written in movie and scale it as needed */
9009 				gmt_getfont (GMT, font, &Tfont);
9010 			gmt_getfont (GMT, font, &Tfont);	/* We already parsed the font string in movie.c for correctness so no need to check here */
9011 			form = gmt_setfont (GMT, &Tfont);	/* Obtain and set the tag font */
9012 			PSL_setfont (PSL, Tfont.id);
9013 			if (!(PP[0] == '-' && FF[0] == '-')) {	/* Requested textbox fill and/or outline */
9014 				int outline = 0;	/* No outline */
9015 				struct GMT_FILL fill;
9016 				PSL_command (PSL, "FQ O0\n");	/* Ensure fill/pen have been reset */
9017 				if (box == 1 || box == 5) {	/* Want a shaded box behind the text box */
9018 					gmt_getfill (GMT, FS, &fill);	/* No need to check since we verified fill syntax in movie.c */
9019 					PSL_setfill (PSL, fill.rgb, 0);	/* Box color (no outline for shade boxes) */
9020 					PSL_plottextbox (PSL, plot_x+soff[GMT_X], plot_y+soff[GMT_Y], Tfont.size, label, 0.0, justify, clearance, box-1);
9021 					box--;	/* Now 0 or 4 suitable for next call to PSL_plottextbox */
9022 				}
9023 				if (PP[0] != '-') {	/* Want to draw outline of tag box */
9024 					struct GMT_PEN pen;
9025 					gmt_M_memset (&pen, 1, struct GMT_PEN);
9026 					gmt_getpen (GMT, PP, &pen);	/* No need to check since we verified pen syntax in movie.c */
9027 					gmt_setpen (GMT, &pen);
9028 					outline = 1;
9029 				}
9030 				gmt_init_fill (GMT, &fill, -1.0, -1.0, -1.0);	/* Initialize to no fill */
9031 				if (FF[0] != '-')	/* Want to paint the background of tag box */
9032 					gmt_getfill (GMT, FF, &fill);	/* No need to check since we verified fill syntax in movie.c */
9033 
9034 				PSL_setfill (PSL, fill.rgb, outline);	/* Box color (or nothing) */
9035 				PSL_plottextbox (PSL, plot_x, plot_y, Tfont.size, label, 0.0, justify, clearance, box);
9036 				form = gmt_setfont (GMT, &Tfont);	/* Set the tag font (again?) */
9037 				PSL_plottext (PSL, plot_x, plot_y, Tfont.size, NULL, 0.0, justify, form);
9038 			}
9039 			else
9040 				PSL_plottext (PSL, plot_x, plot_y, Tfont.size, label, 0.0, justify, form);
9041 			gmt_M_str_free (movie_item_arg[k][T]);	/* Done with this label */
9042 			gmtplot_reset_PSL (GMT, PSL);	/* Reset anything we may have set in building the completion PS procedure */
9043 		}
9044 		PSL_comment (PSL, "End of movie labels\n");
9045 		PSL_command (PSL, "U\n}!\n");
9046 	}
9047 	if (n_movie_items[MOVIE_ITEM_IS_PROG_INDICATOR]) {	/* Obtained movie frame progress indicators, implement them via a completion PostScript procedure */
9048 		/* Decode kind|x|y|t|width|just|clearance_x|clearance_Y|offX|offY|pen|pen2|fill|fill2|-|-|-|-|font|txt in MOVIE_PROG_INDICATOR_ARG# strings */
9049 		double clearance[2] = {0.0, 0.0}, width = 0.0, t, fsize = 0.0;
9050 		char kind, F1[GMT_LEN64] = {""}, F2[GMT_LEN64] = {""}, P1[GMT_LEN64] = {""}, P2[GMT_LEN64] = {""}, font[GMT_LEN64] = {""}, label[GMT_LEN256] = {""};
9051 		int kk, nc;
9052 		unsigned int T;
9053 		struct GMT_FONT Tfont;
9054 		k = MOVIE_ITEM_IS_PROG_INDICATOR;	/* Just a short hand to avoid repeating that long constant */
9055 		/* Create special PSL_movie_label_completion PostScript procedure and define it here in the output PS.
9056 		 * It will be called at the end of all plotting in PSL_endplot. It always exist but is NULL by default.
9057 		 * If not NULL then it will plot 1-32 progress indicators set via movie.c's -P option */
9058 
9059 		PSL_command (PSL, "/PSL_movie_prog_indicator_completion {\n");
9060 		PSL_comment (PSL, "Start of movie progress indicators\n");
9061 		PSL_comment (PSL, "Will not execute until end of plot\n");
9062 
9063 		for (T = 0; T < n_movie_items[k]; T++) {
9064 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%d:  %s\n", T, movie_item_arg[k][T]);
9065 			PSL_command (PSL, "V\n");
9066 			/* Replace the 19 leading bars first with spaces */
9067 			for (kk = nc = 0; movie_item_arg[k][T][kk] && nc < 19; kk++) if (movie_item_arg[k][T][kk] == '|') { movie_item_arg[k][T][kk] = ' '; nc++;}
9068 			if ((kk = sscanf (movie_item_arg[k][T], "%c %lg %lg %lg %lg %d %lg %lg %s %s %s %s %*d %*lg %*lg %*s %s %[^\n]", &kind, &plot_x, &plot_y, &t, &width, &justify, &clearance[GMT_X], &clearance[GMT_Y], P1, P2, F1, F2, font, label)) < 13) {
9069 				GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to parse MOVIE_PROG_INDICATOR_ARG %s for 14 required items [only made %d of 13-14 conversions]\n", movie_item_arg[k][T], kk);
9070 				return NULL;	/* Should never happen */
9071 			}
9072 			/* Because this runs outside main gsave/grestore block the origin is (0,0) */
9073 			if (isupper (kind) && kind != 'A') {	/* Requested text labels so initialize selected font */
9074 				if (font[0] == '+') {	/* User provided a specific font/size which we must honor */
9075 					gmt_getfont (GMT, &font[1], &Tfont);	/* We already parsed the font string in movie.c for correctness */
9076 					fsize = Tfont.size;	/* Must honor the given size */
9077 				}
9078 				else {	/* Pick up the default font written in movie and scale it as needed */
9079 					gmt_getfont (GMT, font, &Tfont);
9080 					fsize = 0.0;	/* So we can scale at will for b-e */
9081 				}
9082 				form = gmt_setfont (GMT, &Tfont);	/* Set the font to be used */
9083 				PSL_setfont (PSL, Tfont.id);
9084 			}
9085 			switch (tolower (kind)) {	/* Set the selected progress indicator a-f */
9086 				case 'a': 	/* Default pie symbol (no label) */
9087 					gmtplot_prog_indicator_A (GMT, plot_x, plot_y, t, width, justify, F1, F2);
9088 					break;
9089 				case 'b': 	/* Growing ring symbol with optional label */
9090 					gmtplot_prog_indicator_B (GMT, plot_x, plot_y, t, width, justify, P1, P2, label, kind, fsize);
9091 					break;
9092 				case 'c': /* Growing circular arrow with optional label */
9093 					gmtplot_prog_indicator_C (GMT, plot_x, plot_y, t, width, justify, P1, P2, label, kind, fsize);
9094 					break;
9095 				case 'd': /* Rounded time line with vertical bar and optional label */
9096 					gmtplot_prog_indicator_D (GMT, plot_x, plot_y, t, width, justify, P1, P2, label, kind, fsize);
9097 					break;
9098 				case 'e': /* Straight line on top of fixed line with optional start/end labels */
9099 					gmtplot_prog_indicator_E (GMT, plot_x, plot_y, t, width, justify, P1, P2, label, kind, fsize);
9100 					break;
9101 				case 'f': /* Moving triangle on basemap time-line */
9102 					gmtplot_prog_indicator_F (GMT, plot_x, plot_y, t, width, justify, P2, F1, label, kind, width, fsize, &Tfont);
9103 					break;
9104 				default:	/* Just for Coverity */
9105 					break;
9106 			}
9107 			gmt_M_str_free (movie_item_arg[k][T]);	/* Free since done with this string */
9108 			gmtplot_reset_PSL (GMT, PSL);	/* Reset anything we may have set in building the completion PS procedure */
9109 		PSL_command (PSL, "U\n");
9110 		}
9111 		PSL_comment (PSL, "End of movie progress indicators\n");
9112 		PSL_command (PSL, "}!\n");
9113 	}
9114 	if (do_paint) {	/* Reset any canvas coloring here */
9115 		GMT->current.map.frame.paint[GMT_Z] = true;
9116 		gmt_M_memcpy (&GMT->current.map.frame.fill[GMT_Z], &fill, 1U, struct GMT_FILL);
9117 	}
9118 
9119 	return (PSL);
9120 }
9121 
gmt_plotcanvas(struct GMT_CTRL * GMT)9122 void gmt_plotcanvas (struct GMT_CTRL *GMT) {
9123 	if (GMT->current.map.frame.paint[GMT_Z]) {	/* Paint the inside of the map (xy plane) with specified fill */
9124 		double *x = NULL, *y = NULL;
9125 		uint64_t np;
9126 		bool donut;
9127 		PSL_comment (GMT->PSL, "Fill the canvas %s\n", gmtlib_putfill (GMT, &GMT->current.map.frame.fill[GMT_Z]));
9128 		np = gmt_map_clip_path (GMT, &x, &y, &donut);
9129 		gmt_setfill (GMT, &GMT->current.map.frame.fill[GMT_Z], 0);
9130 		PSL_plotpolygon (GMT->PSL, x, y, (int)((1 + donut) * np));
9131 		gmt_M_free (GMT, x);
9132 		gmt_M_free (GMT, y);
9133 	}
9134 }
9135 
gmt_strip_layer(struct GMTAPI_CTRL * API,int nlayers)9136 int gmt_strip_layer (struct GMTAPI_CTRL *API, int nlayers) {
9137 	/* Remove the last n layers from the current figure */
9138 	char file[PATH_MAX] = {""}, buffer[GMT_LEN64] = {""};
9139 	int k = 0, fig;
9140 	size_t n_alloc = GMT_SMALL_CHUNK;
9141 	FILE *fp = NULL;
9142 	struct GMT_PSLAYER {	/* Used to hold the layer info locally */
9143 		unsigned int id;
9144 		size_t size;
9145 	} *layer = NULL;
9146 
9147 	fig = gmt_get_current_figure (API);
9148 	/* Get the name of the corresponding gmt.layers.<fig> file */
9149 	snprintf (file, PATH_MAX, "%s/gmt.layers.%d", API->gwf_dir, fig);
9150 	if (nlayers == GMT_NOTSET) {	/* Reset to nothing, but still remain at current figure */
9151 		if (gmt_remove_file (API->GMT, file))	/* Remove the layers file */
9152 			GMT_Report (API, GMT_MSG_WARNING, "Failed to delete file: %s\n", file);
9153 		snprintf (file, PATH_MAX, "%s/gmt_%d.ps-", API->gwf_dir, fig);
9154 		if (gmt_remove_file (API->GMT, file))	/* Wipe the PS file */
9155 			GMT_Report (API, GMT_MSG_WARNING, "Failed to delete file: %s\n", file);
9156 		return GMT_NOERROR;
9157 	}
9158 	/* See if there is a layers file to read for this figure */
9159 	if (access (file, R_OK)) {
9160 		GMT_Report (API, GMT_MSG_ERROR, "No layers available [no file: %s]\n", file);
9161 		return GMT_FILE_NOT_FOUND;
9162 	}
9163 
9164 	/* OK, the file exist, can we read it? */
9165 	if ((fp = fopen (file, "r")) == NULL) {
9166 		GMT_Report (API, GMT_MSG_ERROR, "Could not open file %s\n", file);
9167 		return GMT_ERROR_ON_FOPEN;
9168 	}
9169 	/* Ingest the layer information */
9170 	layer = gmt_M_memory (API->GMT, NULL, n_alloc, struct GMT_PSLAYER);
9171 	while (fgets (buffer, GMT_LEN64, fp)) {
9172 		sscanf (buffer, "%d %" PRIuS, &layer[k].id, &layer[k].size);
9173 		if (++k == (int)n_alloc) {	/* Need more memory */
9174 			n_alloc <<= 1;
9175 			layer = gmt_M_memory (API->GMT, layer, n_alloc, struct GMT_PSLAYER);
9176 		}
9177 	}
9178 	fclose (fp);
9179 	if (nlayers >= k) {
9180 		GMT_Report (API, GMT_MSG_ERROR, "Cannot revert %d layers as only %d layers found!\n", nlayers, k);
9181 		gmt_M_free (API->GMT, layer);
9182 		return GMT_RUNTIME_ERROR;
9183 	}
9184 
9185 	snprintf (file, PATH_MAX, "%s/gmt_%d.ps-", API->gwf_dir, fig);
9186 	if (gmt_truncate_file (API, file, layer[k-nlayers-1].size)) {
9187 		GMT_Report (API, GMT_MSG_ERROR, "Could not truncate file %s!\n", file);
9188 		gmt_M_free (API->GMT, layer);
9189 		return GMT_RUNTIME_ERROR;
9190 	}
9191 	/* Finally, rewrite the layers file to skip the reverted layers */
9192 	snprintf (file, PATH_MAX, "%s/gmt.layers.%d", API->gwf_dir, fig);
9193 	if ((fp = fopen (file, "w")) == NULL) {
9194 		GMT_Report (API, GMT_MSG_ERROR, "Could not create new file %s\n", file);
9195 		gmt_M_free (API->GMT, layer);
9196 		return GMT_ERROR_ON_FOPEN;
9197 	}
9198 	nlayers = k - nlayers;	/* Number of surviving layers */
9199 	for (k = 0; k < nlayers; k++)
9200 		fprintf (fp, "%d %" PRIuS "\n", layer[k].id, layer[k].size);
9201 	fclose (fp);
9202 	gmt_M_free (API->GMT, layer);
9203 	return GMT_NOERROR;
9204 }
9205 
gmt_plotend(struct GMT_CTRL * GMT)9206 void gmt_plotend (struct GMT_CTRL *GMT) {
9207 	unsigned int i;
9208 	bool K_active = (GMT->current.setting.run_mode == GMT_MODERN) ? true : GMT->common.K.active;
9209 	struct PSL_CTRL *PSL= GMT->PSL;
9210 	PSL_endlayer (GMT->PSL);
9211 	if (GMT->common.t.active) PSL_command (PSL, "1 1 /Normal PSL_transp\n"); /* Reset transparency to fully opaque, if required */
9212 
9213 	if (GMT->common.p.do_z_rotation) {	/* Need a undo the rotation about z of the whole page */
9214 		double x0 = 0.0, y0 = 0.0;	/* Default is to rotate around plot origin */
9215 		if (GMT->current.proj.z_project.view_given) {	/* Rotation is about another z-axis than through the origin */
9216 			x0 = GMT->current.proj.z_project.view_x;
9217 			y0 = GMT->current.proj.z_project.view_y;
9218 		}
9219 		else if (GMT->current.proj.z_project.world_given)	/* Rotation is about another lon/lat pole than the origin */
9220 			gmt_geo_to_xy (GMT, GMT->current.proj.z_project.world_x, GMT->current.proj.z_project.world_y, &x0, &y0);
9221 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Transrot: Unrotating plot by %g degrees about (%g, %g)\n", -GMT->common.p.z_rotation, x0, y0);
9222 		PSL_comment (GMT->PSL, "Possibly translate then unrotate rotate whole page\n");
9223 		PSL_setorigin (PSL, x0, y0, -GMT->common.p.z_rotation, PSL_FWD);
9224 		PSL_setorigin (PSL, -x0, -y0, 0.0, PSL_FWD);
9225 	}
9226 
9227 	/* Check expected change of clip level to achieved one. Update overall clip level. Check for pending clips. */
9228 
9229 	if (abs (GMT->current.ps.nclip) == PSL_ALL_CLIP)	/* Special case where we reset all polygon clip levels */
9230 		GMT->current.ps.clip_level = GMT->current.ps.nclip = PSL->current.nclip = 0;
9231 	else
9232 		GMT->current.ps.clip_level += GMT->current.ps.nclip;
9233 
9234 	if (GMT->current.ps.nclip != PSL->current.nclip)
9235 		GMT_Report (GMT->parent, GMT_MSG_INFORMATION,
9236 						"Module was expected to change clip level by %d, but clip level changed by %d\n", GMT->current.ps.nclip, PSL->current.nclip);
9237 
9238 	if (!K_active) {
9239 		if (GMT->current.ps.clip_level > 0)
9240 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "%d external clip operations were not terminated!\n", GMT->current.ps.clip_level);
9241 		if (GMT->current.ps.clip_level < 0)
9242 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "%d extra terminations of external clip operations!\n", -GMT->current.ps.clip_level);
9243 		GMT->current.ps.clip_level = 0;	/* Reset to zero, so it will no longer show up in gmt.history */
9244 	}
9245 	for (i = 0; i < 3; i++) gmt_M_str_free (GMT->current.map.frame.axis[i].file_custom);
9246 	PSL_endplot (PSL, !K_active);
9247 
9248 	if (GMT->current.setting.run_mode == GMT_MODERN) {	/* Reset file pointer and name */
9249 		struct stat buf;
9250 		char file[PATH_MAX] = {""};
9251 		FILE *fp = NULL;
9252 		if (stat (GMT->current.ps.filename, &buf))
9253 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Could not determine size of file %s\n", GMT->current.ps.filename);
9254 		else
9255 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Current size of half-baked PS file %s = %" PRIuS ".\n", GMT->current.ps.filename, buf.st_size);
9256 		GMT->current.ps.fp = NULL;
9257 		GMT->current.ps.filename[0] = '\0';
9258 		/* Write layer size to gmt.layers.<fig> in case of revert calls */
9259 		snprintf (file, PATH_MAX, "%s/gmt.layers.%d", GMT->parent->gwf_dir, GMT->current.ps.figure);
9260 		if ((fp = fopen (file, "a")) == NULL) {
9261 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not open/create file %s\n", file);
9262 			return;
9263 		}
9264 		fprintf (fp, "%d\t%" PRIuS "\n", GMT->current.ps.layer, (size_t)buf.st_size);
9265 		fclose (fp);
9266 	}
9267 	else if (PSL->internal.memory) {    /* Time to write out buffer regardless of mode */
9268 		struct GMT_POSTSCRIPT *P = gmt_get_postscript (GMT);
9269 		struct GMT_POSTSCRIPT_HIDDEN *PH = gmt_get_P_hidden (P);
9270 		if (GMT->current.ps.title[0]) {
9271 			P->header = gmt_M_memory (GMT, NULL, 1, char *);
9272 			P->header[0] = strdup (GMT->current.ps.title);
9273 			P->n_headers = 1;
9274 		}
9275 		P->data = PSL_getplot (PSL);	/* Get the plot buffer */
9276 		P->n_bytes = PSL->internal.n;   /* Length of plot buffer; note P->n_alloc = 0 since we did not allocate this string here */
9277 		P->mode = PSL->internal.pmode;  /* Mode of plot (GMT_PS_{HEADER,TRAILER,COMPLETE}) */
9278 		PH->alloc_mode = GMT_ALLOC_EXTERNALLY;	/* Since created in PSL */
9279 		if (GMT_Write_Data (GMT->parent, GMT_IS_POSTSCRIPT, GMT_IS_REFERENCE, GMT_IS_TEXT, GMT_WRITE_NORMAL, NULL, GMT->current.ps.memname, P) != GMT_OK) {
9280 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Unable to write PS structure to file %s!\n", GMT->current.ps.memname);
9281 		}
9282 		/* coverity[leaked_storage] */	/* We can't free P because it was written into a 'memory file' */
9283 	}
9284 	GMT->current.ps.title[0] = '\0';	/* Reset title */
9285 	if (GMT->current.ps.oneliner) GMT->current.ps.active = true;	/* Since we are plotting we reset this here in case other modules have turned it off */
9286 
9287 	if (!K_active) GMT->current.plot.gridline_spacing[GMT_X] = GMT->current.plot.gridline_spacing[GMT_Y] = 0.0;	/* Done, if they ever were used */
9288 #if 0
9289 	if (GMT->current.setting.run_mode == GMT_CLASSIC) {	/* Remove any gridline file we may have made in /tmp */
9290 		char file[PATH_MAX] = {""};
9291 		snprintf (file, PATH_MAX, "%s/%s-gmt.gridlines", GMT->parent->tmp_dir, GMT->parent->session_name);
9292 		gmt_remove_file (GMT, file);
9293 	}
9294 #endif
9295 }
9296 
gmt_geo_line(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n)9297 void gmt_geo_line (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n) {
9298 	/* When geographic lines are plotted, they may cross the boundaries, may need to be clipped,
9299 	 * or may appear again on the other side of the map. This is all taken care of in this
9300 	 * routine.
9301 	 */
9302 	if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, lon, lat, n)) == 0) return;	/* Nothing further to do */
9303 	gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);	/* Separately plot the outline */
9304 }
9305 
gmt_geo_polarcap_segment(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,double ** lon,double ** lat)9306 uint64_t gmt_geo_polarcap_segment (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, double **lon, double **lat) {
9307 	/* Special treatment for polar caps since they must add in parts of possibly curved periodic boundaries
9308 	 * from the pole up or down to the intersection with the cap perimeter.  We handle this case separately here.
9309 	 * This is in response to issue # 852. P. Wessel */
9310 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
9311 	uint64_t k0, perim_n, n_new, m, n = S->n_rows, k;
9312 	double start_lon, stop_lon, yc = 0.0, dx, pole_lat = 90.0 * SH->pole;
9313 	double *x_perim = NULL, *y_perim = NULL, *plon = NULL, *plat = NULL;
9314 	static char *pole = "S N";
9315 	int type;
9316 #if 0
9317 	FILE *fp;
9318 	bool aap = gmtplot_at_pole (S->data[GMT_Y], S->n_rows);
9319 #endif
9320 	if (GMT->common.R.oblique) return n;	/* Algorithm assumes meridian boundaries */
9321 	/* We want this code to be used for the misc. global projections but also global cylindrical or linear(if degrees) maps */
9322 	if (!(gmt_M_is_misc(GMT) || (GMT->current.map.is_world  && (gmt_M_is_cylindrical(GMT) || (gmt_M_is_linear(GMT) && gmt_M_is_geographic(GMT,GMT_IN)))))) return n;	/* We are only concerned with the global misc projections here */
9323 	/* Global projection need to handle pole path properly */
9324 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Try to include %c pole in polar cap path\n", pole[SH->pole+1]);
9325 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "West longitude = %g.  East longitude = %g\n", GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
9326 	type = gmtlib_determine_pole (GMT, S->data[GMT_X], S->data[GMT_Y], n);
9327 	if (abs(type) == 2)	/* The algorithm only works for clockwise polygon so anything CCW we simply reverse... */
9328 		gmtplot_reverse_polygon (GMT, S);
9329 	start_lon = GMT->common.R.wesn[XHI];
9330 	stop_lon  = GMT->common.R.wesn[XLO];
9331 
9332 	for (k = 0; k < n; k++) 	/* Make negative longitudes only */
9333 		if (S->data[GMT_X][k] >= 180.0) S->data[GMT_X][k] -= 360.0;
9334 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "First longitude = %g.  Last longitude = %g\n", S->data[GMT_X][0], S->data[GMT_X][n-1]);
9335 	for (k = 1, k0 = 0; k0 == 0 && k < n; k++) {	/* Determine where the perimeter crossing with the west boundary occurs */
9336 		/* Must assume longitudes randomly adds/subtracts 360 and hence carefully check for the crossing */
9337 		//double R0, L0;
9338 		double R, L, D;
9339 		//R0 = GMT->common.R.wesn[XLO]-S->data[GMT_X][k];
9340 		//L0 = GMT->common.R.wesn[XLO]-S->data[GMT_X][k-1];
9341 		gmt_M_set_delta_lon (S->data[GMT_X][k],   GMT->common.R.wesn[XLO], R);	/* Handles the 360 jump cases */
9342 		gmt_M_set_delta_lon (S->data[GMT_X][k-1], GMT->common.R.wesn[XLO], L);	/* Handles the 360 jump cases */
9343 		D = R - L;
9344 		//fprintf (stderr, "k = %d L0 R0 = %g %g  L R = %g %g  D = %g\n", (int)k, L0, R0, L, R, D);
9345 		if (D >= 0.0 && D < 180.0 && L <= 0.0 && R >= 0.0) k0 = k;
9346 	}
9347 	/* Determine the latitude of that crossing */
9348 	if (k0) {	/* Occurred somewhere along the perimeter between points k0 and k0-1 */
9349 		//double x_dist = S->data[GMT_X][k0-1] - GMT->common.R.wesn[XLO];
9350 		double x_dist;
9351 		gmt_M_set_delta_lon (S->data[GMT_X][k0-1], S->data[GMT_X][k0], dx);	/* Handles the 360 jump cases */
9352 		gmt_M_set_delta_lon (S->data[GMT_X][k0-1], GMT->common.R.wesn[XLO], x_dist);	/* Handles the 360 jump cases */
9353 		yc = S->data[GMT_Y][k0-1] + (S->data[GMT_Y][k0] - S->data[GMT_Y][k0-1]) * x_dist / dx;
9354 	}
9355 	else	/* Very first point is at the right longitude */
9356 		yc = S->data[GMT_Y][k0];
9357 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Crossing at %g,%g\n", GMT->common.R.wesn[XLO], yc);
9358 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "k at point closest to lon %g is = %d [n = %d]\n", GMT->common.R.wesn[XLO], (int)k0, (int)n);
9359 	/* Then, add path from pole to start longitude, then copy perimeter path, then add path from stop longitude back to pole */
9360 	/* With cylindrical projections we may not go all the way to the pole, so we adjust pole_lat based on -R: */
9361 	if (pole_lat < GMT->common.R.wesn[YLO]) pole_lat = GMT->common.R.wesn[YLO];
9362 	if (pole_lat >GMT->common.R.wesn[YHI])  pole_lat = GMT->common.R.wesn[YHI];
9363 	/* 1. Calculate the path from yc to pole: */
9364 	perim_n = gmtlib_lonpath (GMT, start_lon, pole_lat, yc, &x_perim, &y_perim);
9365 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Created path from %g/%g to %g/%g [%d points]\n", start_lon, pole_lat, start_lon, yc, perim_n);
9366 	/* 2. Allocate enough space for new polar cap polygon */
9367 	n_new = 2 * perim_n + n;
9368 	plon = gmt_M_memory (GMT, NULL, n_new, double);
9369 	plat = gmt_M_memory (GMT, NULL, n_new, double);
9370 	/* Start off with the path from the pole to the crossing */
9371 	if (perim_n) {
9372 		gmt_M_memcpy (plon, x_perim, perim_n, double);
9373 		gmt_M_memcpy (plat, y_perim, perim_n, double);
9374 	}
9375 	/* Now walk from k0 to the end of polygon, wrapping around if needed */
9376 	m = perim_n;	/* Index of next output point */
9377 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Add perimeter data from k0->n [%d->%d], then 0->k0 [%d]\n", k0, n, k0);
9378 	for (k = k0; k < n; k++, m++) {
9379 		plon[m] = S->data[GMT_X][k];
9380 		plat[m] = S->data[GMT_Y][k];
9381 	}
9382 	for (k = 0; k < k0; k++, m++) {
9383 		plon[m] = S->data[GMT_X][k];
9384 		plat[m] = S->data[GMT_Y][k];
9385 	}
9386 	/* Then add the opposite path to the pole, switching the longitude to stop_lon */
9387 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Add path from %g/%g to %g/%g [%d points]\n", stop_lon, yc, stop_lon, pole_lat, perim_n);
9388 	if (perim_n) {
9389 		for (k = perim_n-1; k > 0; k--, m++) {
9390 			plon[m] = stop_lon;
9391 			plat[m] = y_perim[k];
9392 		}
9393 	}
9394 	/* Finally add the duplicate pole at the end of polygon so it is closed */
9395 	plon[m] = stop_lon;
9396 	plat[m++] = pole_lat;
9397 	if (perim_n) {
9398 		gmt_M_free (GMT, x_perim);	gmt_M_free (GMT, y_perim);	/* No longer needed */
9399 	}
9400 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "New path has %d points, we allocated %d points\n", m, n_new);
9401 #if 0
9402 	fp = fopen ("shit.txt", "w");
9403 	for (k = 0; k < m; k++) {
9404 		fprintf (fp, "%lg\t%lg\n", plon[k], plat[k]);
9405 	}
9406 	fclose (fp);
9407 #endif
9408 	*lon = plon;	*lat = plat;
9409 	return (m);	/* Number of points in new polygon */
9410 }
9411 
gmt_geo_polygons(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)9412 void gmt_geo_polygons (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
9413 	/* Deal with plotting of one or more polygons that may wrap across the map.
9414 	 * Multi-polygons occur if composed of a perimeter and one or more holes.
9415 	 * This is marked by S->next being set to point to the next hole.
9416 	 * Also, if the perimeter is a polar cap we must add a helping line that
9417 	 * connects to the pole but this line should not be drawn.  This is why
9418 	 * we must lay down path twice (first for fill; then for line) since the
9419 	 * two paths are not the same.  If no fill is requested then we just draw lines.
9420 	 */
9421 	struct GMT_DATASEGMENT *S2 = NULL;
9422 	struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
9423 	bool add_pole, separate;
9424 	int outline = 0, P_handedness = GMT_NOTSET, H_handedness;
9425 	int geo = gmt_M_is_geographic (GMT, GMT_IN);
9426 	uint64_t used = 0;
9427 	char *type[2] = {"Perimeter", "Polar cap perimeter"};
9428 	char *use[2] = {"fill only", "fill and outline"};
9429 	char comment[GMT_LEN64] = {""};
9430 	struct PSL_CTRL *PSL= GMT->PSL;
9431 
9432 	/* CASE 1: NO FILL REQUESTED -- JUST DRAW OUTLINE */
9433 
9434 	if (gmt_M_eq (PSL->current.rgb[PSL_IS_FILL][0], -1.0)) {
9435 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows)) == 0) return;	/* Nothing further to do */
9436 		PSL_comment (PSL, "Perimeter polygon for outline only\n");
9437 		gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);	/* Separately plot the outline */
9438 		for (S2 = gmt_get_next_S (S); S2; S2 = gmt_get_next_S (S2)) {
9439 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, S2->data[GMT_X], S2->data[GMT_Y], S2->n_rows)) == 0) continue;	/* Nothing for this hole */
9440 			PSL_comment (PSL, "Hole polygon for outline only\n");
9441 			gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);	/* Separately plot the outline */
9442 		}
9443 		return;	/* Done with the simple task of drawing lines */
9444 	}
9445 
9446 	/* CASE 2: FILL REQUESTED -- WITH OR WITHOUT OUTLINE */
9447 
9448 	add_pole = (abs (SH->pole) == 1);	/* true if a polar cap */
9449 	separate = ((add_pole || gmt_get_next_S (S)) && PSL->current.outline);	/* Multi-polygon (or polar cap) fill with outline handled by doing fill and outline separately */
9450 	if (separate) {				/* Do fill and outline separately */
9451 		outline = PSL->current.outline;	/* Keep a copy of what we wanted */
9452 		PSL->current.outline = false;	/* Turns off outline for now (if set) */
9453 		PSL_command (PSL, "O0\n");	/* Temporarily switch off outline in the PS */
9454 	}
9455 
9456 	/* Here we must lay down the perimeter and then the holes.  */
9457 
9458 	if (PSL->internal.comments) snprintf (comment, GMT_LEN64, "%s polygon for %s\n", type[add_pole], use[PSL->current.outline]);
9459 	used = gmtplot_geo_polygon_segment (GMT, S, add_pole, true, comment);	/* First lay down perimeter */
9460 	for (S2 = gmt_get_next_S (S); S2; S2 = gmt_get_next_S (S2)) {	/* Process all holes [none processed if there aren't any holes] */
9461 		if (P_handedness == GMT_NOTSET)
9462 			P_handedness = gmt_polygon_orientation (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows, geo);
9463 		H_handedness = gmt_polygon_orientation (GMT, S2->data[GMT_X], S2->data[GMT_Y], S2->n_rows, geo);
9464 
9465 		if (PSL->internal.comments) snprintf (comment, GMT_LEN64, "Hole polygon for %s\n", use[PSL->current.outline]);
9466 		if (H_handedness == P_handedness) {
9467 			uint64_t row_f, row_l, col;
9468 			for (row_f = 0, row_l = S2->n_rows - 1; row_f < S2->n_rows/2; row_f++, row_l--) {
9469 				for (col = 0; col < S2->n_columns; col++) gmt_M_double_swap (S2->data[col][row_f], S2->data[col][row_l]);
9470 			}
9471 		}
9472 		used += gmtplot_geo_polygon_segment (GMT, S2, false, false, comment);	/* Add this hole to the path */
9473 	}
9474 	if (used) {
9475 		PSL_comment (PSL, "Reset FO and fill the path\n");
9476 		PSL_command (PSL, "/FO {fs os}!\nFO\n");	/* Reset FO to its original settings, then force the fill */
9477 	}
9478 	if (separate) {	/* Must draw outline separately */
9479 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows)) == 0) return;	/* Nothing further to do */
9480 		PSL_command (PSL, "O1\n");	/* Switch on outline again */
9481 		PSL_comment (PSL, "%s polygon for outline only\n", type[add_pole]);
9482 		PSL->current.outline = outline;	/* Reset outline to what it was originally */
9483 		gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);	/* Separately plot the outline */
9484 		for (S2 = gmt_get_next_S (S); S2; S2 = gmt_get_next_S (S2)) {
9485 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, S2->data[GMT_X], S2->data[GMT_Y], S2->n_rows)) == 0) continue;	/* Nothing for this hole */
9486 			PSL_comment (PSL, "Hole polygon for outline only\n");
9487 			gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);	/* Separately plot the outline */
9488 		}
9489 	}
9490 }
9491 
gmt_plot_geo_ellipse(struct GMT_CTRL * GMT,double lon,double lat,double major,double minor,double azimuth)9492 void gmt_plot_geo_ellipse (struct GMT_CTRL *GMT, double lon, double lat, double major, double minor, double azimuth) {
9493 	/* gmt_plot_geo_ellipse takes the location, axes (in km), and azimuth of an ellipse
9494 		and draws an approximate ellipse using N-sided polygon and the chosen map projection.
9495 		We determine N by looking at the spacing between successive points for a trial N and
9496 		then scale N up or down to satisfy the minimum point spacing criteria. */
9497 
9498 	struct GMT_DATASEGMENT *S = gmt_get_geo_ellipse (GMT, lon, lat, major, minor, azimuth, 0);
9499 
9500 	gmt_geo_polygons (GMT, S);
9501 
9502 	gmt_free_segment (GMT, &S);
9503 }
9504 
gmtplot_get_far_point(struct GMT_CTRL * GMT,double lon,double lat,double radius,double * P)9505 GMT_LOCAL void gmtplot_get_far_point (struct GMT_CTRL *GMT, double lon, double lat, double radius, double *P) {
9506 	/* Get a point P that is radius degrees away along the meridian through our point X */
9507 	double plat, plon;
9508 	plat = lat + radius;
9509 	plon = lon;
9510 	if (plat > 90.0) {	/* Went over the pole */
9511 		plon += 180.0;
9512 		plat = 180 - plat;
9513 	}
9514 	gmt_geo_to_cart (GMT, plat, plon, P, true);	/* Vector <radius> degrees away from (lon,lat) along meridian */
9515 }
9516 
gmtplot_geo_spider(struct GMT_CTRL * GMT,double xlon,double xlat,double radius_i,double radius_o,double dr,double az_start,double az_stop,double da,unsigned int wmode)9517 GMT_LOCAL void gmtplot_geo_spider (struct GMT_CTRL *GMT, double xlon, double xlat, double radius_i, double radius_o, double dr, double az_start, double az_stop, double da, unsigned int wmode) {
9518 	/* gmtplot_geo_spider takes the location, radius_i (in unit), radius_o, dr, and start/stop azimuths of an geo-wedge and da
9519 		and draws an approximate circular spider-web using N-sided polygon.
9520 		mode & 1: Just draw arc(s)
9521 		mode & 2: Just draw radii(s)
9522 	*/
9523 
9524 	int n_arc, k, ko, n_seg;
9525 	uint64_t n_new;
9526 	bool windshield = (radius_i > 0.0);	/* Windshield web */
9527 	enum GMT_enum_wedgetype mode = wmode;
9528 	double qlon, qlat, az, rot_start, E[3], P_o[3], P_i[3], Q[3], R[3][3];
9529 	struct GMT_DATASEGMENT *S = NULL;
9530 
9531 	radius_i = gmtlib_conv_distance (GMT, radius_i, 'k', 'd');	/* Convert inner radius from km to degrees */
9532 	radius_o = gmtlib_conv_distance (GMT, radius_o, 'k', 'd');	/* Convert outer radius from km to degrees */
9533 	dr = gmtlib_conv_distance (GMT, dr, 'k', 'd');			/* Convert radial increments from km to degrees */
9534 
9535 	gmt_geo_to_cart (GMT, xlat, xlon, E, true);	/* Euler rotation pole at point location */
9536 
9537 	/* Get a point P_o that is radius_o degrees away along the meridian through our point X */
9538 	gmtplot_get_far_point (GMT, xlon, xlat, radius_o, P_o);
9539 	if (windshield)	/* Get a point P_i that is radius_i degrees away along the meridian through our point X */
9540 		gmtplot_get_far_point (GMT, xlon, xlat, radius_i, P_i);
9541 
9542 	PSL_comment (GMT->PSL, "Placing Spider Web\n");
9543 	PSL_command (GMT->PSL, "V PSL_spiderpen\n");	/* Place spider under gsave/grestore and change to spiderpen */
9544 	if (mode == GMT_WEDGE_RADII || mode == GMT_WEDGE_SPIDER) {	/* Need to draw radial start and end points */
9545 		double *azim = NULL;
9546 		if (da > 0.0)	/* Must draw several radial lines */
9547 			n_seg = gmtlib_linear_array (GMT, az_start, az_stop, da, 0.0, &azim);
9548 		else {	/* Two radial lines only */
9549 			azim = gmt_M_memory (GMT, NULL, n_seg = 2, double);
9550 			azim[0] = az_start;	azim[1] = az_stop;
9551 		}
9552 		PSL_comment (GMT->PSL, "Drawing Spider Radials\n");
9553 		for (k = 0; k < n_seg; k++) {	/* For each radial line */
9554 			S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, 2, 2, NULL, NULL);	/* Segment with two rows */
9555 			gmt_make_rot_matrix2 (GMT, E, -azim[k], R);	/* Since we have a right-handed rotation but gave azimuths */
9556 			if (windshield) {	/* Start at inner arc point */
9557 				gmt_matrix_vect_mult (GMT, 3U, R, P_i, Q);
9558 				gmt_cart_to_geo (GMT, &S->data[GMT_Y][0], &S->data[GMT_X][0], Q, true);	/* Rotated inner point */
9559 			}
9560 			else {	/* Start at origin */
9561 				S->data[GMT_X][0] = xlon;	S->data[GMT_Y][0] = xlat;
9562 			}
9563 			gmt_matrix_vect_mult (GMT, 3U, R, P_o, Q);
9564 			gmt_cart_to_geo (GMT, &S->data[GMT_Y][1], &S->data[GMT_X][1], Q, true);	/* Rotated outer point */
9565 			if ((n_new = gmt_fix_up_path (GMT, &S->data[GMT_X], &S->data[GMT_Y], S->n_rows, 0.0, 0)) == 0) {
9566 				gmt_free_segment (GMT, &S);
9567 				continue;
9568 			}
9569 			S->n_rows = n_new;
9570 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows)) == 0) continue;
9571 			gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
9572 			gmt_free_segment (GMT, &S);
9573 		}
9574 		gmt_M_free (GMT, azim);
9575 	}
9576 	if (mode == GMT_WEDGE_ARCS || mode == GMT_WEDGE_SPIDER) {	/* Need to draw one or more arcs */
9577 		double d_az, px, py, qx, qy, L, plon_o, plat_o;
9578 		double *r = NULL;
9579 		plat_o = xlat + radius_o;
9580 		plon_o = xlon;
9581 		if (plat_o > 90.0) {	/* Went over the pole */
9582 			plon_o += 180.0;
9583 			plat_o = 180 - plat_o;
9584 		}
9585 		/* Compute distance between P_o and Q and compare to map_line_step to determine azimuthal sampling */
9586 		gmt_make_rot_matrix2 (GMT, E, 1.0, R);		/* Test point 1 degree away */
9587 		gmt_matrix_vect_mult (GMT, 3U, R, P_o, Q);	/* Get Q = R * P_o */
9588 		gmt_cart_to_geo (GMT, &qlat, &qlon, Q, true);	/* Coordinates of rotated point */
9589 		gmt_geo_to_xy (GMT, plon_o, plat_o, &px, &py);	/* P_o projected on map */
9590 		gmt_geo_to_xy (GMT, qlon, qlat, &qx, &qy);	/* Q projected on map */
9591 		L = hypot (px - qx, py - qy);			/* Distance in inches along arc for 1 degree of azimuth change */
9592 		/* Estimate how many intermediate points by dividing the estinate arc length by line_step */
9593 		n_arc = MAX (2, irint (fabs (az_stop - az_start) * L /GMT->current.setting.map_line_step));
9594 		d_az = (az_stop - az_start) / (n_arc - 1);	/* Azimuthal sampling rate */
9595 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Arcs will be approximated by %d-point lines\n", n_arc);
9596 		PSL_comment (GMT->PSL, "Drawing Spider Arcs\n");
9597 
9598 		if (dr > 0.0)	/* Must draw several arcs */
9599 			n_seg = gmtlib_linear_array (GMT, (radius_i > 0.0) ? radius_i : dr, radius_o, dr, 0.0, &r);
9600 		else {	/* One or two arcs only */
9601 			if (radius_i > 0.0) {
9602 				r = gmt_M_memory (GMT, NULL, n_seg = 2, double);
9603 				r[0] = radius_i;	r[1] = radius_o;
9604 			}
9605 			else {
9606 				r = gmt_M_memory (GMT, NULL, n_seg = 1, double);
9607 				r[0] = radius_o;
9608 			}
9609 		}
9610 
9611 		d_az = -d_az;	/* Since we have a right-handed rotation but gave azimuths */
9612 		for (k = 0; k < n_seg; k++) {
9613 			S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, n_arc, 2, NULL, NULL);
9614 			gmtplot_get_far_point (GMT, xlon, xlat, r[k], P_o);
9615 			rot_start = -az_start;	/* Since we have a right-handed rotation but gave azimuths */
9616 			for (ko = 0; ko < n_arc; ko++) {
9617 				az = rot_start + ko * d_az;
9618 				gmt_make_rot_matrix2 (GMT, E, az, R);
9619 				gmt_matrix_vect_mult (GMT, 3U, R, P_o, Q);
9620 				gmt_cart_to_geo (GMT, &S->data[GMT_Y][ko], &S->data[GMT_X][ko], Q, true);	/* Rotated outer point */
9621 			}
9622 			if ((n_new = gmt_fix_up_path (GMT, &S->data[GMT_X], &S->data[GMT_Y], S->n_rows, 0.0, 0)) == 0) {
9623 				gmt_free_segment (GMT, &S);
9624 				continue;
9625 			}
9626 			S->n_rows = n_new;
9627 			if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows)) == 0) continue;
9628 			gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
9629 			gmt_free_segment (GMT, &S);
9630 		}
9631 		gmt_M_free (GMT, r);
9632 	}
9633 	PSL_command (GMT->PSL, "U\n");	/* End block where spiderpen is active; this resets previous pen (for outline) */
9634 }
9635 
gmtplot_geo_wedge_fill(struct GMT_CTRL * GMT,double xlon,double xlat,double radius_i,double radius_o,double az_start,double az_stop,bool fill,bool outline)9636 GMT_LOCAL void gmtplot_geo_wedge_fill (struct GMT_CTRL *GMT, double xlon, double xlat, double radius_i, double radius_o, double az_start, double az_stop, bool fill, bool outline) {
9637 	/* gmt_geo_wedge takes the location, radius_i (in unit), radius_o (in unit), and start/stop azimuths of an geo-wedge
9638 		and draws an approximate circluar wedge using N-sided polygon.
9639 		mode = 3: Draw the entire closed wedge.
9640 		mode = 1: Just draw arc [no fill possible]
9641 		mode = 2: Just draw jaw [no fill possible]
9642 	*/
9643 
9644 	int n_arc, k, ki, ko, n_path;
9645 	uint64_t n_new;
9646 	bool windshield = (radius_i > 0.0);	/* Windshield plot */
9647 	double d_az, px, py, qx, qy, L, plon_o, plat_o, qlon, qlat, az, rot_start, E[3], P_o[3], P_i[3], Q[3], R[3][3];
9648 	struct GMT_DATASEGMENT *S = NULL;
9649 
9650 	radius_i = gmtlib_conv_distance (GMT, radius_i, 'k', 'd');	/* Convert inner radius from km to degrees */
9651 	radius_o = gmtlib_conv_distance (GMT, radius_o, 'k', 'd');	/* Convert outer radius from km to degrees */
9652 
9653 	gmt_geo_to_cart (GMT, xlat, xlon, E, true);	/* Euler rotation pole at point location */
9654 
9655 	/* Get a point P_o that is radius_o degrees away along the meridian through our point X */
9656 	gmtplot_get_far_point (GMT, xlon, xlat, radius_o, P_o);
9657 	if (windshield)	/* Get a point P_i that is radius_i degrees away along the meridian through our point X */
9658 		gmtplot_get_far_point (GMT, xlon, xlat, radius_i, P_i);
9659 	/* Compute distance between P_o and Q and compare to map_line_step to determine azimuthal sampling */
9660 	plat_o = xlat + radius_o;
9661 	plon_o = xlon;
9662 	if (plat_o > 90.0) {	/* Went over the pole */
9663 		plon_o += 180.0;
9664 		plat_o = 180 - plat_o;
9665 	}
9666 	gmt_make_rot_matrix2 (GMT, E, 1.0, R);		/* Test point 1 degree away */
9667 	gmt_matrix_vect_mult (GMT, 3U, R, P_o, Q);	/* Get Q = R * P_o */
9668 	gmt_cart_to_geo (GMT, &qlat, &qlon, Q, true);	/* Coordinates of rotated point */
9669 	gmt_geo_to_xy (GMT, plon_o, plat_o, &px, &py);	/* P_o projected on map */
9670 	gmt_geo_to_xy (GMT, qlon, qlat, &qx, &qy);	/* Q projected on map */
9671 	L = hypot (px - qx, py - qy);			/* Distance in inches along arc for 1 degree of azimuth change */
9672 	/* Estimate how many intermediate points by dividing the estinate arc length by line_step */
9673 	n_arc = MAX (2, irint (fabs (az_stop - az_start) * L /GMT->current.setting.map_line_step));
9674 	d_az = (az_stop - az_start) / (n_arc - 1);	/* Azimuthal sampling rate */
9675 	n_path = n_arc;			/* Total number of points along outer arc */
9676 	if (windshield)	/* Need points along inner arc */
9677 		n_path *= 2;
9678 	else	/* Just place apex */
9679 		n_path++;
9680 	n_path++;	/* Closed the polygon */
9681 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Wedge will be approximated by %d-sided polygon\n", n_path);
9682 	S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, n_path, 2, NULL, NULL);	/* Polygon array */
9683 	rot_start = -az_start;	/* Since we have a right-handed rotation but gave azimuths */
9684 	d_az = -d_az;		/* Same reason */
9685 	for (k = ko = 0; k < n_arc; k++, ko++) {
9686 		az = rot_start + k * d_az;
9687 		gmt_make_rot_matrix2 (GMT, E, az, R);
9688 		gmt_matrix_vect_mult (GMT, 3U, R, P_o, Q);
9689 		gmt_cart_to_geo (GMT, &S->data[GMT_Y][ko], &S->data[GMT_X][ko], Q, true);	/* Rotated outer point */
9690 		if (windshield) {	/* Get the corresponding point on the inner arc but offset its index */
9691 			gmt_matrix_vect_mult (GMT, 3U, R, P_i, Q);
9692 			ki = n_path - 2 - ko;	/* Point index for this angle on the return arc */
9693 			gmt_cart_to_geo (GMT, &S->data[GMT_Y][ki], &S->data[GMT_X][ki], Q, true);	/* Rotated inner point */
9694 		}
9695 	}
9696 	if (!windshield)	/* Add apex of wedge */
9697 		S->data[GMT_X][ko] = xlon, S->data[GMT_Y][ko++] = xlat;
9698 	/* Close polygon */
9699 	S->data[GMT_X][n_path-1] = S->data[GMT_X][0];	S->data[GMT_Y][n_path-1] = S->data[GMT_Y][0];
9700 	/* Resample along great circles */
9701 	if ((n_new = gmt_fix_up_path (GMT, &S->data[GMT_X], &S->data[GMT_Y], S->n_rows, 0.0, 0)) == 0) {
9702 		gmt_free_segment (GMT, &S);
9703 		return;
9704 	}
9705 	S->n_rows = n_new;
9706 	gmt_set_seg_minmax (GMT, GMT_IS_POLY, 2, S);	/* Update min/max of x/y only */
9707 
9708 	if (fill && outline) {
9709 		PSL_comment (GMT->PSL, "Drawing Wedge fill and outline\n");
9710 		gmt_geo_polygons (GMT, S);
9711 	}
9712 	else if (fill) {
9713 		PSL_comment (GMT->PSL, "Drawing Wedge fill only\n");
9714 		PSL_command (GMT->PSL, "V O0\n");	/* Temporarily disable outlines */
9715 		gmt_geo_polygons (GMT, S);
9716 		PSL_command (GMT->PSL, "U\n");		/* Undo */
9717 	}
9718 	else if (outline) {
9719 		PSL_comment (GMT->PSL, "Drawing Wedge outline only\n");
9720 		if ((GMT->current.plot.n = gmt_geo_to_xy_line (GMT, S->data[GMT_X], S->data[GMT_Y], S->n_rows)))
9721 			gmt_plot_line (GMT, GMT->current.plot.x, GMT->current.plot.y, GMT->current.plot.pen, GMT->current.plot.n, PSL_LINEAR);
9722 	}
9723 
9724 	gmt_free_segment (GMT, &S);
9725 }
9726 
gmt_geo_wedge(struct GMT_CTRL * GMT,double xlon,double xlat,double radius_i,double radius_o,double dr,double az_start,double az_stop,double da,unsigned int wmode,bool fill,bool outline)9727 void gmt_geo_wedge (struct GMT_CTRL *GMT, double xlon, double xlat, double radius_i, double radius_o, double dr, double az_start, double az_stop, double da, unsigned int wmode, bool fill, bool outline) {
9728 	/* gmt_geo_wedge takes the location, radius_i (in km), radius_o (in km), dr (in km) and start/stop/da azimuths of an geo-wedge
9729 		and draws an approximate circular wedge or windshield using N-sided polygon.
9730 		If fill is true then we paint this polygon.
9731 		If mode is not GMT_WEDGE_NORMAL then we draw the spider web on top.
9732 		The form of that depends on dr > 0 and da > 0, otherwise we just do the outline.
9733 	*/
9734 
9735 	enum GMT_enum_wedgetype mode = wmode;
9736 
9737 	if (mode == GMT_WEDGE_NORMAL) {	/* Just a regular filled/outlined geowedge */
9738 		gmtplot_geo_wedge_fill (GMT, xlon, xlat, radius_i, radius_o, az_start, az_stop, fill, outline);
9739 		return;
9740 	}
9741 
9742 	/* Here we need to lay down fill first (if active), then spider web, then wedge outline (if active) */
9743 
9744 	if (fill) gmtplot_geo_wedge_fill (GMT, xlon, xlat, radius_i, radius_o, az_start, az_stop, true, false);	/* Just fill (if active) */
9745 	gmtplot_geo_spider (GMT, xlon, xlat, radius_i, radius_o, dr, az_start, az_stop, da, wmode);			/* Spiderweb */
9746 	if (outline) gmtplot_geo_wedge_fill (GMT, xlon, xlat, radius_i, radius_o, az_start, az_stop, false, true);	/* Just outline (if active) */
9747 }
9748 
gmt_geo_vector(struct GMT_CTRL * GMT,double lon0,double lat0,double azimuth,double length,struct GMT_PEN * pen,struct GMT_SYMBOL * S)9749 unsigned int gmt_geo_vector (struct GMT_CTRL *GMT, double lon0, double lat0, double azimuth, double length, struct GMT_PEN *pen, struct GMT_SYMBOL *S) {
9750 	/* gmt_geo_vector takes the location lon0, lat0, azimuth of the vector at that point, and the
9751 		length (in km), and and draws the vector using the chosen map projection.  If arrow heads
9752 		have been requested we compute an arc length in degrees that is equivalent to the chosen
9753 		symbol size.  With arrow heads we also shorten the vector arc so that unfilled vector heads
9754 		are possible. If a small-circle vector is chosen then azimuth, length may be opening angles
9755 		1 and 2 if PSL_VEC_ANGLES is set as well. */
9756 	unsigned int warn;
9757 	if ((S->v.status & PSL_VEC_SCALE) == 0) {	/* Must determine the best inch to degree scale for this map */
9758 		if (gmt_M_is_perspective (GMT)) {
9759 			double clon, clat;
9760 			gmt_xy_to_geo (GMT, &clon, &clat, GMT->current.map.half_width, GMT->current.map.half_height);	/* Geographic coordinates of middle map point */
9761 			S->v.scale = gmtplot_inch_to_degree_scale (GMT, clon, clat, azimuth);
9762 			S->v.status |= PSL_VEC_SCALE;
9763 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Vector stem scale is %g degrees/inch at (%g, %g) for az = %g\n", S->v.scale, clon, clat, azimuth);
9764 		}
9765 		else {	/* Set scale each time locally */
9766 			S->v.scale = gmtplot_inch_to_degree_scale (GMT, lon0, lat0, azimuth);
9767 			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Vector stem scale is %g degrees/inch at (%g, %g) for az = %g\n", S->v.scale, lon0, lat0, azimuth);
9768 		}
9769 	}
9770 
9771 	if (S->v.status & PSL_VEC_POLE)
9772 		warn = gmtplot_geo_vector_smallcircle (GMT, lon0, lat0, azimuth, length, pen, S);
9773 	else
9774 		warn = gmtplot_geo_vector_greatcircle (GMT, lon0, lat0, azimuth, length, pen, S);
9775 	return (warn);
9776 }
9777 
gmt_geo_rectangle(struct GMT_CTRL * GMT,double lon,double lat,double width,double height,double azimuth)9778 void gmt_geo_rectangle (struct GMT_CTRL *GMT, double lon, double lat, double width, double height, double azimuth) {
9779 	/* gmt_geo_rectangle takes the location, axes (in km), and azimuth of a rectangle
9780 		and draws the rectangle using the chosen map projection */
9781 
9782 	int jump;
9783 	double sin_azimuth, cos_azimuth, sinp, cosp, x, y, x_prime, y_prime, rho, c, dim[3];
9784 	double sin_c, cos_c, center, lon_w, lat_w, lon_h, lat_h, xp, yp, xw, yw, xh, yh;
9785 	struct PSL_CTRL *PSL= GMT->PSL;
9786 
9787 	azimuth = gmt_azim_to_angle (GMT, lon, lat, 0.1, azimuth);
9788 	gmt_geo_to_xy (GMT, lon, lat, &xp, &yp);		/* Center of rectangle */
9789 
9790 	width *= 500.0, height *= 500.0;	/* Convert to meters and get half the size */
9791 	dim[0] = azimuth;
9792 	sincosd (azimuth, &sin_azimuth, &cos_azimuth);
9793 	sincosd (lat, &sinp, &cosp);		/* Set up azimuthal equidistant projection */
9794 
9795 	center = (GMT->current.proj.central_meridian < GMT->common.R.wesn[XLO] || GMT->current.proj.central_meridian > GMT->common.R.wesn[XHI]) ? 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]) : GMT->current.proj.central_meridian;
9796 
9797 	/* Get first point width away from center */
9798 	sincos (0.0, &y, &x);
9799 	x *= width;
9800 	y *= height;
9801 	/* Get rotated coordinates in m */
9802 	x_prime = x * cos_azimuth - y * sin_azimuth;
9803 	y_prime = x * sin_azimuth + y * cos_azimuth;
9804 	/* Convert m back to lon lat */
9805 	rho = hypot (x_prime, y_prime);
9806 	c = rho / GMT->current.proj.EQ_RAD;
9807 	sincos (c, &sin_c, &cos_c);
9808 	lat_w = d_asind (cos_c * sinp + (y_prime * sin_c * cosp / rho));
9809 	if ((lat - 90.0) > -GMT_CONV8_LIMIT)	/* origin in Northern hemisphere */
9810 		lon_w = lon + d_atan2d (x_prime, -y_prime);
9811 	else if ((lat + 90.0) < GMT_CONV8_LIMIT)	/* origin in Southern hemisphere */
9812 		lon_w = lon + d_atan2d (x_prime, y_prime);
9813 	else
9814 		lon_w = lon + d_atan2d (x_prime * sin_c, (rho * cosp * cos_c - y_prime * sinp * sin_c));
9815 	while ((lon_w - center) < -180.0) lon_w += 360.0;
9816 	while ((lon_w - center) > +180.0) lon_w -= 360.0;
9817 	gmt_geo_to_xy (GMT, lon_w, lat_w, &xw, &yw);	/* Get projected x,y coordinates */
9818 	if ((jump = (*GMT->current.map.jump) (GMT, xp, yp, xw, yw)))	/* Adjust for map jumps */
9819 		xw += jump * 2.0 * gmt_half_map_width (GMT, yp);
9820 	dim[1] = 2.0 * hypot (xp - xw, yp - yw);	/* Estimate of rectangle width in plot units (inch) */
9821 	/* Get 2nd point height away from center */
9822 	sincos (M_PI_2, &y, &x);
9823 	x *= width;
9824 	y *= height;
9825 	/* Get rotated coordinates in m */
9826 	x_prime = x * cos_azimuth - y * sin_azimuth;
9827 	y_prime = x * sin_azimuth + y * cos_azimuth;
9828 	/* Convert m back to lon lat */
9829 	rho = hypot (x_prime, y_prime);
9830 	c = rho / GMT->current.proj.EQ_RAD;
9831 	sincos (c, &sin_c, &cos_c);
9832 	lat_h = d_asind (cos_c * sinp + (y_prime * sin_c * cosp / rho));
9833 	if ((lat - 90.0) > -GMT_CONV8_LIMIT)	/* origin in Northern hemisphere */
9834 		lon_h = lon + d_atan2d (x_prime, -y_prime);
9835 	else if ((lat + 90.0) < GMT_CONV8_LIMIT)	/* origin in Southern hemisphere */
9836 		lon_h = lon + d_atan2d (x_prime, y_prime);
9837 	else
9838 		lon_h = lon + d_atan2d (x_prime * sin_c, (rho * cosp * cos_c - y_prime * sinp * sin_c));
9839 	while ((lon_h - center) < -180.0) lon_h += 360.0;
9840 	while ((lon_h - center) > +180.0) lon_h -= 360.0;
9841 	gmt_geo_to_xy (GMT, lon_h, lat_h, &xh, &yh);
9842 	if ((jump = (*GMT->current.map.jump) (GMT, xp, yp, xh, yh)))	/* Adjust for map jumps */
9843 		xh += jump * 2.0 * gmt_half_map_width (GMT, yp);
9844 	dim[2] = 2.0 * hypot (xp - xh, yp - yh);	/* Estimate of rectangle width in plot units (inch) */
9845 	PSL_plotsymbol (PSL, xp, yp, dim, PSL_ROTRECT);
9846 }
9847 
9848 #define M_PI_3          1.047197551196598	/* 60 degrees in radians */
9849 
gmt_draw_front(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,struct GMT_FRONTLINE * f)9850 void gmt_draw_front (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, struct GMT_FRONTLINE *f) {
9851 	int ngap, tmp_join = 0, tmp_limit = 0;
9852 	bool skip;
9853 	uint64_t i;
9854 	double *s = NULL, xx[6], yy[6], dist = 0.0, w, frac, dx, dy, angle, dir1, dir2;
9855 	double gap, x0, y0, xp, yp, len2, len3, len4, cosa, sina, sa, ca, offx, offy, dim[PSL_MAX_DIMS];
9856 	double a1, a2, sina1, sina2, cosa1, cosa2;
9857 	struct PSL_CTRL *PSL= GMT->PSL;
9858 
9859 	if (n < 2) return;
9860 
9861 	gmt_M_memset (dim, PSL_MAX_DIMS, double);
9862 	s = gmt_M_memory (GMT, NULL, n, double);
9863 	for (i = 1, s[0] = 0.0; i < n; i++) {
9864 		/* Watch out for longitude wraps */
9865 		dx = x[i] - x[i-1];
9866 		w = gmt_half_map_width (GMT, y[i]);
9867 		if (GMT->current.map.is_world && fabs (dx) > w) dx = copysign (2.0 * w - fabs (dx), -dx);
9868 		s[i] = s[i-1] + hypot (dx, y[i] - y[i-1]);
9869 	}
9870 
9871 	if (f->f_gap > 0.0) {	/* Gave positive interval */
9872 		if (f->f_exact) {	/* Use gap exactly as given, not worry about ending line with tick */
9873 			ngap = floor ((s[n-1] * (1.0 + GMT_CONV6_LIMIT)) / f->f_gap);	/* Allow 1 ppm noise since we use floor */
9874 			gap = f->f_gap;	/* As given */
9875 		}
9876 		else {	/* Adjust so we start and end with a tick on each line */
9877 			ngap = irint (s[n-1] / f->f_gap);
9878 			gap = s[n-1] / ngap;	/* Adjust gap to fit line length */
9879 			ngap++;
9880 		}
9881 		dist = f->f_off;	/* Start off at the offset distance [0] */
9882 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Given gap: %g Adjusted gap: %g Number of front gaps: %d\n", f->f_gap, gap, ngap);
9883 	}
9884 	else {	/* Gave negative interval which means the # of ticks required */
9885 		ngap = irint (fabs (f->f_gap));
9886 		if (ngap == 0) {	/* Cannot happen but might as well leave the test in case of snafus */
9887 			GMT_Report (GMT->parent, GMT_MSG_WARNING, "Number of front ticks reset from 0 to 1 (check your arguments)\n");
9888 			ngap = 1;
9889 		}
9890 		if (ngap == 1)	/* Single centered tick */
9891 			dist = 0.5 * s[n-1], gap = s[n-1];
9892 		else		/* Equidistantly spaced tick starting at 1st point and ending at last */
9893 			gap = s[n-1] / (ngap - 1);
9894 		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Given number of front gaps: %d Computed gap: %g\n", ngap, gap);
9895 	}
9896 
9897 	PSL_command (GMT->PSL, "V\n");	/* In case we change the graphic state regarding pens */
9898 
9899 	len2 = 0.5 * f->f_len;
9900 	len4 = 0.25 * f->f_len;
9901 	len3 = 0.866025404 * f->f_len;
9902 	if (f->f_sense == GMT_FRONT_CENTERED) len3 = len2;
9903 	if (f->f_symbol) {	/* Temporarily use miter to get sharp points at slip vectors */
9904 		tmp_join = PSL->internal.line_join;	PSL_setlinejoin (PSL, 0);
9905 		tmp_limit = PSL->internal.miter_limit;	PSL_setmiterlimit (PSL, 0);
9906 	}
9907 	if (f->f_pen == 1) gmt_setpen (GMT, &f->pen);	/* Set alternate symbol pen */
9908 	i = 0;
9909 	while (i < n) {
9910 		while ((s[i] - dist) > -GMT_CONV4_LIMIT) {	/* Time for tick */
9911 			if (i > 0) {
9912 				dx = x[i] - x[i-1];
9913 				dy = y[i] - y[i-1];
9914 			}
9915 			else {
9916 				dx = x[1] - x[0];
9917 				dy = y[1] - y[0];
9918 			}
9919 			if (fabs (dist - s[i]) < GMT_CONV4_LIMIT) {
9920 				x0 = x[i];
9921 				y0 = y[i];
9922 			}
9923 			else {
9924 				frac = (s[i] - dist) / (s[i] - s[i-1]);
9925 				x0 = x[i] - dx * frac;
9926 				y0 = y[i] - dy * frac;
9927 			}
9928 			angle = d_atan2 (dy, dx);
9929 			skip = (GMT->current.map.is_world && fabs (dx) > gmt_half_map_width (GMT, y[i]));	/* Don't do ticks on jumps */
9930 			if (skip) {
9931 				dist += gap;	i++;
9932 				continue;
9933 			}
9934 
9935 			switch (f->f_symbol) {
9936 				case GMT_FRONT_TRIANGLE:	/* Triangle */
9937 					switch (f->f_sense) {
9938 						case GMT_FRONT_CENTERED:
9939 							sincos (angle, &sina, &cosa);
9940 							xx[0] = x0 + len2 * cosa;
9941 							yy[0] = y0 + len2 * sina;
9942 							xx[1] = x0 - len3 * sina;
9943 							yy[1] = y0 + len3 * cosa;
9944 							xx[2] = x0 - len2 * cosa;
9945 							yy[2] = y0 - len2 * sina;
9946 							xx[3] = x0 + len3 * sina;
9947 							yy[3] = y0 - len3 * cosa;
9948 							PSL_plotpolygon (PSL, xx, yy, 4);
9949 							break;
9950 						case GMT_FRONT_RIGHT:
9951 							angle += M_PI;
9952 							/* Intentionally fall through - after changing the angle */
9953 						case GMT_FRONT_LEFT:
9954 							sincos (angle, &sina, &cosa);
9955 							xx[0] = x0 + len2 * cosa;
9956 							yy[0] = y0 + len2 * sina;
9957 							xx[1] = x0 - len3 * sina;
9958 							yy[1] = y0 + len3 * cosa;
9959 							xx[2] = x0 - len2 * cosa;
9960 							yy[2] = y0 - len2 * sina;
9961 							PSL_plotpolygon (PSL, xx, yy, 3);
9962 							break;
9963 					}
9964 					break;
9965 
9966 				case GMT_FRONT_ITRIANGLE:    /* Inverted triangle */
9967 					a1 = angle + M_PI_3;	a2 = a1 + M_PI_3;
9968 					sincos (a1, &sina1, &cosa1);
9969 					sincos (a2, &sina2, &cosa2);
9970 					switch (f->f_sense) {
9971 						case GMT_FRONT_CENTERED:
9972 							xx[0] = x0 + f->f_len * cosa1;   /* UR */
9973 							yy[0] = y0 + f->f_len * sina1;
9974 							xx[1] = x0 + f->f_len * cosa2;   /* UL */
9975 							yy[1] = y0 + f->f_len * sina2;
9976 							xx[2] = x0;   yy[2] = y0; 		 /* MC */
9977 							xx[3] = x0 - f->f_len * cosa1;   /* LR */
9978 							yy[3] = y0 - f->f_len * sina1;
9979 							xx[4] = x0 - f->f_len * cosa2;   /* LL */
9980 							yy[4] = y0 - f->f_len * sina2;
9981 							xx[5] = x0;   yy[5] = y0; 		 /* MC */
9982 							PSL_plotpolygon (PSL, xx, yy, 6);
9983 							break;
9984 						case GMT_FRONT_RIGHT:
9985 							sina1 = -sina1;	cosa1 = -cosa1;	sina2 = -sina2;	cosa2 = -cosa2;
9986 							/* Intentionally fall through - after changing the angle */
9987 						case GMT_FRONT_LEFT:
9988 							xx[0] = x0 + f->f_len * cosa1;   /* UR */
9989 							yy[0] = y0 + f->f_len * sina1;
9990 							xx[1] = x0 + f->f_len * cosa2;   /* UL */
9991 							yy[1] = y0 + f->f_len * sina2;
9992 							xx[2] = x0;   yy[2] = y0; 		 /* MC */
9993 							PSL_plotpolygon (PSL, xx, yy, 3);
9994 							break;
9995 					}
9996 					break;
9997 
9998 				case GMT_FRONT_CIRCLE:	/* Circles */
9999 					switch (f->f_sense) {
10000 						case GMT_FRONT_CENTERED:
10001 							PSL_plotsymbol (PSL, x0, y0, &(f->f_len), PSL_CIRCLE);
10002 							break;
10003 						case GMT_FRONT_RIGHT:
10004 							angle += M_PI;
10005 							/* Intentionally fall through - after changing the angle */
10006 						case GMT_FRONT_LEFT:
10007 							dir1 = R2D * angle;
10008 							dir2 = dir1 + 180.0;
10009 							if (dir1 > dir2) dir1 -= 360.0;
10010 							dim[0] = len2, dim[1] = dir1, dim[2] = dir2;	dim[7] = 3;
10011 							PSL_plotsymbol (PSL, x0, y0, dim, PSL_WEDGE);
10012 							break;
10013 					}
10014 					break;
10015 
10016 				case GMT_FRONT_BOX:	/* Squares */
10017 					switch (f->f_sense) {
10018 						case GMT_FRONT_CENTERED:	/* Full square centered on line */
10019 							sincos (angle, &sina, &cosa);
10020 							xx[0] = x0 + len2 * (cosa + sina);	/* LR */
10021 							yy[0] = y0 + len2 * (sina - cosa);
10022 							xx[1] = x0 + len2 * (cosa - sina);	/* UR */
10023 							yy[1] = y0 + len2 * (sina + cosa);
10024 							xx[2] = x0 + len2 * (-cosa - sina);	/* UL */
10025 							yy[2] = y0 + len2 * (-sina + cosa);
10026 							xx[3] = x0 + len2 * (-cosa + sina);	/* LL */
10027 							yy[3] = y0 + len2 * (-sina - cosa);
10028 							break;
10029 						case GMT_FRONT_RIGHT:
10030 							angle += M_PI;
10031 							/* Intentionally fall through - after changing the angle */
10032 						case GMT_FRONT_LEFT:
10033 							/* Half square on the chosen side */
10034 							sincos (angle, &sina, &cosa);
10035 							xx[0] = x0 + len2 * (cosa);	/* LR */
10036 							yy[0] = y0 + len2 * (sina);
10037 							xx[1] = x0 + len2 * (cosa - sina);	/* UR */
10038 							yy[1] = y0 + len2 * (sina + cosa);
10039 							xx[2] = x0 + len2 * (-cosa - sina);	/* UL */
10040 							yy[2] = y0 + len2 * (-sina + cosa);
10041 							xx[3] = x0 + len2 * (-cosa);	/* LL */
10042 							yy[3] = y0 + len2 * (-sina);
10043 							break;
10044 					}
10045 					PSL_plotpolygon (PSL, xx, yy, 4);
10046 					break;
10047 
10048 				case GMT_FRONT_SLIP: /* draw strike-slip arrows */
10049 					sincos (angle, &sina, &cosa);
10050 					offx = GMT->current.setting.map_annot_offset[GMT_PRIMARY] * sina; /* get offsets from front line */
10051 					offy = GMT->current.setting.map_annot_offset[GMT_PRIMARY] * cosa;
10052 					/* sense == GMT_FRONT_LEFT == left-lateral, R_RIGHT = right lateral */
10053 					/* arrow "above" line */
10054 					sincos (angle + (f->f_sense * f->f_angle * D2R), &sa, &ca);
10055 					xp = x0 + f->f_sense * offx;
10056 					yp = y0 - f->f_sense * offy;
10057 					xx[0] = xp - len2 * cosa;
10058 					yy[0] = yp - len2 * sina;
10059 					xx[1] = xp + len2 * cosa;
10060 					yy[1] = yp + len2 * sina;
10061 					xx[2] = xx[1] - len4 * ca;
10062 					yy[2] = yy[1] - len4 * sa;
10063 					PSL_plotline (PSL, xx, yy, 3, PSL_MOVE|PSL_STROKE);
10064 
10065 					/* arrow "below" line */
10066 					sincos (angle - (f->f_sense * (180.0 - f->f_angle) * D2R), &sa, &ca);
10067 					xp = x0 - f->f_sense * offx;
10068 					yp = y0 + f->f_sense * offy;
10069 					xx[0] = xp + len2 * cosa;
10070 					yy[0] = yp + len2 * sina;
10071 					xx[1] = xp - len2 * cosa;
10072 					yy[1] = yp - len2 * sina;
10073 					xx[2] = xx[1] - len4 * ca;
10074 					yy[2] = yy[1] - len4 * sa;
10075 					PSL_plotline (PSL, xx, yy, 3, PSL_MOVE|PSL_STROKE);
10076 					break;
10077 
10078 				case GMT_FRONT_SLIPC: /* Draw curved strike-slip arrows a la USGS */
10079 					PSL_command (PSL, "V ");	/* Place symbol under gsave/grestore since we will translate/rotate */
10080 					PSL_setorigin (PSL, x0, y0, R2D * angle, PSL_FWD);
10081 					offy = GMT->current.setting.map_annot_offset[GMT_PRIMARY]; /* get offset from front line */
10082 					/* sense == GMT_FRONT_LEFT == left-lateral, R_RIGHT = right lateral */
10083 					/* arrow "above" line */
10084 					xx[0] = f->f_sense* len2;	xx[1] = -f->f_sense*len2;
10085 					yy[0] = yy[1] = offy;
10086 					PSL_plotline (PSL, xx, yy, 2, PSL_MOVE);
10087 					PSL_plotarc (PSL, xx[1], offy + 0.4 * f->f_len, 0.4 * f->f_len, 270.0, 270.0+f->f_sense*f->f_angle, PSL_STROKE);
10088 
10089 					/* arrow "below" line */
10090 					xx[0] = -f->f_sense*len2;	xx[1] = f->f_sense*len2;
10091 					yy[0] = yy[1] = -offy;
10092 					PSL_plotline (PSL, xx, yy, 2, PSL_MOVE);
10093 					PSL_plotarc (PSL, xx[1], -offy - 0.4 * f->f_len, 0.4 * f->f_len, 90.0, 90.0+f->f_sense*f->f_angle, PSL_STROKE);
10094 					PSL_command (PSL, "U\n");
10095 					break;
10096 
10097 				case GMT_FRONT_FAULT:	/* Normal fault ticks */
10098 					xx[0] = xx[1] = x0, yy[0] = yy[1] = y0;
10099 					if (f->f_sense == GMT_FRONT_CENTERED) {
10100 						angle -= M_PI_2;
10101 						sincos (angle, &sina, &cosa);
10102 						xx[0] += len2 * cosa;
10103 						yy[0] += len2 * sina;
10104 						xx[1] -= len2 * cosa;
10105 						yy[1] -= len2 * sina;
10106 					}
10107 					else {
10108 						angle += (f->f_sense * M_PI_2);
10109 						sincos (angle, &sina, &cosa);
10110 						xx[1] += len2 * cosa;
10111 						yy[1] += len2 * sina;
10112 					}
10113 					PSL_plotline (PSL, xx, yy, 2, PSL_MOVE|PSL_STROKE);
10114 					break;
10115 			}
10116 			dist += gap;
10117 		}
10118 		i++;
10119 	}
10120 	gmt_M_free (GMT, s);
10121 	if (f->f_symbol) {	/* Switch line join style back */
10122 		PSL_setlinejoin (PSL, tmp_join);
10123 		PSL_setmiterlimit (PSL, tmp_limit);
10124 	}
10125 	PSL_command (GMT->PSL, "U\n");
10126 }
10127 
gmt_plane_perspective(struct GMT_CTRL * GMT,int plane,double level)10128 void gmt_plane_perspective (struct GMT_CTRL *GMT, int plane, double level) {
10129 	/* This routine write the PostScript code to change any following matter printed in the plane
10130 	 * of the paper into a perspective view of that plane based on the GMT->current.proj.z_project
10131 	 * parameters (azimuth and elevation).
10132 	 * The plane is portrayed as a plane of constant X, Y, or Z.
10133 	 * Input arguments:
10134 	 * GMT	: The GMT struct
10135 	 * PSL	: The PSL struct
10136 	 * plane: The perspective plane if a constant X, Y, or Z (GMT_X = 0, GMT_Y = 1, GMT_Z = 2)
10137 	 *        To indicate that the z-level is not in projected but "world" coordinates, add GMT_ZW = 3
10138 	 *        To reset to normal printing, use -1.
10139 	 * level: Level of X, Y, or Z in projected coordinates (inch).
10140 	 */
10141 	double a, b, c, d, e, f;
10142 	struct PSL_CTRL *PSL= GMT->PSL;
10143 
10144 	/* Only do this in 3D mode */
10145 	if (!GMT->current.proj.three_D) return;
10146 
10147 	/* Only do this at top module */
10148 	if (GMT->hidden.func_level > GMT_TOP_MODULE) return;
10149 
10150 	/* Nothing changed since last call, hence ignore */
10151 	if (plane == GMT->current.proj.z_project.plane && gmt_M_eq(level,GMT->current.proj.z_project.level)) return;
10152 
10153 	/* Store value of level (store plane at end) */
10154 	GMT->current.proj.z_project.level = level;
10155 
10156 	/* Concat contains the proper derivatives of these functions:
10157 	x_out = - x * GMT->current.proj.z_project.cos_az + y * GMT->current.proj.z_project.sin_az + GMT->current.proj.z_project.x_off;
10158 	y_out = - (x * GMT->current.proj.z_project.sin_az + y * GMT->current.proj.z_project.cos_az) *
10159 		GMT->current.proj.z_project.sin_el + z * GMT->current.proj.z_project.cos_el + GMT->current.proj.z_project.y_off;
10160 	*/
10161 
10162 	a = b = c = d = e = f = 0.0;
10163 	if (plane < 0)			/* Reset to original matrix */
10164 		PSL_command (PSL, "PSL_GPP setmatrix\n");
10165 	else {	/* New perspective plane: compute all derivatives and use full matrix */
10166 		if (plane >= GMT_ZW) level = gmt_z_to_zz (GMT, level);	/* First convert world z coordinate to projected z coordinate */
10167 		switch (plane % 3) {
10168 			case GMT_X:	/* Constant x, Convert y,z to x',y' */
10169 				a = GMT->current.proj.z_project.sin_az;
10170 				b = -GMT->current.proj.z_project.cos_az * GMT->current.proj.z_project.sin_el;
10171 				c = 0.0;
10172 				d = GMT->current.proj.z_project.cos_el;
10173 				e = GMT->current.proj.z_project.x_off - level * GMT->current.proj.z_project.cos_az;
10174 				f = GMT->current.proj.z_project.y_off - level * GMT->current.proj.z_project.sin_az * GMT->current.proj.z_project.sin_el;
10175 				break;
10176 			case GMT_Y:	/* Constant y. Convert x,z to x',y' */
10177 				a = -GMT->current.proj.z_project.cos_az;
10178 				b = -GMT->current.proj.z_project.sin_az * GMT->current.proj.z_project.sin_el;
10179 				c = 0.0;
10180 				d = GMT->current.proj.z_project.cos_el;
10181 				e = GMT->current.proj.z_project.x_off + level * GMT->current.proj.z_project.sin_az;
10182 				f = GMT->current.proj.z_project.y_off - level * GMT->current.proj.z_project.cos_az * GMT->current.proj.z_project.sin_el;
10183 				break;
10184 			case GMT_Z:	/* Constant z. Convert x,y to x',y' */
10185 				a = -GMT->current.proj.z_project.cos_az;
10186 				b = -GMT->current.proj.z_project.sin_az * GMT->current.proj.z_project.sin_el;
10187 				c = GMT->current.proj.z_project.sin_az;
10188 				d = -GMT->current.proj.z_project.cos_az * GMT->current.proj.z_project.sin_el;
10189 				e = GMT->current.proj.z_project.x_off;
10190 				f = GMT->current.proj.z_project.y_off + level * GMT->current.proj.z_project.cos_el;
10191 				break;
10192 		}
10193 
10194 		/* First restore the old matrix or save the old one when that was not done before */
10195 		PSL_command (PSL, "%s [%.12g %.12g %.12g %.12g %.12g %.12g] concat\n",
10196 			(GMT->current.proj.z_project.plane >= 0) ? "PSL_GPP setmatrix" : "/PSL_GPP matrix currentmatrix def",
10197 			a, b, c, d, e * PSL->internal.x2ix, f * PSL->internal.y2iy);
10198 	}
10199 
10200 	/* Store value of plane */
10201 	GMT->current.proj.z_project.plane = plane;
10202 }
10203 
10204 /* Creation of hidden PS0 filename used under modern GMT mode */
10205 
10206 /*! . */
gmt_set_psfilename(struct GMT_CTRL * GMT)10207 int gmt_set_psfilename (struct GMT_CTRL *GMT) {
10208 	int k;
10209 	/* Returns 0 or 1 if successful and -1 if failure */
10210 	GMT->current.ps.figure = gmt_get_current_figure (GMT->parent);
10211 
10212 	if (GMT->parent->gwf_dir == NULL) {	/* Use the established temp directory */
10213 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT WorkFlow directory not set!\n");
10214 		return GMT_NOTSET;
10215 	}
10216 	else
10217 		snprintf (GMT->current.ps.filename, GMT_LEN256, "%s/gmt_%d.ps-", GMT->parent->gwf_dir, GMT->current.ps.figure);
10218 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Use PS filename %s\n", GMT->current.ps.filename);
10219 	k = 1 + access (GMT->current.ps.filename, W_OK);	/* 1 = File exists (must append) or 0 (must create) */
10220 	GMT->current.ps.initialize = (k == 0);	/* False means it is an overlay and -R -J may come from history */
10221 	return k;
10222 }
10223 
10224 /*! . */
gmt_ps_append(struct GMT_CTRL * GMT,char * source,unsigned int mode,FILE * dest)10225 int gmt_ps_append (struct GMT_CTRL *GMT, char *source, unsigned int mode, FILE *dest) {
10226 	/* Append parts of the PS from source file to destination stream.  The part is decided by mode:
10227 	 * mode & 1: Include the header. [-K]
10228 	 * mode & 2: Include the trailer. [-O]
10229 	 * The middle part is always included.
10230 	 */
10231 	FILE *fp = NULL;
10232 	char buffer[GMT_BUFSIZ] = {""};
10233 	bool go = true;
10234 	if (mode == 0 || mode == 2)
10235 		fprintf (dest, "/PSL_xorig 0 def /PSL_yorig 0 def\n");	/* Reset these since we did not use -K in making these pieces */
10236 
10237 	if ((fp = fopen (source, "r")) == NULL) {
10238 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not open PostScript file %s\n", source);
10239 		return GMT_NOTSET;
10240 	}
10241 	go = (mode & 1);	/* Start output immediately if header was requested, else we wait until header has passed */
10242 	while (fgets (buffer, GMT_BUFSIZ, fp)) {
10243 		if (!strncmp (buffer, "%PSL_Begin_Trailer", 18U)) {	/* Beginning of the trailer */
10244 			go = (mode & 2);	/* true if we want the trailer */
10245 		}
10246 		if (go) fprintf (dest, "%s", buffer);	/* Write this line to output stream */
10247 		if (!strncmp (buffer, "%PSL_End_Header", 15U)) {	/* Now passed the header section */
10248 			go = true;	/* The juicy middle is always delicious */
10249 		}
10250 	}
10251 	fclose (fp);
10252 	return GMT_NOERROR;
10253 }
10254 
10255 /* All functions involved in reading, writing, duplicating GMT_POSTSCRIPT structs and their PostScript content */
10256 
10257 /*! . */
gmtlib_create_ps(struct GMT_CTRL * GMT,uint64_t length)10258 struct GMT_POSTSCRIPT * gmtlib_create_ps (struct GMT_CTRL *GMT, uint64_t length) {
10259 	/* Makes an empty GMT_POSTSCRIPT struct - If length > 0 then we also allocate the string */
10260 	struct GMT_POSTSCRIPT *P = gmt_get_postscript (GMT);
10261 	struct GMT_POSTSCRIPT_HIDDEN *PH = gmt_get_P_hidden (P);
10262 	PH->alloc_level = GMT->hidden.func_level;	/* Must be freed at this level. */
10263 	PH->id = GMT->parent->unique_var_ID++;		/* Give unique identifier */
10264 	if (length) {	/* Allocate a blank string */
10265 		P->data = gmt_M_memory (GMT, NULL, length, char);
10266 		PH->n_alloc = length;	/* But P->n_bytes = 0 since nothing was placed there */
10267 		PH->alloc_mode = GMT_ALLOC_INTERNALLY;		/* Memory can be freed by GMT. */
10268 	}
10269 	return (P);
10270 }
10271 
10272 /*! . */
gmtlib_free_ps_ptr(struct GMT_CTRL * GMT,struct GMT_POSTSCRIPT * P)10273 void gmtlib_free_ps_ptr (struct GMT_CTRL *GMT, struct GMT_POSTSCRIPT *P) {
10274 	/* Free the memory allocated to hold a PostScript plot (which is pointed to by P->data) */
10275 	unsigned k;
10276 	struct GMT_POSTSCRIPT_HIDDEN *PH = gmt_get_P_hidden (P);
10277 	if (P->data) {
10278 		if (PH->alloc_mode == GMT_ALLOC_INTERNALLY)	/* Was allocated by GMT */
10279 			gmt_M_free (GMT, P->data);
10280 		/* Note: We never need to free the array allocated inside PSL since PSL always destroys it */
10281 	}
10282 	P->data = NULL;		/* Whatever we pointed to is now longer known to P */
10283 	PH->n_alloc = P->n_bytes = 0;
10284 	/* Use free() to free the headers since they were allocated with strdup */
10285 	if (P->n_headers) {
10286 		for (k = 0; k < P->n_headers; k++) gmt_M_str_free (P->header[k]);
10287 		gmt_M_free (GMT, P->header);
10288 	}
10289 	gmt_M_free (GMT, P->hidden);
10290 	P->mode = GMT_PS_EMPTY;
10291 }
10292 
10293 /*! . */
gmtlib_free_ps(struct GMT_CTRL * GMT,struct GMT_POSTSCRIPT ** P)10294 void gmtlib_free_ps (struct GMT_CTRL *GMT, struct GMT_POSTSCRIPT **P) {
10295 	/* Free the memory allocated to hold a PostScript plot (which is pointed to by P->data) */
10296 	gmtlib_free_ps_ptr (GMT, *P);
10297 	gmt_M_free (GMT, *P);
10298 	*P = NULL;
10299 }
10300 
gmtlib_read_ps(struct GMT_CTRL * GMT,void * source,unsigned int source_type,unsigned int mode)10301 struct GMT_POSTSCRIPT * gmtlib_read_ps (struct GMT_CTRL *GMT, void *source, unsigned int source_type, unsigned int mode) {
10302 	/* Opens and reads a PostScript file.
10303 	 * Return the result as a GMT_POSTSCRIPT struct.
10304 	 * source_type can be GMT_IS_[FILE|STREAM|FDESC]
10305 	 * mode is not yet used.
10306 	 */
10307 
10308 	char ps_file[PATH_MAX] = {""}, buffer[GMT_LEN256] = {""};
10309 	int c;
10310 	bool close_file = false;
10311 	size_t n_alloc = 0;
10312 	struct GMT_POSTSCRIPT *P = NULL;
10313 	struct GMT_POSTSCRIPT_HIDDEN *PH = NULL;
10314 	FILE *fp = NULL;
10315 	gmt_M_unused(mode);
10316 
10317 	/* Determine input source */
10318 
10319 	if (source_type == GMT_IS_FILE) {	/* source is a file name */
10320 		struct stat buf;
10321 		char path[PATH_MAX] = {""};
10322 		strncpy (ps_file, source, PATH_MAX-1);
10323 		if (!gmt_getdatapath (GMT, ps_file, path, R_OK)) {
10324 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot find PostScript file %s\n", ps_file);
10325 			return (NULL);
10326 		}
10327 		if (stat (path, &buf)) {
10328 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot determine size of PostScript file %s\n", ps_file);
10329 			return (NULL);
10330 		}
10331 		if ((fp = fopen (ps_file, "r")) == NULL) {
10332 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot open PostScript file %s\n", ps_file);
10333 			return (NULL);
10334 		}
10335 		n_alloc = buf.st_size;	/* We know what to allocate here */
10336 		close_file = true;	/* We only close files we have opened here */
10337 	}
10338 	else if (source_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
10339 		fp = (FILE *)source;
10340 		if (fp == NULL) fp = GMT->session.std[GMT_IN];	/* Default input */
10341 		if (fp == GMT->session.std[GMT_IN])
10342 			strcpy (ps_file, "<stdin>");
10343 		else
10344 			strcpy (ps_file, "<input stream>");
10345 	}
10346 	else if (source_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
10347 		struct stat buf;
10348 		int *fd = source;
10349 		if (fstat (*fd, &buf)) {
10350 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot determine size of PostScript file give by file descriptor %d\n", *fd);
10351 			return (NULL);
10352 		}
10353 		if ((fp = fdopen (*fd, "r")) == NULL) {
10354 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert PostScript file descriptor %d to stream in gmtlib_read_ps\n", *fd);
10355 			return (NULL);
10356 		}
10357 		else
10358 			close_file = true;	/* Since fdopen creates a FILE struct */
10359 		n_alloc = buf.st_size;		/* We know what to allocate here */
10360 		if (fp == GMT->session.std[GMT_IN])
10361 			strcpy (ps_file, "<stdin>");
10362 		else
10363 			strcpy (ps_file, "<input file descriptor>");
10364 	}
10365 	else {
10366 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtlib_read_ps\n", source_type);
10367 		return (NULL);
10368 	}
10369 
10370 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reading PostScript from %s\n", ps_file);
10371 
10372 	P = gmt_get_postscript (GMT);
10373 	P->header = gmt_M_memory (GMT, NULL, 1, char *);
10374 	snprintf (buffer, GMT_LEN256, "PostScript read from file: %s", ps_file);
10375 	P->header[0] = strdup (buffer);
10376 	P->n_headers = 1;
10377 	if (n_alloc) P->data = gmt_M_memory (GMT, NULL, n_alloc, char);
10378 
10379 	/* Start reading PostScript from fp */
10380 
10381 	while ((c = fgetc (fp)) != EOF ) {
10382 		if (P->n_bytes >= n_alloc) {
10383 			n_alloc = (n_alloc == 0) ? GMT_INITIAL_MEM_ROW_ALLOC : n_alloc << 1;	/* Start at 2 Mb, then double */
10384 			P->data = gmt_M_memory (GMT, P->data, n_alloc, char);
10385 		}
10386 		P->data[P->n_bytes++] = (char)c;
10387 	}
10388 	if (close_file) fclose (fp);
10389 	if (P->n_bytes > n_alloc)
10390 		P->data = gmt_M_memory (GMT, P->data, P->n_bytes, char);
10391 	PH = gmt_get_P_hidden (P);
10392 	PH->n_alloc = P->n_bytes;
10393 	PH->alloc_mode = GMT_ALLOC_INTERNALLY;	/* So GMT can free the data array */
10394 	/* Determine the mode by checking for typical starts and ends of PS */
10395 	if (P->n_bytes > 4 && !strncmp (P->data, "%!PS", 4U))
10396 		P->mode = GMT_PS_HEADER;	/* Found start of PS header */
10397 	if (P->n_bytes > 10 && !strncmp (&P->data[P->n_bytes-10], "end\n%%EOF\n", 10U))
10398 		P->mode += GMT_PS_TRAILER;	/* Found end of PS trailer */
10399 	return (P);
10400 }
10401 
gmtlib_write_ps(struct GMT_CTRL * GMT,void * dest,unsigned int dest_type,unsigned int mode,struct GMT_POSTSCRIPT * P)10402 int gmtlib_write_ps (struct GMT_CTRL *GMT, void *dest, unsigned int dest_type, unsigned int mode, struct GMT_POSTSCRIPT *P) {
10403 	/* We write the PostScript file to fp [or stdout].
10404 	 * dest_type can be GMT_IS_[FILE|STREAM|FDESC]
10405 	 * mode is not used yet.
10406 	 */
10407 
10408 	bool close_file = false, append = false;
10409 	char ps_file[PATH_MAX] = {""};
10410 	static char *msg1[2] = {"Writing", "Appending"};
10411 	FILE *fp = NULL;
10412 	gmt_M_unused(mode);
10413 
10414 	if (dest_type == GMT_IS_FILE && !dest) dest_type = GMT_IS_STREAM;	/* No filename given, default to stdout */
10415 
10416 	if (dest_type == GMT_IS_FILE) {	/* dest is a file name */
10417 		static char *msg2[2] = {"create", "append to"};
10418 		strncpy (ps_file, dest, PATH_MAX-1);
10419 		append = (ps_file[0] == '>');	/* Want to append to existing file */
10420 		if ((fp = fopen (&ps_file[append], (append) ? "a" : "w")) == NULL) {
10421 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot %s PostScript file %s\n", msg2[append], &ps_file[append]);
10422 			return (GMT_ERROR_ON_FOPEN);
10423 		}
10424 		close_file = true;	/* We only close files we have opened here */
10425 	}
10426 	else if (dest_type == GMT_IS_STREAM) {	/* Open file pointer given, just copy */
10427 		fp = (FILE *)dest;
10428 		if (fp == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
10429 		if (fp == GMT->session.std[GMT_OUT])
10430 			strcpy (ps_file, "<stdout>");
10431 		else
10432 			strcpy (ps_file, "<output stream>");
10433 	}
10434 	else if (dest_type == GMT_IS_FDESC) {		/* Open file descriptor given, just convert to file pointer */
10435 		int *fd = dest;
10436 		if (fd && (fp = fdopen (*fd, "a")) == NULL) {
10437 			GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot convert PostScript file descriptor %d to stream in gmtlib_write_ps\n", *fd);
10438 			return (GMT_ERROR_ON_FDOPEN);
10439 		}
10440 		if (fd == NULL) fp = GMT->session.std[GMT_OUT];	/* Default destination */
10441 		if (fp == GMT->session.std[GMT_OUT])
10442 			strcpy (ps_file, "<stdout>");
10443 		else
10444 			strcpy (ps_file, "<output file descriptor>");
10445 		close_file = true;	/* since fdopen allocates space */
10446 	}
10447 	else {
10448 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unrecognized source type %d in gmtlib_write_ps\n", dest_type);
10449 		return (GMT_NOT_A_VALID_METHOD);
10450 	}
10451 	GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s PostScript to %s\n", msg1[append], &ps_file[append]);
10452 
10453 	/* Start writing PostScript to fp */
10454 
10455 	if (fwrite (P->data, 1U, P->n_bytes, fp) != P->n_bytes) {
10456 		GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure while %s PostScript to %s\n", msg1[append], &ps_file[append]);
10457 		if (close_file) fclose (fp);
10458 		return (GMT_DATA_WRITE_ERROR);
10459 	}
10460 	if (close_file) fclose (fp);
10461 	return (GMT_NOERROR);
10462 }
10463 
gmtplot_copy_ps(struct GMT_CTRL * GMT,struct GMT_POSTSCRIPT * P_copy,struct GMT_POSTSCRIPT * P_obj)10464 void gmtplot_copy_ps (struct GMT_CTRL *GMT, struct GMT_POSTSCRIPT *P_copy, struct GMT_POSTSCRIPT *P_obj) {
10465 	/* Just duplicate from P_obj into P_copy */
10466 	struct GMT_POSTSCRIPT_HIDDEN *PH = gmt_get_P_hidden (P_copy);
10467 	if (P_obj->n_bytes > PH->n_alloc) P_copy->data = gmt_M_memory (GMT, P_copy->data, P_obj->n_bytes, char);
10468 	gmt_M_memcpy (P_copy->data, P_obj->data, P_obj->n_bytes, char);
10469 	gmt_M_memcpy (P_copy->hidden, P_obj->hidden, 1, struct GMT_POSTSCRIPT_HIDDEN);
10470 	P_copy->mode = P_obj->mode;
10471 	PH->n_alloc = P_copy->n_bytes = P_obj->n_bytes;
10472 	PH->alloc_mode = GMT_ALLOC_INTERNALLY;	/* So GMT can free the data array */
10473 }
10474 
10475 /*! . */
gmtlib_duplicate_ps(struct GMT_CTRL * GMT,struct GMT_POSTSCRIPT * P_from,unsigned int mode)10476 struct GMT_POSTSCRIPT * gmtlib_duplicate_ps (struct GMT_CTRL *GMT, struct GMT_POSTSCRIPT *P_from, unsigned int mode) {
10477 	/* Duplicates a GMT_POSTSCRIPT structure.  Mode not used yet */
10478 	struct GMT_POSTSCRIPT *P = gmtlib_create_ps (GMT, P_from->n_bytes);
10479 	gmt_M_unused(mode);
10480 	gmtplot_copy_ps (GMT, P, P_from);
10481 	return (P);
10482 }
10483 
gmt_get_postscript(struct GMT_CTRL * GMT)10484 struct GMT_POSTSCRIPT * gmt_get_postscript (struct GMT_CTRL *GMT) {
10485 	struct GMT_POSTSCRIPT *P = NULL;
10486 	P = gmt_M_memory (GMT, NULL, 1, struct GMT_POSTSCRIPT);
10487 	P->hidden = gmt_M_memory (GMT, NULL, 1, struct GMT_POSTSCRIPT_HIDDEN);
10488 	return (P);
10489 }
10490