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