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 * G M T _ M A P . C
19 *
20 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
21 * GMT_map.c contains code related to generic coordinate transformation.
22 * For the actual projection functions, see gmt_proj.c
23 *
24 * Map Transformation Setup Routines
25 * These routines initializes the selected map transformation
26 * The names and main function are listed below
27 * NB! Note that the transformation function does not check that they are
28 * passed valid lon,lat numbers. I.e asking for log10 scaling using values
29 * <= 0 results in problems.
30 *
31 * The ellipsoid used is selectable by editing the gmt.conf in your
32 * home directory. If no such file, create one by running gmtdefaults.
33 *
34 * Usage: Initialize system by calling gmt_g (separate module), and
35 * then just use gmt_geo_to_xy() and gmt_xy_to_geo() functions.
36 *
37 * Author: Paul Wessel
38 * Date: 1-JAN-2010
39 * Version: 5
40 */
41
42 /*!
43 * \file gmt_map.c
44 * \brief gmt_map.c contains code related to generic coordinate transformation.
45 *
46 * PUBLIC GMT Functions include:
47 *
48 * gmt_azim_to_angle : Converts azimuth to angle on the map\n
49 * gmt_clip_to_map : Force polygon points to be inside map\n
50 * gmt_compact_line : Remove redundant pen movements\n
51 * gmt_geo_to_xy : Generic lon/lat to x/y\n
52 * gmt_geo_to_xy_line : Same for polygons\n
53 * gmt_geoz_to_xy : Generic 3-D lon/lat/z to x/y\n
54 * gmt_grd_project : Generalized grid projection with interpolation\n
55 * GMT_great_circle_dist : Returns great circle distance in degrees\n
56 * gmt_img_project : Generalized image projection with interpolation\n
57 * gmt_map_outside : Generic function determines if we're outside map boundary\n
58 * gmtlib_map_path : Return gmtlib_latpath or gmtlib_lonpath\n
59 * gmt_map_setup : Initialize map projection\n
60 * gmt_project_init : Initialize parameters for grid/image transformations\n
61 * gmt_xy_to_geo : Generic inverse x/y to lon/lat projection\n
62 * gmt_xyz_to_xy : Generic xyz to xy projection\n
63 *
64 * Internal GMT Functions include:
65 *
66 * gmtmap_get_origin : Find origin of projection based on pole and 2nd point\n
67 * gmtmap_get_rotate_pole : Find rotation pole based on two points on great circle\n
68 * gmtmap_ilinearxy : Inverse linear projection\n
69 * gmtmap_init_three_D : Initializes parameters needed for 3-D plots\n
70 * gmtmap_crossing : Generic function finds crossings between line and map boundary\n
71 * gmtlib_latpath : Return path between 2 points of equal latitude\n
72 * gmtlib_lonpath : Return path between 2 points of equal longitude\n
73 * gmtmap_radial_crossing : Determine map crossing in the Lambert azimuthal equal area projection\n
74 * gmtlib_left_boundary : Return left boundary in x-inches\n
75 * gmtmap_linearxy : Linear xy projection\n
76 * gmtmap_lon_inside : Accounts for wrap-around in longitudes and checks for inside\n
77 * gmtmap_ellipse_crossing : Find map crossings in the Mollweide projection\n
78 * gmtmap_move_to_rect : Move an outside point straight in to nearest edge\n
79 * gmtmap_polar_outside : Determines if a point is outside polar projection region\n
80 * gmtmap_pole_rotate_forward : Compute positions from oblique coordinates\n
81 * gmtmap_radial_clip : Clip path outside radial region\n
82 * gmtmap_radial_outside : Determine if point is outside radial region\n
83 * gmtmap_radial_overlap : Determine overlap, always true for his projection\n
84 * gmtmap_rect_clip : Clip to rectangular region\n
85 * gmtmap_rect_crossing : Find crossing between line and rect region\n
86 * gmtmap_rect_outside : Determine if point is outside rect region\n
87 * gmtmap_rect_outside2 : Determine if point is outside rect region (azimuthal proj only)\n
88 * gmtmap_rect_overlap : Determine overlap between rect regions\n
89 * gmtlib_right_boundary : Return x value of right map boundary\n
90 * gmtmap_xy_search : Find xy map boundary\n
91 * map_wesn_clip: Clip polygon to wesn boundaries\n
92 * gmtmap_wesn_crossing : Find crossing between line and lon/lat rectangle\n
93 * gmtmap_wesn_outside : Determine if a point is outside a lon/lat rectangle\n
94 * gmtmap_wesn_overlap : Determine overlap between lon/lat rectangles\n
95 * gmt_wesn_search : Search for extreme coordinates\n
96 * GMT_wrap_around_check_{x,tm} : Check if line wraps around due to Greenwich\n
97 * gmt_x_to_xx : Generic linear x projection\n
98 * map_xx_to_x : Generic inverse linear x projection\n
99 * gmt_y_to_yy : Generic linear y projection\n
100 * map_yy_to_y : Generic inverse linear y projection\n
101 * gmt_z_to_zz : Generic linear z projection\n
102 * map_zz_to_z : Generic inverse linear z projection\n
103 */
104
105 #include "gmt_dev.h"
106 #include "gmt_internals.h"
107
108 /* We put all the declaration of external functions from gmt_proj.h here since
109 * (a) they are only called in gmt_map.c so no need to go in gmt_internals.h
110 * (b) this will change completely when proj4 is relacing gmt_proj in GMT 6.
111 */
112
113 #include "gmt_proj.c"
114
115 /*! CCW order of side in some tests */
116 enum GMT_side {
117 GMT_BOTTOM = 0,
118 GMT_RIGHT = 1,
119 GMT_TOP = 2,
120 GMT_LEFT = 3};
121
122 /* Basic error reporting when things go badly wrong. This Error_and_return macro can be
123 * used in stead of regular return(code) to print out what the code is before
124 * it returns. We assume the GMT pointer is available in the function!
125 */
126 #define Error_and_return(code,err) { GMT_Report(GMT->parent,GMT_MSG_ERROR,"%s\n",gmt_error_string[code]); return (err);}
127
128 /* Note by P. Wessel, 18-Oct-2012, updated 08-JAN-2014:
129 * In the olden days, GMT only did great circle distances. In GMT 4 we implemented geodesic
130 * distances by Rudoe's formula as given in Bomford [1971]. However, that geodesic is not
131 * exactly what we wanted as it is a normal section and does not strictly follow the geodesic.
132 * Other candidates are Vincenty [1975], which is widely used and Karney [2012], which is super-
133 * accurate. At this point their differences are in the micro-meter level. For GMT 5 we have
134 * now switched to the Vincenty algorithm as provided by Gerald Evenden, USGS [author of proj4],
135 * which is a modified translation of the NGS algorithm and not exactly what is in proj4's geod
136 * program (which Evenden thinks is inferior.) I ran a comparison between many algorithms that
137 * either were available via codes or had online calculators. I sought the geodesic distance
138 * from (0,0) to (10,10) on WGS-84; the results were (in meters):
139 *
140 * GMT4 (Rudoe): 1565109.099232116
141 * proj4: 1565109.095557918
142 * vdist(0,0,10,10) [0] 1565109.09921775
143 * Karney [1]: 1565109.09921789
144 * Vincenty [2]: 1565109.099218036
145 * NGS [3] 1565109.0992
146 * Andoyer [4] 1565092.276857755
147 *
148 * [0] via Joaquim Luis, supposedly Vincenty [2012]
149 * [1] via online calculator at max precision http://geographiclib.sourceforge.net/cgi-bin/Geod
150 * [2] downloading, compiling and running http://article.gmane.org/gmane.comp.gis.proj-4.devel/3478.
151 * This is not identical to Vincenty in proj4 but written by Evenden (proj.4 author)
152 * [3] via online calculator http://www.ngs.noaa.gov/cgi-bin/Inv_Fwd/inverse2.prl. Their notes says
153 * this is Vincenty; unfortunately I cannot control the output precision.
154 * [4] Andoyer approximate from Astronomical Algorithms, Jean Meeus, 2009, second edition, Willmann-Bell, Inc.
155 *
156 * Based on these comparisons we decided to implement the Vincenty [2] code as given. The older Rudoe
157 * code is also accessible, as is the approximation by Andoyer which is good to a few tens of m.
158 * The choice was based on the readily available C code versus having to reimplement Karney in C.
159 * When GMT 6 is started we expect to use proj4
160 */
161
162 static char *GEOD_TEXT[3] = {"Vincenty", "Andoyer", "Rudoe"};
163
164 /*! . */
gmtmap_get_angle(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)165 GMT_LOCAL double gmtmap_get_angle (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
166 double x1, y1, x2, y2, angle, direction;
167
168 gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
169 gmt_geo_to_xy (GMT, lon2, lat2, &x2, &y2);
170 if (doubleAlmostEqualZero (y1, y2) && doubleAlmostEqualZero (x1, x2)) { /* Special case that only(?) occurs at N or S pole or r=0 for GMT_POLAR */
171 if (fabs (fmod (lon1 - GMT->common.R.wesn[XLO] + 360.0, 360.0)) > fabs (fmod (lon1 - GMT->common.R.wesn[XHI] + 360.0, 360.0))) { /* East */
172 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], &x1, &y1);
173 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &x2, &y2);
174 GMT->current.map.corner = 1;
175 }
176 else {
177 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &x1, &y1);
178 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &x2, &y2);
179 GMT->current.map.corner = 3;
180 }
181 angle = d_atan2d (y2-y1, x2-x1) - 90.0;
182 if (GMT->current.proj.got_azimuths) angle += 180.0;
183 if (GMT->current.proj.flip) angle += 180.0;
184 }
185 else
186 angle = d_atan2d (y2 - y1, x2 - x1);
187
188 if (abs (GMT->current.map.prev_x_status) == 2 && abs (GMT->current.map.prev_y_status) == 2) /* Last point outside */
189 direction = angle + 180.0;
190 else if (GMT->current.map.prev_x_status == 0 && GMT->current.map.prev_y_status == 0) /* Last point inside */
191 direction = angle;
192 else {
193 if (abs (GMT->current.map.this_x_status) == 2 && abs (GMT->current.map.this_y_status) == 2) /* This point outside */
194 direction = angle;
195 else if (GMT->current.map.this_x_status == 0 && GMT->current.map.this_y_status == 0) /* This point inside */
196 direction = angle + 180.0;
197 else { /* Special case of corners and sides only */
198 if (GMT->current.map.prev_x_status == GMT->current.map.this_x_status)
199 direction = (GMT->current.map.prev_y_status == 0) ? angle : angle + 180.0;
200 else if (GMT->current.map.prev_y_status == GMT->current.map.this_y_status)
201 direction = (GMT->current.map.prev_x_status == 0) ? angle : angle + 180.0;
202 else
203 direction = angle;
204
205 }
206 }
207
208 if (direction < 0.0) direction += 360.0;
209 if (direction >= 360.0) direction -= 360.0;
210 return (direction);
211 }
212
213
214 /*! . */
gmtlib_left_boundary(struct GMT_CTRL * GMT,double y)215 double gmtlib_left_boundary (struct GMT_CTRL *GMT, double y) {
216 return ((*GMT->current.map.left_edge) (GMT, y));
217 }
218
219 /*! . */
gmtlib_right_boundary(struct GMT_CTRL * GMT,double y)220 double gmtlib_right_boundary (struct GMT_CTRL *GMT, double y) {
221 return ((*GMT->current.map.right_edge) (GMT, y));
222 }
223
224 /* Private functions internal to gmt_map.c */
225
226 /*! . */
gmtmap_quickconic(struct GMT_CTRL * GMT)227 GMT_LOCAL bool gmtmap_quickconic (struct GMT_CTRL *GMT) {
228 /* Returns true if area/scale are large/small enough
229 * so that we can use spherical equations with authalic
230 * or conformal latitudes instead of the full ellipsoidal
231 * equations.
232 */
233
234 double s, dlon, width;
235
236 if (GMT->current.proj.gave_map_width) { /* Gave width */
237 dlon = GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO];
238 width = GMT->current.proj.pars[4] * GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_M]; /* Convert to meters */
239 s = (dlon * GMT->current.proj.M_PR_DEG) / width;
240 }
241 else if (GMT->current.proj.units_pr_degree) { /* Gave scale */
242 /* Convert to meters */
243 s = GMT->current.proj.M_PR_DEG / (GMT->current.proj.pars[4] * GMT->session.u2u[GMT->current.setting.proj_length_unit][GMT_M]);
244 }
245 else { /* Got 1:xxx that was changed */
246 s = (1.0 / GMT->current.proj.pars[4]) / GMT->current.proj.unit;
247 }
248
249 if (s > 1.0e7) { /* if s in 1:s exceeds 1e7 we do the quick thing */
250 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Using spherical projection with conformal latitudes\n");
251 return (true);
252 }
253 else /* Use full ellipsoidal terms */
254 return (false);
255 }
256
257 /*! . */
gmtmap_quicktm(struct GMT_CTRL * GMT,double lon0,double limit)258 GMT_LOCAL bool gmtmap_quicktm (struct GMT_CTRL *GMT, double lon0, double limit) {
259 /* Returns true if the region chosen is too large for the
260 * ellipsoidal series to be valid; hence use spherical equations
261 * with authalic latitudes instead.
262 * We let +-limit degrees from central meridian be the cutoff.
263 */
264
265 double d_left, d_right;
266
267 d_left = lon0 - GMT->common.R.wesn[XLO] - 360.0;
268 d_right = lon0 - GMT->common.R.wesn[XHI] - 360.0;
269 while (d_left < -180.0) d_left += 360.0;
270 while (d_right < -180.0) d_right += 360.0;
271 if (fabs (d_left) > limit || fabs (d_right) > limit) {
272 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Using spherical projection with authalic latitudes\n");
273 return (true);
274 }
275 else /* Use full ellipsoidal terms */
276 return (false);
277 }
278
279 /*! . */
gmtmap_set_polar(struct GMT_CTRL * GMT)280 GMT_LOCAL void gmtmap_set_polar (struct GMT_CTRL *GMT) {
281 /* Determines if the projection pole is N or S pole */
282
283 if (doubleAlmostEqual (fabs (GMT->current.proj.pars[1]), 90.0)) {
284 GMT->current.proj.polar = true;
285 GMT->current.proj.north_pole = (GMT->current.proj.pars[1] > 0.0);
286 }
287 }
288
gmtmap_central_meridian_not_set(struct GMT_CTRL * GMT)289 GMT_LOCAL bool gmtmap_central_meridian_not_set (struct GMT_CTRL *GMT) {
290 /* Just to make it clearer to understand the code. If NaN then we were never given a central meridian */
291 return (gmt_M_is_dnan (GMT->current.proj.pars[0]));
292 }
293
gmtmap_set_default_central_meridian(struct GMT_CTRL * GMT)294 GMT_LOCAL void gmtmap_set_default_central_meridian (struct GMT_CTRL *GMT) {
295 /* Pick half-way between w and e, but watch for -R+r */
296 if (GMT->common.R.oblique && GMT->common.R.wesn[XHI] < GMT->common.R.wesn[XLO])
297 GMT->current.proj.pars[0] = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI] + 360.0); /* Set to middle lon but watch for e < w */
298 else
299 GMT->current.proj.pars[0] = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]); /* Set to middle lon */
300 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian not given, default to %g\n", GMT->current.proj.pars[0]);
301 }
302
303 /*! . */
gmtmap_cyl_validate_clon(struct GMT_CTRL * GMT,unsigned int mode)304 GMT_LOCAL void gmtmap_cyl_validate_clon (struct GMT_CTRL *GMT, unsigned int mode) {
305 /* Make sure that for global (360-range) cylindrical projections, the central meridian is neither west nor east.
306 * If so then we reset it to the middle value or we change -R:
307 * mode == 0: <clon> should be reset based on w/e mid-point
308 * mode == 1: -J<clon> is firm so w/e is centered on <c.lon>
309 */
310 if (gmtmap_central_meridian_not_set (GMT))
311 gmtmap_set_default_central_meridian (GMT);
312 else if (GMT->current.map.is_world && (GMT->current.proj.pars[0] == GMT->common.R.wesn[XLO] || GMT->current.proj.pars[0] == GMT->common.R.wesn[XHI])) {
313 /* Reset central meridian since cannot be 360 away from one of the boundaries since that gives xmin == xmax below */
314 if (mode == 1) { /* Change -R to fit central meridian */
315 double w = GMT->current.proj.pars[0] - 180.0, e = GMT->current.proj.pars[0] + 180.0;
316 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Region for global cylindrical projection had to be reset from %g/%g to %g/%g\n",
317 GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], w, e);
318 GMT->common.R.wesn[XLO] = w; GMT->common.R.wesn[XHI] = e;
319 }
320 else { /* Change central meridian to fit -R */
321 double new_lon = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
322 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian for global cylindrical projection had to be reset from %g to %g\n", GMT->current.proj.pars[0], new_lon);
323 GMT->current.proj.pars[0] = new_lon;
324 }
325 }
326 else if (!GMT->current.map.is_world) { /* For reginal areas we cannot have clon > 180 away from either boundary */
327 if (fabs (GMT->current.proj.pars[0] - GMT->common.R.wesn[XLO]) > 180.0 || fabs (GMT->current.proj.pars[0] - GMT->common.R.wesn[XHI]) > 180.0) {
328 double new_lon = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
329 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian for cylindrical projection had to be reset from %g to %g\n", GMT->current.proj.pars[0], new_lon);
330 GMT->current.proj.pars[0] = new_lon;
331 }
332 }
333 }
334
335 /*! . */
gmtmap_lat_swap_init(struct GMT_CTRL * GMT)336 GMT_LOCAL void gmtmap_lat_swap_init (struct GMT_CTRL *GMT) {
337 /* Initialize values in GMT->current.proj.lat_swap_vals based on GMT->current.proj.
338
339 First compute GMT->current.proj.lat_swap_vals.ra (and rm), the radii to use in
340 spherical formulae for area (respectively, N-S distance) when
341 using the authalic (respectively, meridional) latitude.
342
343 Then for each type of swap:
344 First load GMT->current.proj.lat_swap_vals.c[itype][k], k=0,1,2,3 with the
345 coefficient for sin(2 (k +1) phi), based on series from Adams.
346 Next reshuffle these coefficients so they form a nested
347 polynomial using equations (3-34) and (3-35) on page 19 of
348 Snyder.
349
350 References: J. P. Snyder, "Map projections - a working manual",
351 U. S. Geological Survey Professional Paper #1395, 1987.
352 O. S. Adams, "Latitude Developments Connected With Geodesy and
353 Cartography", U. S. Coast and Geodetic Survey Special Publication
354 number 67, 1949.
355 P. D. Thomas, "Conformal Projections in Geodesy and Cartography",
356 US CGS Special Pub #251, 1952.
357 See also other US CGS Special Pubs (#53, 57, 68, 193, and 251).
358
359 Latitudes are named as follows (this only partly conforms to
360 names in the literature, which are varied):
361
362 Geodetic = G, angle between ellipsoid normal and equator
363 geocentric = O, angle between radius from Origin and equator
364 Parametric = P, angle such that x=a*cos(phi), y=b*sin(phi) is on ellipse
365 Authalic = A, angle to use in equal area development of ellipsoid
366 Conformal = GMT, angle to use in conformal development of ellipsoid
367 Meridional = M, angle to use in N-S distance calculation of ellipsoid
368
369 (The parametric latitude is the one used in orthogonal curvilinear
370 coordinates and ellipsoidal harmonics. The term "authalic" was coined
371 by A. Tissot in "Memoire sur la Representations des Surfaces et les
372 projections des cartes geographiques", Gauthier Villars, Paris, 1881;
373 it comes from the Greek meaning equal area.)
374
375 The idea of latitude swaps is this: Conformal, equal-area, and other
376 developments of spherical surfaces usually lead to analytic formulae
377 for the forward and inverse projections which are stable over a wide
378 range of the values. It is handy to use the same formulae when
379 developing the surface of the ellipsoid. The authalic (respectively,
380 conformal) lat is such that when plugged into a spherical development
381 formula, it results in an authalic (meaning equal area) (respectively,
382 conformal) development of the ellipsoid. The meridional lat does the
383 same thing for measurement of N-S distances along a meridian.
384
385 Adams gives coefficients for series in sin(2 (k +1) phi), for k
386 up to 2 or 3. I have extended these to k=3 in all cases except
387 the authalic. I have sometimes multiplied his coefficients by
388 (-1) so that the sense here is always to give a correction to be
389 added to the input lat to get the output lat in gmt_lat_swap().
390
391 I have tested this code by checking that
392 fabs (geocentric) < fabs (parametric) < fabs (geodetic)
393 and also, for each pair of possible conversions, that the
394 forward followed by the inverse returns the original lat to
395 within a small tolerance. This tolerance is as follows:
396
397 geodetic <-> authalic: max error (degrees) = 1.253344e-08
398 geodetic <-> conformal: max error (degrees) = 2.321796e-07 should be better after 13nov07 fix
399 geodetic <-> meridional: max error (degrees) = 4.490630e-12
400 geodetic <-> geocentric: max error (degrees) = 1.350031e-13
401 geodetic <-> parametric: max error (degrees) = 1.421085e-14
402 geocentric <-> parametric: max error (degrees) = 1.421085e-14
403
404 Currently, (GMT v5) the only ones we anticipate using are
405 geodetic, authalic, and conformal. I have put others in here
406 for possible future convenience.
407
408 Also, I made this depend on GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid]
409 rather than on GMT->current.proj, so that it will be possible to
410 call gmt_lat_swap() without having to pass -R and -J to
411 gmt_map_setup(), so that in the future we will be able to use
412 lat conversions without plotting maps.
413
414 W H F Smith, 10--13 May 1999. */
415
416 /* PW notes: Projections only convert latitudes if GMT->current.proj.GMT_convert_latitudes is true.
417 * This is set by gmt_map_setup if the ellipsoid is not a sphere. Calling gmtmap_lat_swap_init by itself
418 * does not affect the mapping machinery. Since various situations call for the use
419 * of auxiliary latitudes we initialize gmtmap_lat_swap_init in gmt_begin. This means
420 * programs can use functions like gmt_lat_swap whenever needed.
421 */
422
423 unsigned int i;
424 double x, xx[4], a, f, e2, e4, e6, e8;
425
426 f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
427 a = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius;
428
429 if (gmt_M_is_zero (f)) {
430 gmt_M_memset (GMT->current.proj.lat_swap_vals.c, GMT_LATSWAP_N * 4, double);
431 GMT->current.proj.lat_swap_vals.ra = GMT->current.proj.lat_swap_vals.rm = a;
432 GMT->current.proj.lat_swap_vals.spherical = true;
433 return;
434 }
435 GMT->current.proj.lat_swap_vals.spherical = false;
436
437 /* Below are two sums for x to get the two radii. I have nested the
438 parentheses to add the terms in the order that would minimize roundoff
439 error. However, in double precision there may be no need to do this.
440 I have carried these to 4 terms (eccentricity to the 8th power) because
441 this is as far as Adams goes with anything, but it is not clear what
442 the truncation error is, since every term in the sum has the same sign. */
443
444 e2 = f * (2.0 - f);
445 e4 = e2 * e2;
446 e6 = e4 * e2;
447 e8 = e4 * e4;
448
449 /* This expression for the Authalic radius comes from Adams [1949] */
450 xx[0] = 2.0 / 3.0;
451 xx[1] = 3.0 / 5.0;
452 xx[2] = 4.0 / 7.0;
453 xx[3] = 5.0 / 9.0;
454 x = xx[0] * e2 + ( xx[1] * e4 + ( xx[2] * e6 + xx[3] * e8));
455 GMT->current.proj.lat_swap_vals.ra = a * sqrt( (1.0 + x) * (1.0 - e2));
456
457 /* This expression for the Meridional radius comes from Gradshteyn and Ryzhik, 8.114.1,
458 because Adams only gets the first two terms. This can be worked out by expressing the
459 meridian arc length in terms of an integral in parametric latitude, which reduces to
460 equatorial radius times Elliptic Integral of the Second Kind. Expanding this using
461 binomial theorem leads to Gradshteyn and Ryzhik's expression: */
462 xx[0] = 1.0 / 4.0;
463 xx[1] = xx[0] * 3.0 / 16.0;
464 xx[2] = xx[1] * 3.0 * 5.0 / 36.0;
465 xx[3] = xx[2] * 5.0 * 7.0 / 64.0;
466 x = xx[0] * e2 + ( xx[1] * e4 + ( xx[2] * e6 + xx[3] * e8));
467 GMT->current.proj.lat_swap_vals.rm = a * (1.0 - x);
468
469
470 /* Geodetic to authalic: */
471 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][0] = -(e2 / 3.0 + (31.0 * e4 / 180.0 + 59.0 * e6 / 560.0));
472 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][1] = 17.0 * e4 / 360.0 + 61.0 * e6 / 1260;
473 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][2] = -383.0 * e6 / 45360.0;
474 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2A][3] = 0.0;
475
476 /* Authalic to geodetic: */
477 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][0] = e2 / 3.0 + (31.0 * e4 / 180.0 + 517.0 * e6 / 5040.0);
478 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][1] = 23.0 * e4 / 360.0 + 251.0 * e6 / 3780;
479 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][2] = 761.0 * e6 / 45360.0;
480 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_A2G][3] = 0.0;
481
482 /* Geodetic to conformal: */
483 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][0] = -(e2 / 2.0 + (5.0 * e4 / 24.0 + (3.0 * e6 / 32.0 + 281.0 * e8 / 5760.0)));
484 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][1] = 5.0 * e4 / 48.0 + (7.0 * e6 / 80.0 + 697.0 * e8 / 11520.0);
485 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][2] = -(13.0 * e6 / 480.0 + 461.0 * e8 / 13440.0);
486 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2C][3] = 1237.0 * e8 / 161280.0;
487
488 /* Conformal to geodetic: */
489 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][0] = e2 / 2.0 + (5.0 * e4 / 24.0 + (e6 / 12.0 + 13.0 * e8 / 360.0)) ;
490 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][1] = 7.0 * e4 / 48.0 + (29.0 * e6 / 240.0 + 811.0 * e8 / 11520.0);
491 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][2] = 7.0 * e6 / 120.0 + 81.0 * e8 / 1120.0; /* Bug fixed 13nov07 whfs */
492 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_C2G][3] = 4279.0 * e8 / 161280.0;
493
494
495 /* The meridional and parametric developments use this parameter: */
496 x = f/(2.0 - f); /* Adams calls this n. It is f/(2-f), or -betaJK in my notes. */
497 xx[0] = x; /* n */
498 xx[1] = x * x; /* n-squared */
499 xx[2] = xx[1] * x; /* n-cubed */
500 xx[3] = xx[2] * x; /* n to the 4th */
501
502 /* Geodetic to meridional: */
503 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][0] = -(3.0 * xx[0] / 2.0 - 9.0 * xx[2] / 16.0);
504 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][1] = 15.0 * xx[1] / 16.0 - 15.0 * xx[3] / 32.0;
505 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][2] = -35.0 * xx[2] / 48.0;
506 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2M][3] = 315.0 * xx[3] / 512.0;
507
508 /* Meridional to geodetic: */
509 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][0] = 3.0 * xx[0] / 2.0 - 27.0 * xx[2] / 32.0;
510 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][1] = 21.0 * xx[1] / 16.0 - 55.0 * xx[3] / 32.0;
511 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][2] = 151.0 * xx[2] / 96.0;
512 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_M2G][3] = 1097.0 * xx[3] / 512.0;
513
514 /* Geodetic to parametric equals parametric to geocentric: */
515 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][0] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][0] = -xx[0];
516 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][1] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][1] = xx[1] / 2.0;
517 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][2] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][2] = -xx[2] / 3.0;
518 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2P][3] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2O][3] = xx[3] / 4.0;
519
520 /* Parametric to geodetic equals geocentric to parametric: */
521 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][0] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][0] = xx[0];
522 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][1] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][1] = xx[1] / 2.0;
523 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][2] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][2] = xx[2] / 3.0;
524 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_P2G][3] = GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2P][3] = xx[3] / 4.0;
525
526
527 /* The geodetic <->geocentric use this parameter: */
528 x = 1.0 - e2;
529 x = (1.0 - x)/(1.0 + x); /* Adams calls this m. It is e2/(2-e2), or -betaJK in my notes. */
530 xx[0] = x; /* m */
531 xx[1] = x * x; /* m-squared */
532 xx[2] = xx[1] * x; /* m-cubed */
533 xx[3] = xx[2] * x; /* m to the 4th */
534
535 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][0] = -xx[0];
536 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][1] = xx[1] / 2.0;
537 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][2] = -xx[2] / 3.0;
538 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_G2O][3] = xx[3] / 4.0;
539
540 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][0] = xx[0];
541 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][1] = xx[1] / 2.0;
542 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][2] = xx[2] / 3.0;
543 GMT->current.proj.lat_swap_vals.c[GMT_LATSWAP_O2G][3] = xx[3] / 4.0;
544
545
546 /* Now do the Snyder Shuffle: */
547 for (i = 0; i < GMT_LATSWAP_N; i++) {
548 GMT->current.proj.lat_swap_vals.c[i][0] = GMT->current.proj.lat_swap_vals.c[i][0] - GMT->current.proj.lat_swap_vals.c[i][2];
549 GMT->current.proj.lat_swap_vals.c[i][1] = 2.0 * GMT->current.proj.lat_swap_vals.c[i][1] - 4.0 * GMT->current.proj.lat_swap_vals.c[i][3];
550 GMT->current.proj.lat_swap_vals.c[i][2] *= 4.0;
551 GMT->current.proj.lat_swap_vals.c[i][3] *= 8.0;
552 }
553 }
554
555 /* The *_outside routines return the status of the current point.
556 * Status is the sum of x_status and y_status.
557 * x_status may be
558 * 0 w < lon < e
559 * -1 lon == w
560 * 1 lon == e
561 * -2 lon < w
562 * 2 lon > e
563 * y_status may be
564 * 0 s < lat < n
565 * -1 lat == s
566 * 1 lat == n
567 * -2 lat < s
568 * 2 lat > n
569 */
570
571 /*! . */
gmtmap_wesn_outside(struct GMT_CTRL * GMT,double lon,double lat)572 GMT_LOCAL bool gmtmap_wesn_outside (struct GMT_CTRL *GMT, double lon, double lat) {
573 /* Determine if a point (lon,lat) is outside or on the rectangular lon/lat boundaries
574 * The check GMT->current.map.lon_wrap is include since we need to consider the 360
575 * degree periodicity of the longitude coordinate.
576 * When we are making basemaps and may want to ensure that a point is
577 * slightly outside the border without having it automatically flip by
578 * 360 degrees. In that case GMT->current.map.lon_wrap will be temporarily set to false.
579 */
580
581 if (GMT->current.map.lon_wrap) {
582 while (lon < GMT->common.R.wesn[XLO] && (lon + GMT->current.map.lon_wrap_range) <= GMT->common.R.wesn[XHI]) lon += GMT->current.map.lon_wrap_range;
583 while (lon > GMT->common.R.wesn[XHI] && (lon - GMT->current.map.lon_wrap_range) >= GMT->common.R.wesn[XLO]) lon -= GMT->current.map.lon_wrap_range;
584 }
585 else if (GMT->current.map.lat_wrap) {
586 while (lat < GMT->common.R.wesn[YLO] && (lat + GMT->current.map.lon_wrap_range) <= GMT->common.R.wesn[YHI]) lat += GMT->current.map.lon_wrap_range;
587 while (lat > GMT->common.R.wesn[YHI] && (lat - GMT->current.map.lon_wrap_range) >= GMT->common.R.wesn[YLO]) lat -= GMT->current.map.lon_wrap_range;
588 }
589
590 /* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT. Trying the latter */
591 if (GMT->current.map.on_border_is_outside && fabs (lon - GMT->common.R.wesn[XLO]) < GMT_CONV8_LIMIT)
592 GMT->current.map.this_x_status = -1;
593 else if (GMT->current.map.on_border_is_outside && fabs (lon - GMT->common.R.wesn[XHI]) < GMT_CONV8_LIMIT)
594 GMT->current.map.this_x_status = 1;
595 else if (lon < GMT->common.R.wesn[XLO])
596 GMT->current.map.this_x_status = -2;
597 else if (lon > GMT->common.R.wesn[XHI])
598 GMT->current.map.this_x_status = 2;
599 else
600 GMT->current.map.this_x_status = 0;
601
602 if (GMT->current.map.on_border_is_outside && fabs (lat - GMT->common.R.wesn[YLO]) < GMT_CONV8_LIMIT)
603 GMT->current.map.this_y_status = -1;
604 else if (GMT->current.map.on_border_is_outside && fabs (lat - GMT->common.R.wesn[YHI]) < GMT_CONV8_LIMIT)
605 GMT->current.map.this_y_status = 1;
606 else if (lat < GMT->common.R.wesn[YLO])
607 GMT->current.map.this_y_status = -2;
608 else if (lat > GMT->common.R.wesn[YHI])
609 GMT->current.map.this_y_status = 2;
610 else
611 GMT->current.map.this_y_status = 0;
612
613 return (GMT->current.map.this_x_status != 0 || GMT->current.map.this_y_status != 0);
614
615 }
616
617 /*! . */
gmtmap_polar_outside(struct GMT_CTRL * GMT,double lon,double lat)618 GMT_LOCAL bool gmtmap_polar_outside (struct GMT_CTRL *GMT, double lon, double lat) {
619 gmtmap_wesn_outside (GMT, lon, lat);
620
621 if (!GMT->current.proj.edge[1]) GMT->current.map.this_x_status = 0; /* 360 degrees, no edge */
622 if (GMT->current.map.this_y_status < 0 && !GMT->current.proj.edge[0]) GMT->current.map.this_y_status = 0; /* South pole enclosed */
623 if (GMT->current.map.this_y_status > 0 && !GMT->current.proj.edge[2]) GMT->current.map.this_y_status = 0; /* North pole enclosed */
624
625 return (GMT->current.map.this_x_status != 0 || GMT->current.map.this_y_status != 0);
626 }
627
628 /*! . */
gmtmap_radial_outside(struct GMT_CTRL * GMT,double lon,double lat)629 GMT_LOCAL bool gmtmap_radial_outside (struct GMT_CTRL *GMT, double lon, double lat) {
630 double dist;
631
632 /* Test if point is more than horizon spherical degrees from origin. For global maps, let all borders be "south" */
633
634 /* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT. Trying the latter */
635 GMT->current.map.this_x_status = 0;
636 dist = gmtlib_great_circle_dist_degree (GMT, lon, lat, GMT->current.proj.central_meridian, GMT->current.proj.pole);
637 if (GMT->current.map.on_border_is_outside && fabs (dist - GMT->current.proj.f_horizon) < GMT_CONV8_LIMIT)
638 GMT->current.map.this_y_status = -1;
639 else if (dist > GMT->current.proj.f_horizon)
640 GMT->current.map.this_y_status = -2;
641 else
642 GMT->current.map.this_y_status = 0;
643 return (GMT->current.map.this_y_status != 0);
644 }
645
646 /*! . */
gmtmap_rect_outside(struct GMT_CTRL * GMT,double lon,double lat)647 GMT_LOCAL bool gmtmap_rect_outside (struct GMT_CTRL *GMT, double lon, double lat) {
648 double x, y;
649
650 gmt_geo_to_xy (GMT, lon, lat, &x, &y);
651
652 return (gmt_cart_outside (GMT, x, y));
653 }
654
655 /*! . */
gmtmap_rect_outside2(struct GMT_CTRL * GMT,double lon,double lat)656 GMT_LOCAL bool gmtmap_rect_outside2 (struct GMT_CTRL *GMT, double lon, double lat) {
657 /* For Azimuthal proj with rect borders since gmtmap_rect_outside may fail for antipodal points */
658 if (gmtmap_radial_outside (GMT, lon, lat)) return (true); /* Point > 90 degrees away */
659 return (gmtmap_rect_outside (GMT, lon, lat)); /* Must check if inside box */
660 }
661
662 /*! . */
gmtmap_x_wesn_corner(struct GMT_CTRL * GMT,double * x)663 GMT_LOCAL void gmtmap_x_wesn_corner (struct GMT_CTRL *GMT, double *x) {
664 /* if (fabs (fmod (fabs (*x - GMT->common.R.wesn[XLO]), 360.0)) <= GMT_CONV4_LIMIT)
665 *x = GMT->common.R.wesn[XLO];
666 else if (fabs (fmod (fabs (*x - GMT->common.R.wesn[XHI]), 360.0)) <= GMT_CONV4_LIMIT)
667 *x = GMT->common.R.wesn[XHI]; */
668
669 /* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT. Trying the latter */
670 if (fabs (*x - GMT->common.R.wesn[XLO]) <= GMT_CONV8_LIMIT)
671 *x = GMT->common.R.wesn[XLO];
672 else if (fabs (*x - GMT->common.R.wesn[XHI]) <= GMT_CONV8_LIMIT)
673 *x = GMT->common.R.wesn[XHI];
674 }
675
676 /*! . */
gmtmap_y_wesn_corner(struct GMT_CTRL * GMT,double * y)677 GMT_LOCAL void gmtmap_y_wesn_corner (struct GMT_CTRL *GMT, double *y) {
678 /* Note PW: 8-20-2014: Was GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT. Trying the latter */
679 if (fabs (*y - GMT->common.R.wesn[YLO]) <= GMT_CONV8_LIMIT)
680 *y = GMT->common.R.wesn[YLO];
681 else if (fabs (*y - GMT->common.R.wesn[YHI]) <= GMT_CONV8_LIMIT)
682 *y = GMT->common.R.wesn[YHI];
683 }
684
685 /*! . */
gmtmap_is_wesn_corner(struct GMT_CTRL * GMT,double x,double y)686 GMT_LOCAL bool gmtmap_is_wesn_corner (struct GMT_CTRL *GMT, double x, double y) {
687 /* Checks if point is a corner */
688 GMT->current.map.corner = 0;
689
690 if (doubleAlmostEqualZero (fmod(fabs(x), 360.0), fmod(fabs(GMT->common.R.wesn[XLO]), 360.0))) {
691 if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YLO]))
692 GMT->current.map.corner = 1;
693 else if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YHI]))
694 GMT->current.map.corner = 4;
695 }
696 else if (doubleAlmostEqualZero (fmod(fabs(x), 360.0), fmod(fabs(GMT->common.R.wesn[XHI]), 360.0))) {
697 if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YLO]))
698 GMT->current.map.corner = 2;
699 else if (doubleAlmostEqualZero (y, GMT->common.R.wesn[YHI]))
700 GMT->current.map.corner = 3;
701 }
702 return (GMT->current.map.corner > 0);
703 }
704
705 /*! . */
gmtmap_lon_inside(struct GMT_CTRL * GMT,double lon,double w,double e)706 GMT_LOCAL bool gmtmap_lon_inside (struct GMT_CTRL *GMT, double lon, double w, double e) {
707 while (lon < GMT->common.R.wesn[XLO]) lon += 360.0;
708 while (lon > GMT->common.R.wesn[XHI]) lon -= 360.0;
709
710 if (lon < w) return (false);
711 if (lon > e) return (false);
712 return (true);
713 }
714
715 /*! . */
gmtmap_wesn_crossing(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)716 GMT_LOCAL unsigned int gmtmap_wesn_crossing (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
717 /* Compute all crossover points of a line segment with the rectangular lat/lon boundaries
718 * Since it may not be obvious which side the line may cross, and since in some cases the two points may be
719 * entirely outside the region but still cut through it, we first find all possible candidates and then decide
720 * which ones are valid crossings. We may find 0, 1, or 2 intersections */
721
722 unsigned int n = 0, i;
723 double d, x0, y0;
724
725 /* If wrapping is allowed: first bring both points between W and E boundaries,
726 * then move the western-most point east if it is further than 180 degrees away.
727 * This may cause the points to span the eastern boundary */
728
729 if (GMT->current.map.lon_wrap) {
730 while (lon0 < GMT->common.R.wesn[XLO]) lon0 += GMT->current.map.lon_wrap_range;
731 while (lon0 > GMT->common.R.wesn[XHI]) lon0 -= GMT->current.map.lon_wrap_range;
732 while (lon1 < GMT->common.R.wesn[XLO]) lon1 += GMT->current.map.lon_wrap_range;
733 while (lon1 > GMT->common.R.wesn[XHI]) lon1 -= GMT->current.map.lon_wrap_range;
734 if (fabs (lon0 - lon1) <= 0.5 * GMT->current.map.lon_wrap_range) { /* Nothing */ }
735 else if (lon0 < lon1)
736 lon0 += GMT->current.map.lon_wrap_range;
737 else
738 lon1 += GMT->current.map.lon_wrap_range;
739 }
740 else if (GMT->current.map.lat_wrap) {
741 while (lat0 < GMT->common.R.wesn[YLO]) lat0 += GMT->current.map.lat_wrap_range;
742 while (lat0 > GMT->common.R.wesn[YHI]) lat0 -= GMT->current.map.lat_wrap_range;
743 while (lat1 < GMT->common.R.wesn[YLO]) lat1 += GMT->current.map.lat_wrap_range;
744 while (lat1 > GMT->common.R.wesn[YHI]) lat1 -= GMT->current.map.lat_wrap_range;
745 if (fabs (lat0 - lat1) <= 0.5 * GMT->current.map.lat_wrap_range) { /* Nothing */ }
746 else if (lat0 < lat1)
747 lat0 += GMT->current.map.lat_wrap_range;
748 else
749 lat1 += GMT->current.map.lat_wrap_range;
750 }
751
752 /* Then set 'almost'-corners to corners */
753 gmtmap_x_wesn_corner (GMT, &lon0);
754 gmtmap_x_wesn_corner (GMT, &lon1);
755 gmtmap_y_wesn_corner (GMT, &lat0);
756 gmtmap_y_wesn_corner (GMT, &lat1);
757
758 /* Crossing South */
759 if ((lat0 >= GMT->common.R.wesn[YLO] && lat1 <= GMT->common.R.wesn[YLO]) || (lat1 >= GMT->common.R.wesn[YLO] && lat0 <= GMT->common.R.wesn[YLO])) {
760 sides[n] = 0;
761 clat[n] = GMT->common.R.wesn[YLO];
762 d = lat0 - lat1;
763 clon[n] = (doubleAlmostEqualZero (lat0, lat1)) ? lon1 : lon1 + (lon0 - lon1) * (clat[n] - lat1) / d;
764 gmtmap_x_wesn_corner (GMT, &clon[n]);
765 if (fabs (d) > 0.0 && gmtmap_lon_inside (GMT, clon[n], GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) n++;
766 }
767 /* Crossing East */
768 if ((lon0 >= GMT->common.R.wesn[XHI] && lon1 <= GMT->common.R.wesn[XHI]) || (lon1 >= GMT->common.R.wesn[XHI] && lon0 <= GMT->common.R.wesn[XHI])) {
769 sides[n] = 1;
770 clon[n] = GMT->common.R.wesn[XHI];
771 d = lon0 - lon1;
772 clat[n] = (doubleAlmostEqualZero (lon0, lon1)) ? lat1 : lat1 + (lat0 - lat1) * (clon[n] - lon1) / d;
773 gmtmap_y_wesn_corner (GMT, &clat[n]);
774 if (fabs (d) > 0.0 && clat[n] >= GMT->common.R.wesn[YLO] && clat[n] <= GMT->common.R.wesn[YHI]) n++;
775 }
776
777 /* Now adjust the longitudes so that they might span the western boundary */
778 if (GMT->current.map.lon_wrap && MAX(lon0, lon1) > GMT->common.R.wesn[XHI]) {
779 lon0 -= GMT->current.map.lon_wrap_range; lon1 -= GMT->current.map.lon_wrap_range;
780 }
781 else if (GMT->current.map.lat_wrap && MAX(lat0, lat1) > GMT->common.R.wesn[YHI]) {
782 lat0 -= GMT->current.map.lat_wrap_range; lat1 -= GMT->current.map.lat_wrap_range;
783 }
784
785 /* Crossing North */
786 if ((lat0 >= GMT->common.R.wesn[YHI] && lat1 <= GMT->common.R.wesn[YHI]) || (lat1 >= GMT->common.R.wesn[YHI] && lat0 <= GMT->common.R.wesn[YHI])) {
787 sides[n] = 2;
788 clat[n] = GMT->common.R.wesn[YHI];
789 d = lat0 - lat1;
790 clon[n] = (doubleAlmostEqualZero (lat0, lat1)) ? lon1 : lon1 + (lon0 - lon1) * (clat[n] - lat1) / d;
791 gmtmap_x_wesn_corner (GMT, &clon[n]);
792 if (fabs (d) > 0.0 && gmtmap_lon_inside (GMT, clon[n], GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) n++;
793 }
794 /* Crossing West */
795 if ((lon0 <= GMT->common.R.wesn[XLO] && lon1 >= GMT->common.R.wesn[XLO]) || (lon1 <= GMT->common.R.wesn[XLO] && lon0 >= GMT->common.R.wesn[XLO])) {
796 sides[n] = 3;
797 clon[n] = GMT->common.R.wesn[XLO];
798 d = lon0 - lon1;
799 clat[n] = (doubleAlmostEqualZero (lon0, lon1)) ? lat1 : lat1 + (lat0 - lat1) * (clon[n] - lon1) / d;
800 gmtmap_y_wesn_corner (GMT, &clat[n]);
801 if (fabs (d) > 0.0 && clat[n] >= GMT->common.R.wesn[YLO] && clat[n] <= GMT->common.R.wesn[YHI]) n++;
802 }
803
804 if (n == 0) return (0);
805
806 for (i = 0; i < n; i++) {
807 gmt_geo_to_xy (GMT, clon[i], clat[i], &xx[i], &yy[i]);
808 if (GMT->current.proj.projection_GMT == GMT_POLAR && sides[i]%2) sides[i] = 4 - sides[i]; /* toggle 1 <-> 3 */
809 }
810
811 if (n == 1) return (1);
812
813 /* Check for corner xover if n == 2 */
814
815 if (gmtmap_is_wesn_corner (GMT, clon[0], clat[0])) return (1);
816
817 if (gmtmap_is_wesn_corner (GMT, clon[1], clat[1])) {
818 clon[0] = clon[1];
819 clat[0] = clat[1];
820 xx[0] = xx[1];
821 yy[0] = yy[1];
822 sides[0] = sides[1];
823 return (1);
824 }
825
826 /* Sort the two intermediate points into the right order based on projected distances from the first point */
827
828 gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
829
830 if (hypot (x0 - xx[1], y0 - yy[1]) < hypot (x0 - xx[0], y0 - yy[0])) {
831 gmt_M_double_swap (clon[0], clon[1]);
832 gmt_M_double_swap (clat[0], clat[1]);
833 gmt_M_double_swap (xx[0], xx[1]);
834 gmt_M_double_swap (yy[0], yy[1]);
835 gmt_M_uint_swap (sides[0], sides[1]);
836 }
837
838 return (2);
839 }
840
841 #if 0
842 GMT_LOCAL void gmtmap_x_rect_corner (struct GMT_CTRL *GMT, double *x) {
843 if (fabs (*x) <= GMT_CONV4_LIMIT)
844 *x = 0.0;
845 else if (fabs (*x - GMT->current.proj.rect[XHI]) <= GMT_CONV4_LIMIT)
846 *x = GMT->current.proj.rect[XHI];
847 }
848
849 GMT_LOCAL void gmtmap_y_rect_corner (struct GMT_CTRL *GMT, double *y) {
850 if (fabs (*y) <= GMT_CONV4_LIMIT)
851 *y = 0.0;
852 else if (fabs (*y - GMT->current.proj.rect[YHI]) <= GMT_CONV4_LIMIT)
853 *y = GMT->current.proj.rect[YHI];
854 }
855 #endif
856
857 /*! . */
gmtmap_x_rect_corner(struct GMT_CTRL * GMT,double * x)858 GMT_LOCAL void gmtmap_x_rect_corner (struct GMT_CTRL *GMT, double *x) {
859 if (fabs (*x) <= GMT_CONV8_LIMIT)
860 *x = 0.0;
861 else if (fabs (*x - GMT->current.proj.rect[XHI]) <= GMT_CONV8_LIMIT)
862 *x = GMT->current.proj.rect[XHI];
863 }
864
865 /*! . */
gmtmap_y_rect_corner(struct GMT_CTRL * GMT,double * y)866 GMT_LOCAL void gmtmap_y_rect_corner (struct GMT_CTRL *GMT, double *y) {
867 if (fabs (*y) <= GMT_CONV8_LIMIT)
868 *y = 0.0;
869 else if (fabs (*y - GMT->current.proj.rect[YHI]) <= GMT_CONV8_LIMIT)
870 *y = GMT->current.proj.rect[YHI];
871 }
872
873 /*! . */
gmtmap_is_rect_corner(struct GMT_CTRL * GMT,double x,double y)874 GMT_LOCAL bool gmtmap_is_rect_corner (struct GMT_CTRL *GMT, double x, double y) {
875 /* Checks if point is a corner */
876 GMT->current.map.corner = -1;
877 if (doubleAlmostEqualZero (x, GMT->current.proj.rect[XLO])) {
878 if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YLO]))
879 GMT->current.map.corner = 1;
880 else if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YHI]))
881 GMT->current.map.corner = 4;
882 }
883 else if (doubleAlmostEqualZero (x, GMT->current.proj.rect[XHI])) {
884 if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YLO]))
885 GMT->current.map.corner = 2;
886 else if (doubleAlmostEqualZero (y, GMT->current.proj.rect[YHI]))
887 GMT->current.map.corner = 3;
888 }
889 return (GMT->current.map.corner > 0);
890 }
891
892 /*! . */
gmtmap_rect_crossing(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)893 GMT_LOCAL unsigned int gmtmap_rect_crossing (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
894 /* Compute all crossover points of a line segment with the boundaries in a rectangular projection */
895
896 unsigned int i, j, n = 0;
897 double x0, x1, y0, y1, d;
898
899 /* Since it may not be obvious which side the line may cross, and since in some cases the two points may be
900 * entirely outside the region but still cut through it, we first find all possible candidates and then decide
901 * which ones are valid crossings. We may find 0, 1, or 2 intersections */
902
903 gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
904 gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
905
906 /* First set 'almost'-corners to corners */
907
908 gmtmap_x_rect_corner (GMT, &x0);
909 gmtmap_x_rect_corner (GMT, &x1);
910 gmtmap_y_rect_corner (GMT, &y0);
911 gmtmap_y_rect_corner (GMT, &y1);
912
913 if ((y0 >= GMT->current.proj.rect[YLO] && y1 <= GMT->current.proj.rect[YLO]) || (y1 >= GMT->current.proj.rect[YLO] && y0 <= GMT->current.proj.rect[YLO])) {
914 sides[n] = 0;
915 yy[n] = GMT->current.proj.rect[YLO];
916 d = y0 - y1;
917 xx[n] = (doubleAlmostEqualZero (y0, y1)) ? x0 : x1 + (x0 - x1) * (yy[n] - y1) / d;
918 gmtmap_x_rect_corner (GMT, &xx[n]);
919 if (fabs (d) > 0.0 && xx[n] >= GMT->current.proj.rect[XLO] && xx[n] <= GMT->current.proj.rect[XHI]) n++;
920 }
921 if ((x0 <= GMT->current.proj.rect[XHI] && x1 >= GMT->current.proj.rect[XHI]) || (x1 <= GMT->current.proj.rect[XHI] && x0 >= GMT->current.proj.rect[XHI])) {
922 sides[n] = 1;
923 xx[n] = GMT->current.proj.rect[XHI];
924 d = x0 - x1;
925 yy[n] = (doubleAlmostEqualZero (x0, x1)) ? y0 : y1 + (y0 - y1) * (xx[n] - x1) / d;
926 gmtmap_y_rect_corner (GMT, &yy[n]);
927 if (fabs (d) > 0.0 && yy[n] >= GMT->current.proj.rect[YLO] && yy[n] <= GMT->current.proj.rect[YHI]) n++;
928 }
929 if ((y0 <= GMT->current.proj.rect[YHI] && y1 >= GMT->current.proj.rect[YHI]) || (y1 <= GMT->current.proj.rect[YHI] && y0 >= GMT->current.proj.rect[YHI])) {
930 sides[n] = 2;
931 yy[n] = GMT->current.proj.rect[YHI];
932 d = y0 - y1;
933 xx[n] = (doubleAlmostEqualZero (y0, y1)) ? x0 : x1 + (x0 - x1) * (yy[n] - y1) / d;
934 gmtmap_x_rect_corner (GMT, &xx[n]);
935 if (fabs (d) > 0.0 && xx[n] >= GMT->current.proj.rect[XLO] && xx[n] <= GMT->current.proj.rect[XHI]) n++;
936 }
937 if ((x0 >= GMT->current.proj.rect[XLO] && x1 <= GMT->current.proj.rect[XLO]) || (x1 >= GMT->current.proj.rect[XLO] && x0 <= GMT->current.proj.rect[XLO])) {
938 sides[n] = 3;
939 xx[n] = GMT->current.proj.rect[XLO];
940 d = x0 - x1;
941 yy[n] = (doubleAlmostEqualZero (x0, x1)) ? y0 : y1 + (y0 - y1) * (xx[n] - x1) / d;
942 gmtmap_y_rect_corner (GMT, &yy[n]);
943 if (fabs (d) > 0.0 && yy[n] >= GMT->current.proj.rect[YLO] && yy[n] <= GMT->current.proj.rect[YHI]) n++;
944 }
945
946 if (n == 0) return (0);
947
948 /* Eliminate duplicates */
949
950 for (i = 0; i < n; ++i) {
951 for (j = i + 1; j < n; ++j) {
952 if (doubleAlmostEqualZero (xx[i], xx[j]) && doubleAlmostEqualZero (yy[i], yy[j])) /* Duplicate */
953 sides[j] = 99; /* Mark as duplicate */
954 }
955 }
956 for (i = 1; i < n; i++) {
957 if (sides[i] == 99) { /* This is a duplicate, overwrite */
958 for (j = i + 1; j < n; j++) {
959 xx[j-1] = xx[j];
960 yy[j-1] = yy[j];
961 sides[j-1] = sides[j];
962 }
963 n--;
964 i--; /* Must start at same point again */
965 }
966 }
967
968 for (i = 0; i < n; i++) gmt_xy_to_geo (GMT, &clon[i], &clat[i], xx[i], yy[i]);
969
970 if (gmt_M_is_cartesian (GMT, GMT_IN)) return (n);
971
972 if (n < 2) return (n);
973
974 /* Check for corner xover if n == 2 */
975
976 if (gmtmap_is_rect_corner (GMT, xx[0], yy[0])) return (1);
977
978 if (gmtmap_is_rect_corner (GMT, xx[1], yy[1])) {
979 clon[0] = clon[1];
980 clat[0] = clat[1];
981 xx[0] = xx[1];
982 yy[0] = yy[1];
983 sides[0] = sides[1];
984 return (1);
985 }
986
987 /* Sort the two intermediate points into the right order based on projected distances from the first point */
988
989 if (hypot (x0 - xx[1], y0 - yy[1]) < hypot (x0 - xx[0], y0 - yy[0])) {
990 gmt_M_double_swap (clon[0], clon[1]);
991 gmt_M_double_swap (clat[0], clat[1]);
992 gmt_M_double_swap (xx[0], xx[1]);
993 gmt_M_double_swap (yy[0], yy[1]);
994 gmt_M_uint_swap (sides[0], sides[1]);
995 }
996
997 return (2);
998 }
999
1000 /*! . */
gmtmap_radial_crossing(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)1001 GMT_LOCAL unsigned int gmtmap_radial_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
1002 /* Computes the lon/lat of a point that is f_horizon spherical degrees from
1003 * the origin and lies on the great circle between points 1 and 2 */
1004
1005 double dist1, dist2, delta, eps, dlon;
1006
1007 dist1 = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon1, lat1);
1008 dist2 = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon2, lat2);
1009 delta = dist2 - dist1;
1010 eps = (doubleAlmostEqualZero (dist1, dist2)) ? 0.0 : (GMT->current.proj.f_horizon - dist1) / delta;
1011 gmt_M_set_delta_lon (lon1, lon2, dlon);
1012 clon[0] = lon1 + dlon * eps;
1013 clat[0] = lat1 + (lat2 - lat1) * eps;
1014
1015 gmt_geo_to_xy (GMT, clon[0], clat[0], &xx[0], &yy[0]);
1016
1017 sides[0] = 1;
1018
1019 return (1);
1020 }
1021
1022 /*! . */
gmtmap_genper_crossing(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double * clon,double * clat,double * xx,double * yy,unsigned int * sides)1023 GMT_LOCAL unsigned int gmtmap_genper_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, unsigned int *sides) {
1024 /* Awkward case of windowing which gives a boundary that is a clipped circle.
1025 * Here, the rectangular and circular boundaries take turns in being the definitive boundary.
1026 * Approach: Determine how the points do relative to either boundary. */
1027
1028 bool h_out[2], r_out[2];
1029
1030 /* Check if point 1 is beyond horizon: */
1031 h_out[0] = gmtmap_radial_outside (GMT, lon1, lat1); /* true if point 1 is beyond the horizon */
1032 h_out[1] = gmtmap_radial_outside (GMT, lon2, lat2); /* true if point 2 is beyond the horizon */
1033 r_out[0] = gmtmap_rect_outside (GMT, lon1, lat1); /* true if point 1 is beyond the map box */
1034 r_out[1] = gmtmap_rect_outside (GMT, lon2, lat2); /* true if point 2 is beyond the map box */
1035 if (h_out[0] == false && h_out[1] == false) /* We are not beyond horizon so technically inside unless clipped by the window: */
1036 return (gmtmap_rect_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1037 if (r_out[0] == false && r_out[1] == false) /* We are not outside the map box but might be beyond horizon: */
1038 return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1039 /* Here we have a mixed bag. Try our best */
1040 if (h_out[0] == false && r_out[0] == true) /* Point 1 is outside box but inside horizon. That means point 2 is inside and we use rect_crossing */
1041 return (gmtmap_rect_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1042 if (h_out[0] == true && r_out[0] == false) /* Point 1 is beyond horizon but inside box. That means point 2 is inside and we use gmtmap_radial_crossing */
1043 return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1044 if (h_out[1] == false && r_out[1] == true) /* Point 2 is outside box but inside horizon. That means point 1 is inside and we use rect_crossing */
1045 return (gmtmap_rect_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1046 if (h_out[1] == true && r_out[1] == false) /* Point 2 is beyond horizon but inside box. That means point 1 is inside and we use gmtmap_radial_crossing */
1047 return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1048 /* Don't think we should get here... */
1049 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in gmtmap_genper_crossing: None of the cases matched crossing scenario");
1050 return (gmtmap_radial_crossing (GMT, lon1, lat1, lon2, lat2, clon, clat, xx, yy, sides));
1051
1052 }
1053
gmtmap_jump_x(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)1054 GMT_LOCAL int gmtmap_jump_x (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
1055 /* true if x-distance between points exceeds 1/2 map width at this y value */
1056 double dx, map_half_size, half_lon_range;
1057
1058 if (!(gmt_M_is_cylindrical (GMT) || gmt_M_is_perspective (GMT) || gmt_M_is_misc (GMT))) return (0); /* Only projections with periodic boundaries may apply */
1059
1060 if (gmt_M_is_cartesian (GMT, GMT_IN) || fabs (GMT->common.R.wesn[XLO] - GMT->common.R.wesn[XHI]) < 90.0) return (0);
1061
1062 map_half_size = MAX (gmt_half_map_width (GMT, y0), gmt_half_map_width (GMT, y1));
1063 if (fabs (map_half_size) < GMT_CONV4_LIMIT) return (0);
1064
1065 half_lon_range = (GMT->common.R.oblique) ? 180.0 : 0.5 * (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]);
1066 dx = x1 - x0;
1067 if (fabs (dx) > map_half_size) { /* Possible jump; let's see how far apart those longitudes really are */
1068 /* This test on longitudes was added to deal with issue #672, also see test/psxy/nojump.sh */
1069 double last_lon = 0.0, this_lon = 0.0, dummy = 0.0, dlon;
1070 gmt_xy_to_geo (GMT, &last_lon, &dummy, x0, y0);
1071 gmt_xy_to_geo (GMT, &this_lon, &dummy, x1, y1);
1072 gmt_M_set_delta_lon (last_lon, this_lon, dlon); /* Beware of jumps due to sign differences */
1073 if (fabs (dlon) < half_lon_range) /* Not going the long way so we judge this to be no jump */
1074 return (0);
1075 /* Jump it is */
1076 if (dx > map_half_size) return (-1); /* Cross left/west boundary */
1077 if (dx < (-map_half_size)) return (1); /* Cross right/east boundary */
1078 }
1079 return (0);
1080 }
1081
gmtmap_jump_xy(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)1082 GMT_LOCAL int gmtmap_jump_xy (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
1083 /* true if x- or y-distance between points exceeds 1/2 map width at this y value or map height */
1084 double dx, dy, map_half_width, map_half_height;
1085
1086 if (!(gmt_M_is_cylindrical (GMT) || gmt_M_is_perspective (GMT) || gmt_M_is_misc (GMT))) return (0); /* Only projections with periodic boundaries may apply */
1087
1088 if (gmt_M_is_cartesian (GMT, GMT_IN) || fabs (GMT->common.R.wesn[XLO] - GMT->common.R.wesn[XHI]) < 90.0) return (0);
1089
1090 map_half_width = MAX (gmt_half_map_width (GMT, y0), gmt_half_map_width (GMT, y1));
1091 if (fabs (map_half_width) < GMT_CONV4_LIMIT) return (0); /* Very close to a pole for some projections */
1092 map_half_height = 0.5 * GMT->current.map.height; /* We assume this is constant */
1093
1094 dx = x1 - x0;
1095 if (fabs (dx) > map_half_width) { /* Possible jump; let's see how far apart those longitudes really are */
1096 /* This test on longitudes was added to deal with issue #672, also see test/psxy/nojump.sh */
1097 double last_lon = 0.0, this_lon = 0.0, dummy = 0.0, dlon;
1098 double half_lon_range = (GMT->common.R.oblique) ? 180.0 : 0.5 * (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]);
1099 gmt_xy_to_geo (GMT, &last_lon, &dummy, x0, y0);
1100 gmt_xy_to_geo (GMT, &this_lon, &dummy, x1, y1);
1101 gmt_M_set_delta_lon (last_lon, this_lon, dlon); /* Beware of jumps due to sign differences */
1102 if (fabs (dlon) < half_lon_range) /* Not going the long way so we judge this to be no jump */
1103 return (0);
1104 /* Jump it is */
1105 if (dx > map_half_width) return (-1); /* Cross left/west boundary */
1106 if (dx < (-map_half_width)) return (1); /* Cross right/east boundary */
1107 }
1108 else if (fabs ((dy = y1 - y0)) > map_half_height) { /* Possible jump for TM or UTM */
1109 /* Jump it is */
1110 if (dy > map_half_height) return (-2); /* Cross bottom/south boundary */
1111 if (dy < (-map_half_height)) return (2); /* Cross top/north boundary */
1112 }
1113 return (0); /* No jump */
1114 }
1115
gmtmap_jump_not(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)1116 GMT_LOCAL int gmtmap_jump_not (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
1117 #if 0
1118 double dx, map_half_size;
1119 /* For Genper we believe there should not be jumps [Introduced to test issue #667] */
1120 /* However, it does not work for all cases. Consider this one
1121 * psbasemap -Rg -JG280.969616634/58.7193421224/2513/0/0/0/200/180/6i -Ba30f30g30/a5f5g5NWSE -P
1122 * which draws jump lines across. I believe this is related to the fact that in this ase
1123 * the map region is a clipped circle and we have no effective way of determining which points
1124 * are outside the map of not.
1125 */
1126 map_half_size = MAX (gmt_half_map_width (GMT, y0), gmt_half_map_width (GMT, y1));
1127 if (fabs (map_half_size) < GMT_CONV4_LIMIT) return (0);
1128 dx = x1 - x0;
1129 if (dx > map_half_size) return (-1); /* Cross left/west boundary */
1130 if (dx < (-map_half_size)) return (1); /* Cross right/east boundary */
1131 #else
1132 gmt_M_unused(GMT);
1133 gmt_M_unused(x0);
1134 gmt_M_unused(y0);
1135 gmt_M_unused(x1);
1136 gmt_M_unused(y1);
1137 #endif
1138 return (0);
1139 }
1140
1141 #if 0
1142 /* NOT USED ?? */
1143 GMT_LOCAL int gmtmap_ellipse_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, int *sides) {
1144 /* Compute the crossover point(s) on the map boundary for rectangular projections */
1145 int n = 0, i, jump;
1146 double x1, x2, y1, y2;
1147
1148 /* Crossings here must be at the W or E borders. Lat points may only touch border */
1149
1150 if (lat1 <= -90.0) {
1151 sides[n] = 0;
1152 clon[n] = lon1;
1153 clat[n] = lat1;
1154 n = 1;
1155 }
1156 else if (lat2 <= -90.0) {
1157 sides[n] = 0;
1158 clon[n] = lon2;
1159 clat[n] = lat2;
1160 n = 1;
1161 }
1162 else if (lat1 >= 90.0) {
1163 sides[n] = 2;
1164 clon[n] = lon1;
1165 clat[n] = lat1;
1166 n = 1;
1167 }
1168 else if (lat2 >= 90.0) {
1169 sides[n] = 2;
1170 clon[n] = lon2;
1171 clat[n] = lat2;
1172 n = 1;
1173 }
1174 else { /* May cross somewhere else */
1175 gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
1176 gmt_geo_to_xy (GMT, lon2, lat2, &x2, &y2);
1177 if ((jump = gmtmap_jump_x (GMT, x2, y2, x1, y1))) {
1178 (*GMT->current.map.get_crossings) (GMT, xx, yy, x2, y2, x1, y1);
1179 if (jump == 1) { /* Add right border point first */
1180 gmt_M_double_swap (xx[0], xx[1]);
1181 gmt_M_double_swap (yy[0], yy[1]);
1182 }
1183 gmt_xy_to_geo (GMT, &clon[0], &clat[0], xx[0], yy[0]);
1184 gmt_xy_to_geo (GMT, &clon[1], &clat[1], xx[1], yy[1]);
1185 n = 2;
1186 }
1187 }
1188 if (n == 1) for (i = 0; i < n; i++) gmt_geo_to_xy (GMT, clon[i], clat[i], &xx[i], &yy[i]);
1189 return (n);
1190 }
1191
1192 /* NOT USED ?? */
1193 GMT_LOCAL bool gmtmap_eqdist_outside (struct GMT_CTRL *GMT, double lon, double lat) {
1194 double cc, s, c;
1195
1196 lon -= GMT->current.proj.central_meridian;
1197 while (lon < -180.0) lon += 360.0;
1198 while (lon > 180.0) lon -= 360.0;
1199 sincosd (lat, &s, &c);
1200 cc = GMT->current.proj.sinp * s + GMT->current.proj.cosp * c * cosd (lon);
1201 if (cc < -1.0) {
1202 GMT->current.map.this_y_status = -1;
1203 GMT->current.map.this_x_status = 0;
1204 }
1205 else
1206 GMT->current.map.this_x_status = GMT->current.map.this_y_status = 0;
1207 return (GMT->current.map.this_y_status != 0);
1208 }
1209
1210 GMT_LOCAL int gmtmap_eqdist_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *clon, double *clat, double *xx, double *yy, int *sides) {
1211 double angle, x, y, s, c;
1212
1213 /* Computes the x.y of the antipole point that lies on a radius from
1214 * the origin through the inside point */
1215
1216 if (gmtmap_eqdist_outside (GMT, lon1, lat1)) { /* Point 1 is on perimeter */
1217 gmt_geo_to_xy (GMT, lon2, lat2, &x, &y);
1218 angle = d_atan2 (y - GMT->current.proj.origin[GMT_Y], x - GMT->current.proj.origin[GMT_X]);
1219 sincos (angle, &s, &c);
1220 xx[0] = GMT->current.proj.r * c + GMT->current.proj.origin[GMT_X];
1221 yy[0] = GMT->current.proj.r * s + GMT->current.proj.origin[GMT_Y];
1222 clon[0] = lon1;
1223 clat[0] = lat1;
1224 }
1225 else { /* Point 2 is on perimeter */
1226 gmt_geo_to_xy (GMT, lon1, lat1, &x, &y);
1227 angle = d_atan2 (y - GMT->current.proj.origin[GMT_Y], x - GMT->current.proj.origin[GMT_X]);
1228 sincos (angle, &s, &c);
1229 xx[0] = GMT->current.proj.r * c + GMT->current.proj.origin[GMT_X];
1230 yy[0] = GMT->current.proj.r * s + GMT->current.proj.origin[GMT_Y];
1231 clon[0] = lon2;
1232 clat[0] = lat2;
1233 }
1234 sides[0] = 1;
1235
1236 return (1);
1237 }
1238 #endif
1239
1240 /* Routines to do with clipping */
1241
1242 /*! . */
gmtmap_crossing(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double * xlon,double * xlat,double * xx,double * yy,unsigned int * sides)1243 GMT_LOCAL unsigned int gmtmap_crossing (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double *xlon, double *xlat, double *xx, double *yy, unsigned int *sides) {
1244 if (GMT->current.map.prev_x_status == GMT->current.map.this_x_status && GMT->current.map.prev_y_status == GMT->current.map.this_y_status) {
1245 /* This is naive. We could have two points outside with a line drawn between crossing the plotting area. */
1246 return (0);
1247 }
1248 else if ((GMT->current.map.prev_x_status == 0 && GMT->current.map.prev_y_status == 0) || (GMT->current.map.this_x_status == 0 && GMT->current.map.this_y_status == 0)) {
1249 /* This is a crossing */
1250 }
1251 else if (!(*GMT->current.map.overlap) (GMT, lon1, lat1, lon2, lat2)) /* Less clearcut case, check for overlap */
1252 return (0);
1253
1254 /* Now compute the crossing */
1255
1256 GMT->current.map.corner = -1;
1257 return ((*GMT->current.map.crossing) (GMT, lon1, lat1, lon2, lat2, xlon, xlat, xx, yy, sides));
1258 }
1259
1260 /*! . */
gmtmap_x_to_corner(struct GMT_CTRL * GMT,double x)1261 GMT_LOCAL double gmtmap_x_to_corner (struct GMT_CTRL *GMT, double x) {
1262 return ( (fabs (x - GMT->current.proj.rect[XLO]) < fabs (x - GMT->current.proj.rect[XHI])) ? GMT->current.proj.rect[XLO] : GMT->current.proj.rect[XHI]);
1263 }
1264
1265 /*! . */
gmtmap_y_to_corner(struct GMT_CTRL * GMT,double y)1266 GMT_LOCAL double gmtmap_y_to_corner (struct GMT_CTRL *GMT, double y) {
1267 return ( (fabs (y - GMT->current.proj.rect[YLO]) < fabs (y - GMT->current.proj.rect[YHI])) ? GMT->current.proj.rect[YLO] : GMT->current.proj.rect[YHI]);
1268 }
1269
1270 /*! . */
gmtmap_move_to_rect(struct GMT_CTRL * GMT,double * x_edge,double * y_edge,uint64_t j,uint64_t nx)1271 GMT_LOCAL uint64_t gmtmap_move_to_rect (struct GMT_CTRL *GMT, double *x_edge, double *y_edge, uint64_t j, uint64_t nx) {
1272 uint64_t n = 0;
1273 int key;
1274 double xtmp, ytmp;
1275
1276 /* May add 0, 1, or 2 points to path */
1277
1278 if (GMT->current.map.this_x_status == 0 && GMT->current.map.this_y_status == 0) return (1); /* Completely Inside */
1279
1280 if (!nx && j > 0 && GMT->current.map.this_x_status != GMT->current.map.prev_x_status && GMT->current.map.this_y_status != GMT->current.map.prev_y_status) { /* Must include corner */
1281 xtmp = x_edge[j]; ytmp = y_edge[j];
1282 if ((GMT->current.map.this_x_status * GMT->current.map.prev_x_status) == -4 || (GMT->current.map.this_y_status * GMT->current.map.prev_y_status) == -4) { /* the two points outside on opposite sides */
1283 x_edge[j] = (GMT->current.map.prev_x_status < 0) ? GMT->current.proj.rect[XLO] : ((GMT->current.map.prev_x_status > 0) ? GMT->current.proj.rect[XHI] : gmtmap_x_to_corner (GMT, x_edge[j-1]));
1284 y_edge[j] = (GMT->current.map.prev_y_status < 0) ? GMT->current.proj.rect[YLO] : ((GMT->current.map.prev_y_status > 0) ? GMT->current.proj.rect[YHI] : gmtmap_y_to_corner (GMT, y_edge[j-1]));
1285 j++;
1286 x_edge[j] = (GMT->current.map.this_x_status < 0) ? GMT->current.proj.rect[XLO] : ((GMT->current.map.this_x_status > 0) ? GMT->current.proj.rect[XHI] : gmtmap_x_to_corner (GMT, xtmp));
1287 y_edge[j] = (GMT->current.map.this_y_status < 0) ? GMT->current.proj.rect[YLO] : ((GMT->current.map.this_y_status > 0) ? GMT->current.proj.rect[YHI] : gmtmap_y_to_corner (GMT, ytmp));
1288 j++;
1289 }
1290 else {
1291 key = MIN (GMT->current.map.this_x_status, GMT->current.map.prev_x_status);
1292 x_edge[j] = (key < 0) ? GMT->current.proj.rect[XLO] : GMT->current.proj.rect[XHI];
1293 key = MIN (GMT->current.map.this_y_status, GMT->current.map.prev_y_status);
1294 y_edge[j] = (key < 0) ? GMT->current.proj.rect[YLO] : GMT->current.proj.rect[YHI];
1295 j++;
1296 }
1297 x_edge[j] = xtmp; y_edge[j] = ytmp;
1298 n = 1;
1299 }
1300
1301 if (GMT->current.map.outside == gmtmap_rect_outside2) { /* Need special check because this outside2 test is screwed up... */
1302 if (x_edge[j] < GMT->current.proj.rect[XLO]) {
1303 x_edge[j] = GMT->current.proj.rect[XLO];
1304 GMT->current.map.this_x_status = -2;
1305 }
1306 else if (x_edge[j] > GMT->current.proj.rect[XHI]) {
1307 x_edge[j] = GMT->current.proj.rect[XHI];
1308 GMT->current.map.this_x_status = 2;
1309 }
1310 if (y_edge[j] < GMT->current.proj.rect[YLO]) {
1311 y_edge[j] = GMT->current.proj.rect[YLO];
1312 GMT->current.map.this_y_status = -2;
1313 }
1314 else if (y_edge[j] > GMT->current.proj.rect[YHI]) {
1315 y_edge[j] = GMT->current.proj.rect[YHI];
1316 GMT->current.map.this_y_status = 2;
1317 }
1318 }
1319 else {
1320 if (GMT->current.map.this_x_status != 0) x_edge[j] = (GMT->current.map.this_x_status < 0) ? GMT->current.proj.rect[XLO] : GMT->current.proj.rect[XHI];
1321 if (GMT->current.map.this_y_status != 0) y_edge[j] = (GMT->current.map.this_y_status < 0) ? GMT->current.proj.rect[YLO] : GMT->current.proj.rect[YHI];
1322 }
1323
1324 return (n + 1);
1325 }
1326
1327 /*! . */
gmtmap_rect_clip_old(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n,double ** x,double ** y,uint64_t * total_nx)1328 GMT_LOCAL uint64_t gmtmap_rect_clip_old (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n, double **x, double **y, uint64_t *total_nx) {
1329 uint64_t i, j = 0;
1330 unsigned int nx, k, sides[4];
1331 double xlon[4], xlat[4], xc[4], yc[4];
1332
1333 *total_nx = 0; /* Keep track of total of crossings */
1334
1335 if (n == 0) return (0);
1336
1337 gmt_prep_tmp_arrays (GMT, GMT_NOTSET, 1, 2); /* Init or reallocate tmp vectors */
1338 (void) gmt_map_outside (GMT, lon[0], lat[0]);
1339 gmt_geo_to_xy (GMT, lon[0], lat[0], &GMT->hidden.mem_coord[GMT_X][0], &GMT->hidden.mem_coord[GMT_Y][0]);
1340 j += gmtmap_move_to_rect (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], 0, 0);
1341 for (i = 1; i < n; i++) {
1342 (void) gmt_map_outside (GMT, lon[i], lat[i]);
1343 nx = gmtmap_crossing (GMT, lon[i-1], lat[i-1], lon[i], lat[i], xlon, xlat, xc, yc, sides);
1344 for (k = 0; k < nx; k++) {
1345 gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j, 2); /* Init or reallocate tmp vectors */
1346 GMT->hidden.mem_coord[GMT_X][j] = xc[k];
1347 GMT->hidden.mem_coord[GMT_Y][j++] = yc[k];
1348 (*total_nx) ++;
1349 }
1350 gmt_geo_to_xy (GMT, lon[i], lat[i], &GMT->hidden.mem_coord[GMT_X][j], &GMT->hidden.mem_coord[GMT_Y][j]);
1351 gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j+2, 2); /* Init or reallocate tmp vectors */
1352 j += gmtmap_move_to_rect (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], j, nx); /* May add 2 points, which explains the j+2 stuff */
1353 }
1354
1355 *x = gmtlib_assign_vector (GMT, j, GMT_X);
1356 *y = gmtlib_assign_vector (GMT, j, GMT_Y);
1357
1358 return (j);
1359 }
1360
1361 /* Functions and macros used for new rectangular clipping using the Sutherland/Hodgman algorithm
1362 * in which we clip the polygon against each of the 4 sides. To avoid lots of if/switch I have
1363 * two clip functions (for x and y line) and two in/out functions that tells us if a point is
1364 * inside the polygon relative to the line. Then, pointers to these functions are passed to
1365 * make sure the right functions are used for each side in the loop over sides. Unless I can
1366 * figure out a more clever recursive way I need to have 2 temporary arrays to shuffle the
1367 * intermediate results around.
1368 *
1369 * P.Wessel, March 2008
1370 */
1371
1372 /*! This macro calculates the x-coordinates where the line segment crosses the border x = border.
1373 * By swapping x and y in the call we can use it for finding the y intersection. This macro is
1374 * never called when (y_prev - y_curr) = 0 so we don't divide by zero.
1375 */
1376 #define INTERSECTION_COORD(x_curr,y_curr,x_prev,y_prev,border) x_curr + (x_prev - x_curr) * (border - y_curr) / (y_prev - y_curr)
1377
1378 /*! . */
gmtmap_clip_sn(double x_prev,double y_prev,double x_curr,double y_curr,double x[],double y[],double border,bool (* inside)(double,double),bool (* outside)(double,double),int * cross)1379 GMT_LOCAL unsigned int gmtmap_clip_sn (double x_prev, double y_prev, double x_curr, double y_curr, double x[], double y[], double border, bool (*inside) (double, double), bool (*outside) (double, double), int *cross) {
1380 /* Clip against the south or north boundary (i.e., a horizontal line with y = border) */
1381 *cross = 0;
1382 if (doubleAlmostEqualZero (x_prev, x_curr) && doubleAlmostEqualZero (y_prev, y_curr))
1383 return (0); /* Do nothing for duplicates */
1384 if (outside (y_prev, border)) { /* Previous point is outside... */
1385 if (outside (y_curr, border)) return 0; /* ...as is the current point. Do nothing. */
1386 /* Here, the line segment intersects the border - return both intersection and inside point */
1387 y[0] = border; x[0] = INTERSECTION_COORD (x_curr, y_curr, x_prev, y_prev, border);
1388 *cross = +1; /* Crossing to the inside */
1389 x[1] = x_curr; y[1] = y_curr; return (2);
1390 }
1391 /* Here x_prev is inside */
1392 if (inside (y_curr, border)) { /* Return current point only */
1393 x[0] = x_curr; y[0] = y_curr; return (1);
1394 }
1395 /* Segment intersects border - return intersection only */
1396 *cross = -1; /* Crossing to the outside */
1397 y[0] = border; x[0] = INTERSECTION_COORD (x_curr, y_curr, x_prev, y_prev, border); return (1);
1398 }
1399
1400 /*! . */
gmtmap_clip_we(double x_prev,double y_prev,double x_curr,double y_curr,double x[],double y[],double border,bool (* inside)(double,double),bool (* outside)(double,double),int * cross)1401 GMT_LOCAL unsigned int gmtmap_clip_we (double x_prev, double y_prev, double x_curr, double y_curr, double x[], double y[], double border, bool (*inside) (double, double), bool (*outside) (double, double), int *cross) {
1402 /* Clip against the west or east boundary (i.e., a vertical line with x = border) */
1403 *cross = 0;
1404 if (doubleAlmostEqualZero (x_prev, x_curr) && doubleAlmostEqualZero (y_prev, y_curr))
1405 return (0); /* Do nothing for duplicates */
1406 if (outside (x_prev, border)) { /* Previous point is outside... */
1407 if (outside (x_curr, border)) return 0; /* ...as is the current point. Do nothing. */
1408 /* Here, the line segment intersects the border - return both intersection and inside point */
1409 x[0] = border; y[0] = INTERSECTION_COORD (y_curr, x_curr, y_prev, x_prev, border);
1410 *cross = +1; /* Crossing to the inside */
1411 x[1] = x_curr; y[1] = y_curr; return (2);
1412 }
1413 /* Here x_prev is inside */
1414 if (inside (x_curr, border)) { /* Return current point only */
1415 x[0] = x_curr; y[0] = y_curr; return (1);
1416 }
1417 /* Segment intersects border - return intersection only */
1418 *cross = -1; /* Crossing to the outside */
1419 x[0] = border; y[0] = INTERSECTION_COORD (y_curr, x_curr, y_prev, x_prev, border); return (1);
1420 }
1421
1422 /* Tiny functions to tell if a value is <, <=, >=, > than the limit */
gmtmap_inside_lower_boundary(double val,double min)1423 static inline bool gmtmap_inside_lower_boundary (double val, double min) {return (val >= min);}
gmtmap_inside_upper_boundary(double val,double max)1424 static inline bool gmtmap_inside_upper_boundary (double val, double max) {return (val <= max);}
gmtmap_outside_lower_boundary(double val,double min)1425 static inline bool gmtmap_outside_lower_boundary (double val, double min) {return (val < min);}
gmtmap_outside_upper_boundary(double val,double max)1426 static inline bool gmtmap_outside_upper_boundary (double val, double max) {return (val > max);}
1427
1428 /*! gmtmap_rect_clip is an implementation of the Sutherland/Hodgman algorithm polygon clipping algorithm.
1429 * Basically, it compares the polygon to one boundary at the time, and clips the polygon to be inside
1430 * that boundary; this is then repeated for all boundaries. Assumptions here are Cartesian coordinates
1431 * so all boundaries are straight lines in x or y. */
gmtmap_rect_clip(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n,double ** x,double ** y,uint64_t * total_nx)1432 GMT_LOCAL uint64_t gmtmap_rect_clip (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n, double **x, double **y, uint64_t *total_nx) {
1433 uint64_t i, n_get, m;
1434 size_t n_alloc = 0;
1435 unsigned int side, in = 1, out = 0, j, np;
1436 int cross = 0;
1437 bool polygon;
1438 double *xtmp[2] = {NULL, NULL}, *ytmp[2] = {NULL, NULL}, xx[2], yy[2], border[4];
1439 unsigned int (*clipper[4]) (double, double, double, double, double *, double *, double, bool (*inside) (double, double), bool (*outside) (double, double), int *);
1440 bool (*inside[4]) (double, double);
1441 bool (*outside[4]) (double, double);
1442
1443 #ifdef DEBUG
1444 FILE *fp = NULL;
1445 bool dump = false;
1446 #endif
1447
1448 if (n == 0) return (0);
1449
1450 polygon = (n > 2 && !gmt_polygon_is_open (GMT, lon, lat, n)); /* true if input segment is a closed polygon */
1451
1452 *total_nx = 1; /* So that calling program will not discard the clipped polygon */
1453
1454 /* Set up function pointers. This could be done once in gmt_begin at some point */
1455
1456 clipper[GMT_BOTTOM] = gmtmap_clip_sn; clipper[GMT_RIGHT] = gmtmap_clip_we; clipper[GMT_TOP] = gmtmap_clip_sn; clipper[GMT_LEFT] = gmtmap_clip_we;
1457 inside[GMT_RIGHT] = inside[GMT_TOP] = gmtmap_inside_upper_boundary; outside[GMT_RIGHT] = outside[GMT_TOP] = gmtmap_outside_upper_boundary;
1458 inside[GMT_BOTTOM] = inside[GMT_LEFT] = gmtmap_inside_lower_boundary; outside[GMT_BOTTOM] = outside[GMT_LEFT] = gmtmap_outside_lower_boundary;
1459 border[GMT_BOTTOM] = border[GMT_LEFT] = 0.0; border[GMT_RIGHT] = GMT->current.map.width; border[GMT_TOP] = GMT->current.map.height;
1460
1461 n_get = lrint (1.05*n+5); /* Anticipate just a few crossings (5%)+5, allocate more later if needed */
1462 /* Create a pair of arrays for holding input and output */
1463 gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], n_get, &n_alloc, double);
1464
1465 /* Get Cartesian map coordinates */
1466
1467 for (m = 0; m < n; m++) gmt_geo_to_xy (GMT, lon[m], lat[m], &xtmp[0][m], &ytmp[0][m]);
1468
1469 #ifdef DEBUG
1470 if (dump) {
1471 fp = fopen ("input.d", "w");
1472 for (i = 0; i < n; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1473 fclose (fp);
1474 }
1475 #endif
1476 for (side = 0; side < 4; side++) { /* Must clip polygon against a single border, one border at a time */
1477 n = m; /* Current size of polygon */
1478 m = 0; /* Start with nuthin' */
1479
1480 gmt_M_uint_swap (in, out); /* Swap what is input and output for clipping against this border */
1481 if (n) {
1482 /* Must ensure we copy the very first point if it is inside the clip rectangle */
1483 if (inside[side] ((side%2) ? xtmp[in][0] : ytmp[in][0], border[side])) {xtmp[out][0] = xtmp[in][0]; ytmp[out][0] = ytmp[in][0]; m = 1;} /* First point is inside; add it */
1484 }
1485 for (i = 1; i < n; i++) { /* For each line segment */
1486 np = clipper[side] (xtmp[in][i-1], ytmp[in][i-1], xtmp[in][i], ytmp[in][i], xx, yy, border[side], inside[side], outside[side], &cross); /* Returns 0, 1, or 2 points */
1487 for (j = 0; j < np; j++) { /* Add the np returned points to the new clipped polygon path */
1488 if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1489 xtmp[out][m] = xx[j]; ytmp[out][m] = yy[j]; m++;
1490 }
1491 }
1492 if (polygon && gmt_polygon_is_open (GMT, xtmp[out], ytmp[out], m)) { /* Do we need to explicitly close this clipped polygon? */
1493 if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1494 xtmp[out][m] = xtmp[out][0]; ytmp[out][m] = ytmp[out][0]; m++; /* Yes. */
1495 }
1496 }
1497
1498 gmt_M_free (GMT, xtmp[1]); /* Free the pairs of arrays that holds the last input array */
1499 gmt_M_free (GMT, ytmp[1]);
1500
1501 if (m) { /* Reallocate and return the array with the final clipped polygon */
1502 n_alloc = m;
1503 gmt_M_malloc2 (GMT, xtmp[0], ytmp[0], 0U, &n_alloc, double);
1504 *x = xtmp[0];
1505 *y = ytmp[0];
1506 #ifdef DEBUG
1507 if (dump) {
1508 fp = fopen ("output.d", "w");
1509 for (i = 0; i < m; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1510 fclose (fp);
1511 }
1512 #endif
1513 if (GMT->common.R.oblique) { /* Ensure region corners are added if any poles are enclosed */
1514 double ycoord_pole = 0;
1515 bool do_it = true;
1516 if (GMT->current.proj.corner[0] && GMT->current.proj.corner[1]) /* Ensure S pole is included */
1517 ycoord_pole = GMT->current.proj.rect[YLO];
1518 else if (GMT->current.proj.corner[2] && GMT->current.proj.corner[3]) /* Ensure N pole is included */
1519 ycoord_pole = GMT->current.proj.rect[YHI];
1520 else
1521 do_it = false;
1522
1523 if (do_it) {
1524 for (i = 1; i < m; i++) {
1525 if (doubleAlmostEqual (fabs (xtmp[0][i] - xtmp[0][i-1]), GMT->current.map.width))
1526 ytmp[0][i] = ytmp[0][i-1] = ycoord_pole;
1527 }
1528 }
1529 }
1530 }
1531 else { /* Nothing survived the clipping - free the output arrays */
1532 gmt_M_free (GMT, xtmp[0]);
1533 gmt_M_free (GMT, ytmp[0]);
1534 }
1535
1536 return (m);
1537 }
1538
1539 /* map_wesn_clip differs from gmtmap_rect_clip in that the boundaries of constant lon or lat may end up as
1540 * curved lines depending on the map projection. Thus, if a line crosses the boundary and reenters at
1541 * another point on the boundary then the straight line between these crossing points should really
1542 * project to a curved boundary segment. The H-S algorithm was originally rectangular so we got straight
1543 * lines. Here, we check if (1) the particular boundary being tested is curved, and if true then we
1544 * keep track of the indices of the exit and entry points in the array, and once a boundary has been
1545 * processed we must add more points between the exit and entry pairs to properly handle the curved
1546 * segment. The arrays x_index and x_type stores the index of the exit/entry points and the type
1547 * (+1 we enter, -1 we exit). We then use gmtlib_map_path to compute the required segments to insert.
1548 * P. Wessel, 2--9-05-07
1549 */
1550
1551 /*! . */
gmtmap_lon_to_corner(struct GMT_CTRL * GMT,double lon)1552 GMT_LOCAL double gmtmap_lon_to_corner (struct GMT_CTRL *GMT, double lon) {
1553 return ( (fabs (lon - GMT->common.R.wesn[XLO]) < fabs (lon - GMT->common.R.wesn[XHI])) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI]);
1554 }
1555
1556 /*! . */
gmtmap_lat_to_corner(struct GMT_CTRL * GMT,double lat)1557 GMT_LOCAL double gmtmap_lat_to_corner (struct GMT_CTRL *GMT, double lat) {
1558 return ( (fabs (lat - GMT->common.R.wesn[YLO]) < fabs (lat - GMT->common.R.wesn[YHI])) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI]);
1559 }
1560
1561 /*! . */
gmtmap_move_to_wesn(struct GMT_CTRL * GMT,double * x_edge,double * y_edge,double lon,double lat,double lon_old,double lat_old,uint64_t j,uint64_t nx)1562 GMT_LOCAL int gmtmap_move_to_wesn (struct GMT_CTRL *GMT, double *x_edge, double *y_edge, double lon, double lat, double lon_old, double lat_old, uint64_t j, uint64_t nx) {
1563 int n = 0, key;
1564 double xtmp, ytmp, lon_p, lat_p;
1565
1566 /* May add 0, 1, or 2 points to path */
1567
1568 if (!nx && j > 0 && GMT->current.map.this_x_status != GMT->current.map.prev_x_status && GMT->current.map.this_y_status != GMT->current.map.prev_y_status) { /* Need corner */
1569 xtmp = x_edge[j]; ytmp = y_edge[j];
1570 if ((GMT->current.map.this_x_status * GMT->current.map.prev_x_status) == -4 || (GMT->current.map.this_y_status * GMT->current.map.prev_y_status) == -4) { /* the two points outside on opposite sides */
1571 lon_p = (GMT->current.map.prev_x_status < 0) ? GMT->common.R.wesn[XLO] : ((GMT->current.map.prev_x_status > 0) ? GMT->common.R.wesn[XHI] : gmtmap_lon_to_corner (GMT, lon_old));
1572 lat_p = (GMT->current.map.prev_y_status < 0) ? GMT->common.R.wesn[YLO] : ((GMT->current.map.prev_y_status > 0) ? GMT->common.R.wesn[YHI] : gmtmap_lat_to_corner (GMT, lat_old));
1573 gmt_geo_to_xy (GMT, lon_p, lat_p, &x_edge[j], &y_edge[j]);
1574 j++;
1575 lon_p = (GMT->current.map.this_x_status < 0) ? GMT->common.R.wesn[XLO] : ((GMT->current.map.this_x_status > 0) ? GMT->common.R.wesn[XHI] : gmtmap_lon_to_corner (GMT, lon));
1576 lat_p = (GMT->current.map.this_y_status < 0) ? GMT->common.R.wesn[YLO] : ((GMT->current.map.this_y_status > 0) ? GMT->common.R.wesn[YHI] : gmtmap_lat_to_corner (GMT, lat));
1577 gmt_geo_to_xy (GMT, lon_p, lat_p, &x_edge[j], &y_edge[j]);
1578 j++;
1579 }
1580 else {
1581 key = MIN (GMT->current.map.this_x_status, GMT->current.map.prev_x_status);
1582 lon_p = (key < 0) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
1583 key = MIN (GMT->current.map.this_y_status, GMT->current.map.prev_y_status);
1584 lat_p = (key < 0) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
1585 gmt_geo_to_xy (GMT, lon_p, lat_p, &x_edge[j], &y_edge[j]);
1586 j++;
1587 }
1588 x_edge[j] = xtmp; y_edge[j] = ytmp;
1589 n = 1;
1590 }
1591 if (GMT->current.map.this_x_status != 0) lon = (GMT->current.map.this_x_status < 0) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
1592 if (GMT->current.map.this_y_status != 0) lat = (GMT->current.map.this_y_status < 0) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
1593 gmt_geo_to_xy (GMT, lon, lat, &x_edge[j], &y_edge[j]);
1594 return (n + 1);
1595 }
1596
1597 /*! . */
gmtmap_wesn_clip_old(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n,double ** x,double ** y,uint64_t * total_nx)1598 GMT_LOCAL uint64_t gmtmap_wesn_clip_old (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n, double **x, double **y, uint64_t *total_nx) {
1599 uint64_t i, j = 0, nx, k;
1600 unsigned int sides[4];
1601 double xlon[4], xlat[4], xc[4], yc[4];
1602
1603 *total_nx = 0; /* Keep track of total of crossings */
1604
1605 if (n == 0) return (0);
1606
1607 gmt_prep_tmp_arrays (GMT, GMT_NOTSET, 1, 2); /* Init or reallocate tmp vectors */
1608
1609 (void) gmt_map_outside (GMT, lon[0], lat[0]);
1610 j = gmtmap_move_to_wesn (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], lon[0], lat[0], 0.0, 0.0, 0, 0); /* Add one point */
1611
1612 for (i = 1; i < n; i++) {
1613 (void) gmt_map_outside (GMT, lon[i], lat[i]);
1614 nx = gmtmap_crossing (GMT, lon[i-1], lat[i-1], lon[i], lat[i], xlon, xlat, xc, yc, sides);
1615 for (k = 0; k < nx; k++) {
1616 gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j, 2); /* Init or reallocate tmp vectors */
1617 GMT->hidden.mem_coord[GMT_X][j] = xc[k];
1618 GMT->hidden.mem_coord[GMT_Y][j++] = yc[k];
1619 (*total_nx) ++;
1620 }
1621 gmt_prep_tmp_arrays (GMT, GMT_NOTSET, j+2, 2); /* Init or reallocate tmp vectors */
1622 j += gmtmap_move_to_wesn (GMT, GMT->hidden.mem_coord[GMT_X], GMT->hidden.mem_coord[GMT_Y], lon[i], lat[i], lon[i-1], lat[i-1], j, nx); /* May add 2 points, which explains the j+2 stuff */
1623 }
1624
1625 *x = gmtlib_assign_vector (GMT, j, GMT_X);
1626 *y = gmtlib_assign_vector (GMT, j, GMT_Y);
1627
1628 #ifdef CRAP
1629 {
1630 FILE *fp = NULL;
1631 double out[2];
1632 fp = fopen ("crap.d", "a");
1633 fprintf (fp, "> N = %d\n", (int)j);
1634 for (i = 0; i < j; i++) {
1635 out[GMT_X] = *x[i];
1636 out[GMT_Y] = *y[i];
1637 GMT->current.io.output (GMT, fp, 2, out);
1638 }
1639 fclose (fp);
1640 }
1641 #endif
1642 return (j);
1643 }
1644
1645 /*! . */
map_wesn_clip(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n_orig,double ** x,double ** y,uint64_t * total_nx)1646 uint64_t map_wesn_clip (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n_orig, double **x, double **y, uint64_t *total_nx) {
1647 char *x_type = NULL, *name = "SENW";
1648 size_t n_alloc = 0, n_x_alloc = 0, n_t_alloc = 0;
1649 uint64_t new_n, i, n_get, n, m, n_cross = 0, *x_index = NULL;
1650 unsigned int j, np, side, in = 1, out = 0;
1651 int cross = 0;
1652 bool curved, jump = false, polygon, periodic = false;
1653 double *xtmp[2] = {NULL, NULL}, *ytmp[2] = {NULL, NULL}, xx[2], yy[2], border[4];
1654 double x1, x2, y1, y2;
1655 unsigned int (*clipper[4]) (double, double, double, double, double *, double *, double, bool (*inside) (double, double), bool (*outside) (double, double), int *);
1656 bool (*inside[4]) (double, double);
1657 bool (*outside[4]) (double, double);
1658
1659 #ifdef DEBUG
1660 FILE *fp = NULL;
1661 bool dump = false;
1662 #endif
1663
1664 if ((n = n_orig) == 0) return (0);
1665
1666 /* If there are jumps etc call the old clipper, else we try the new clipper */
1667
1668 gmt_geo_to_xy (GMT, lon[0], lat[0], &x1, &y1);
1669 for (i = 1; !jump && i < n; i++) {
1670 gmt_geo_to_xy (GMT, lon[i], lat[i], &x2, &y2);
1671 jump = gmtmap_jump_x (GMT, x2, y2, x1, y1);
1672 x1 = x2; y1 = y2;
1673 }
1674
1675 if (jump) return (gmtmap_wesn_clip_old (GMT, lon, lat, n, x, y, total_nx)); /* Must do the old way for now */
1676 periodic = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]); /* No point clipping against W and E if periodic map */
1677
1678 /* Here we can try the Sutherland/Hodgman algorithm */
1679
1680 polygon = (n > 2 && !gmt_polygon_is_open (GMT, lon, lat, n)); /* true if input segment is a closed polygon */
1681
1682 *total_nx = 1; /* So that calling program will not discard the clipped polygon */
1683
1684 /* Set up function pointers. This could be done once in gmt_begin at some point */
1685
1686 clipper[GMT_BOTTOM] = gmtmap_clip_sn; clipper[GMT_RIGHT] = gmtmap_clip_we; clipper[GMT_TOP] = gmtmap_clip_sn; clipper[GMT_LEFT] = gmtmap_clip_we;
1687 inside[GMT_RIGHT] = inside[GMT_TOP] = gmtmap_inside_upper_boundary; outside[GMT_RIGHT] = outside[GMT_TOP] = gmtmap_outside_upper_boundary;
1688 inside[GMT_BOTTOM] = inside[GMT_LEFT] = gmtmap_inside_lower_boundary; outside[GMT_BOTTOM] = outside[GMT_LEFT] = gmtmap_outside_lower_boundary;
1689 border[GMT_BOTTOM] = GMT->common.R.wesn[YLO]; border[GMT_LEFT] = GMT->common.R.wesn[XLO]; border[GMT_RIGHT] = GMT->common.R.wesn[XHI]; border[GMT_TOP] = GMT->common.R.wesn[YHI];
1690
1691 /* This is new approach to get rid of those crossing lines for filled polygons,
1692 * i.e., issue # 949. Also see comments further down.
1693 * P. Wessel, Dec 1 2016 */
1694 if (GMT->current.map.coastline && periodic) { /* Make data longitudes have no jumps [This is for pscoast] */
1695 for (i = 0; i < n; i++) {
1696 if (lon[i] < border[GMT_LEFT] && (lon[i] + 360.0) <= border[GMT_RIGHT])
1697 lon[i] += 360.0;
1698 else if (lon[i] > border[GMT_RIGHT] && (lon[i] - 360.0) >= border[GMT_LEFT])
1699 lon[i] -= 360.0;
1700 }
1701 }
1702 else { /* Don't let points be > 180 from center longitude */
1703 double mid_lon = 0.5 * (border[GMT_LEFT] + border[GMT_RIGHT]), diff;
1704 for (i = 0; i < n; i++) {
1705 diff = lon[i] - mid_lon;
1706 if (diff > 180.0) lon[i] -= 360.0;
1707 else if (diff < -180.0) lon[i] += 360.0;
1708 }
1709 }
1710 if (!GMT->current.map.coastline) { /* Not do if pscoast since it has its own oddness */
1711 double xmin, xmax;
1712 gmtlib_get_lon_minmax (GMT, lon, n, &xmin, &xmax);
1713 xmin -= 360.0; xmax -= 360.0;
1714 while (xmax < border[GMT_LEFT]) {xmin += 360.0; xmax += 360.0;}
1715 if (xmin > border[GMT_RIGHT]) return 0; /* Outside */
1716 }
1717
1718 n_get = lrint (1.05*n+5); /* Anticipate just a few crossings (5%)+5, allocate more later if needed */
1719 /* Create a pair of arrays for holding input and output */
1720 gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], n_get, &n_alloc, double);
1721
1722 /* Make copy of lon/lat coordinates */
1723
1724 gmt_M_memcpy (xtmp[0], lon, n, double); gmt_M_memcpy (ytmp[0], lat, n, double);
1725 m = n;
1726
1727 /* Preallocate space for crossing information */
1728
1729 x_index = gmt_M_malloc (GMT, NULL, GMT_TINY_CHUNK, &n_x_alloc, uint64_t);
1730 x_type = gmt_M_malloc (GMT, NULL, GMT_TINY_CHUNK, &n_t_alloc, char);
1731
1732 #ifdef DEBUG
1733 if (dump) {
1734 fp = fopen ("input.d", "w");
1735 for (i = 0; i < n; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1736 fclose (fp);
1737 }
1738 #endif
1739 for (side = 0; side < 4; side++) { /* Must clip polygon against a single border, one border at a time */
1740 n = m; /* Current size of polygon */
1741 m = 0; /* Start with nuthin' */
1742 n_cross = 0; /* No crossings so far */
1743
1744 curved = !((side%2) ? GMT->current.map.meridian_straight : GMT->current.map.parallel_straight); /* Is this border straight or curved when projected */
1745 gmt_M_uint_swap (in, out); /* Swap what is input and output for clipping against this border */
1746 if (side%2 && periodic) { /* No clipping can take place on w or e border; just copy all and go to next side */
1747 m = n;
1748 if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1749 gmt_M_memcpy (xtmp[out], xtmp[in], m, double);
1750 gmt_M_memcpy (ytmp[out], ytmp[in], m, double);
1751 continue;
1752 }
1753 #if 0
1754 /* This caused lots of issues with various map types so replaced by the check about
1755 * that uses the mid-longitude as center and does a +/- 180 test from that.
1756 * P. Wessel, Dec 1 2016 */
1757 if (!GMT->current.map.coastline && side % 2) { /* Either left or right border */
1758 /* For non-periodic maps we have to be careful to position the polygon so it does
1759 * not have longitude jumps at the current border. This does not apply to pscoast
1760 * which has special handling and hence bypasses this test.
1761 * Note that we may be using the map longitude range if this exceeds 180
1762 * as it is tricky to make those plots with large but < 360 longitudes. */
1763 //double map_lon_range = MAX (180.0, border[GMT_RIGHT] - border[GMT_LEFT]);
1764 double map_lon_range = 1800.0, diff;
1765 for (i = 0; i < n; i++) { /* If points is > map_lon_range degrees from border, flip side */
1766 diff = xtmp[in][i] - border[side];
1767 if (diff > map_lon_range) xtmp[in][i] -= 360.0;
1768 else if (diff < -map_lon_range) xtmp[in][i] += 360.0;
1769 }
1770 }
1771 #endif
1772 if (n) {
1773 /* Must ensure we copy the very first point if it is inside the clip rectangle */
1774 if (inside[side] ((side%2) ? xtmp[in][0] : ytmp[in][0], border[side])) {xtmp[out][0] = xtmp[in][0]; ytmp[out][0] = ytmp[in][0]; m = 1;} /* First point is inside; add it */
1775 }
1776 for (i = 1; i < n; i++) { /* For each line segment */
1777 np = clipper[side] (xtmp[in][i-1], ytmp[in][i-1], xtmp[in][i], ytmp[in][i], xx, yy, border[side], inside[side], outside[side], &cross); /* Returns 0, 1, or 2 points */
1778 if (polygon && cross && curved) { /* When crossing in/out of a curved boundary we must eventually sample along the curve between crossings */
1779 x_index[n_cross] = m; /* Index of intersection point (which will be copied from xx[0], yy[0] below) */
1780 x_type[n_cross] = (char)cross; /* -1 going out, +1 going in */
1781 if (++n_cross == n_x_alloc) {
1782 x_index = gmt_M_malloc (GMT, x_index, n_cross, &n_x_alloc, uint64_t);
1783 x_type = gmt_M_malloc (GMT, x_type, n_cross, &n_t_alloc, char);
1784 }
1785 }
1786 for (j = 0; j < np; j++) { /* Add the np returned points to the new clipped polygon path */
1787 if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1788 xtmp[out][m] = xx[j]; ytmp[out][m] = yy[j]; m++;
1789 }
1790 }
1791 if (polygon && gmt_polygon_is_open (GMT, xtmp[out], ytmp[out], m)) { /* Do we need to explicitly close this clipped polygon? */
1792 if (m == n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], m, &n_alloc, double);
1793 xtmp[out][m] = xtmp[out][0]; ytmp[out][m] = ytmp[out][0]; m++; /* Yes. */
1794 }
1795 if (polygon && curved && n_cross) { /* Must resample between crossing points */
1796 double *x_add = NULL, *y_add = NULL, *x_cpy = NULL, *y_cpy = NULL;
1797 size_t np = 0;
1798 uint64_t add, last_index = 0, p, p_next;
1799
1800 if (n_cross%2 == 1) { /* Should not happen with a polygon */
1801 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Failure in map_wesn_clip, side %c: odd number of crossings?\n", name[side]);
1802 }
1803
1804 /* First copy the current polygon */
1805
1806 gmt_M_malloc2 (GMT, x_cpy, y_cpy, m, &np, double);
1807 gmt_M_memcpy (x_cpy, xtmp[out], m, double);
1808 gmt_M_memcpy (y_cpy, ytmp[out], m, double);
1809
1810 for (p = np = 0; p < n_cross; p++) { /* Process each crossing point */
1811 if (last_index < x_index[p]) { /* Copy over segment from were we left off to this crossing point */
1812 add = x_index[p] - last_index;
1813 if ((new_n = (np+add)) >= n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], new_n, &n_alloc, double);
1814 gmt_M_memcpy (&xtmp[out][np], &x_cpy[last_index], add, double);
1815 gmt_M_memcpy (&ytmp[out][np], &y_cpy[last_index], add, double);
1816 np += add;
1817 last_index = x_index[p];
1818 }
1819 if (x_type[p] == -1) { /* Must add path from this exit to the next entry */
1820 double start_lon, stop_lon;
1821 p_next = (p == (n_cross-1)) ? 0 : p + 1; /* index of the next crossing */
1822 start_lon = x_cpy[x_index[p]]; stop_lon = x_cpy[x_index[p_next]];
1823 if (side%2 == 0 && periodic) { /* Make sure we select the shortest longitude arc */
1824 if ((x_cpy[x_index[p_next]] - x_cpy[x_index[p]]) < -180.0)
1825 stop_lon += 360.0;
1826 else if ((x_cpy[x_index[p_next]] - x_cpy[x_index[p]]) > +180.0)
1827 stop_lon -= 360.0;
1828 }
1829 add = gmtlib_map_path (GMT, start_lon, y_cpy[x_index[p]], stop_lon, y_cpy[x_index[p_next]], &x_add, &y_add);
1830 if ((new_n = (np+add)) >= n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], new_n, &n_alloc, double);
1831 gmt_M_memcpy (&xtmp[out][np], x_add, add, double);
1832 gmt_M_memcpy (&ytmp[out][np], y_add, add, double);
1833 if (add) { gmt_M_free (GMT, x_add); gmt_M_free (GMT, y_add); }
1834 np += add;
1835 last_index = x_index[p_next];
1836 }
1837 }
1838 if (x_index[0] > 0) { /* First point was clean inside, must add last connection */
1839 add = m - last_index;
1840 if ((new_n = (np+add)) >= n_alloc) gmt_M_malloc4 (GMT, xtmp[0], ytmp[0], xtmp[1], ytmp[1], new_n, &n_alloc, double);
1841 gmt_M_memcpy (&xtmp[out][np], &x_cpy[last_index], add, double);
1842 gmt_M_memcpy (&ytmp[out][np], &y_cpy[last_index], add, double);
1843 np += add;
1844 }
1845 m = np; /* New total of points */
1846 gmt_M_free (GMT, x_cpy); gmt_M_free (GMT, y_cpy);
1847 }
1848 }
1849
1850 gmt_M_free (GMT, xtmp[1]); /* Free the pairs of arrays that holds the last input array */
1851 gmt_M_free (GMT, ytmp[1]);
1852 gmt_M_free (GMT, x_index); /* Free the pairs of arrays that holds the crossing info */
1853 gmt_M_free (GMT, x_type);
1854
1855 if (m) { /* Reallocate and return the array with the final clipped polygon */
1856 n_alloc = m;
1857 gmt_M_malloc2 (GMT, xtmp[0], ytmp[0], 0U, &n_alloc, double);
1858 /* Convert to map coordinates */
1859 for (i = 0; i < m; i++) gmt_geo_to_xy (GMT, xtmp[0][i], ytmp[0][i], &xtmp[0][i], &ytmp[0][i]);
1860
1861 *x = xtmp[0];
1862 *y = ytmp[0];
1863 #ifdef DEBUG
1864 if (dump) {
1865 fp = fopen ("output.d", "w");
1866 for (i = 0; i < m; i++) fprintf (fp, "%g\t%g\n", xtmp[0][i], ytmp[0][i]);
1867 fclose (fp);
1868 }
1869 #endif
1870 }
1871 else { /* Nothing survived the clipping - free the output arrays */
1872 gmt_M_free (GMT, xtmp[0]);
1873 gmt_M_free (GMT, ytmp[0]);
1874 }
1875
1876 return (m);
1877 }
1878
1879 /*! . */
gmtmap_radial_boundary_arc(struct GMT_CTRL * GMT,int this_way,double end_x[],double end_y[],double ** xarc,double ** yarc)1880 GMT_LOCAL uint64_t gmtmap_radial_boundary_arc (struct GMT_CTRL *GMT, int this_way, double end_x[], double end_y[], double **xarc, double **yarc) {
1881 uint64_t n_arc, k, pt;
1882 double az1, az2, d_az, da, xr, yr, da_try, *xx = NULL, *yy = NULL;
1883
1884 /* When a polygon crosses out then in again into the circle we need to add a boundary arc
1885 * to the polygon where it is clipped. We simply sample the circle as finely as the arc
1886 * length and the current line_step demands */
1887
1888 da_try = (GMT->current.setting.map_line_step * 360.0) / (TWO_PI * GMT->current.proj.r); /* Angular step in degrees */
1889 az1 = d_atan2d (end_y[0], end_x[0]); /* azimuth from map center to 1st crossing */
1890 az2 = d_atan2d (end_y[1], end_x[1]); /* azimuth from map center to 2nd crossing */
1891 gmt_M_set_delta_lon (az1, az2, d_az); /* Insist we take the short arc for now */
1892 n_arc = lrint (ceil (fabs (d_az) / da_try)); /* Get number of integer increments of da_try degree... */
1893 if (n_arc < 2) n_arc = 2; /* ...but minimum 2 */
1894 da = d_az / (n_arc - 1); /* Reset da to get exact steps */
1895 if (n_arc <= 2) return (0); /* Arc is too short to have intermediate points */
1896 n_arc -= 2; /* We do not include the end points since these are the crossing points handled in the calling function */
1897 gmt_M_malloc2 (GMT, xx, yy, n_arc, NULL, double);
1898 for (k = 1; k <= n_arc; k++) { /* Create points along arc from first to second crossing point (k-loop excludes the end points) */
1899 sincosd (az1 + k * da, &yr, &xr);
1900 pt = (this_way) ? n_arc - k : k - 1; /* The order we add the arc depends if we exited or entered the inside area */
1901 xx[pt] = GMT->current.proj.r * (1.0 + xr);
1902 yy[pt] = GMT->current.proj.r * (1.0 + yr);
1903 }
1904
1905 *xarc = xx;
1906 *yarc = yy;
1907 return (n_arc);
1908 }
1909
1910 #ifdef DEBUG
1911 /* If we need to dump out clipped polygon then set clip_dump = 1 during execution */
1912 static int clip_dump = 0, clip_id = 0;
gmtmap_dumppol(uint64_t n,double * x,double * y,int * id)1913 GMT_LOCAL void gmtmap_dumppol (uint64_t n, double *x, double *y, int *id) {
1914 uint64_t i;
1915 FILE *fp = NULL;
1916 char line[GMT_LEN64];
1917 snprintf (line, GMT_LEN64, "dump_%d.d", *id);
1918 fp = fopen (line, "w");
1919 for (i = 0; i < n; i++) fprintf (fp, "%g\t%g\n", x[i], y[i]);
1920 fclose (fp);
1921 (*id)++;
1922 }
1923 #endif
1924
1925 /*! . */
gmtmap_radial_clip(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t np,double ** x,double ** y,uint64_t * total_nx)1926 GMT_LOCAL uint64_t gmtmap_radial_clip (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t np, double **x, double **y, uint64_t *total_nx) {
1927 size_t n_alloc = 0;
1928 uint64_t n = 0, n_arc;
1929 unsigned int i, nx;
1930 unsigned int sides[4];
1931 bool this_side = false, add_boundary = false;
1932 double xlon[4], xlat[4], xc[4], yc[4], end_x[3], end_y[3], xr, yr;
1933 double *xx = NULL, *yy = NULL, *xarc = NULL, *yarc = NULL;
1934
1935 *total_nx = 0; /* Keep track of total of crossings */
1936
1937 if (np == 0) return (0);
1938
1939 if (!gmt_map_outside (GMT, lon[0], lat[0])) {
1940 gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1941 gmt_geo_to_xy (GMT, lon[0], lat[0], &xx[0], &yy[0]);
1942 n++;
1943 }
1944 nx = 0;
1945 for (i = 1; i < np; i++) {
1946 this_side = gmt_map_outside (GMT, lon[i], lat[i]);
1947 if (gmtmap_crossing (GMT, lon[i-1], lat[i-1], lon[i], lat[i], xlon, xlat, xc, yc, sides)) {
1948 if (this_side) { /* Crossing boundary and leaving circle: Add exit point to the path */
1949 if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1950 xx[n] = xc[0]; yy[n] = yc[0]; n++;
1951 }
1952 end_x[nx] = xc[0] - GMT->current.proj.r; end_y[nx] = yc[0] - GMT->current.proj.r;
1953 nx++;
1954 (*total_nx) ++;
1955 if (nx >= 2) { /* Got a pair of entry+exit points */
1956 add_boundary = !this_side; /* We only add boundary arcs if we first exited and now entered the circle again */
1957 }
1958 if (add_boundary) { /* Crossed twice. Now add arc between the two crossing points */
1959 /* PW: Currently, we make the assumption that the shortest arc is the one we want. However,
1960 * extremely large polygons could cut the boundary so that it is the longest arc we want.
1961 * The way to improve this algorithm in the future is to find the two opposite points on
1962 * the circle boundary that lies on the bisector of az1,az2, and see which point lies
1963 * inside the polygon. This would require that gmt_inonout_sphpol be called.
1964 */
1965 if ((n_arc = gmtmap_radial_boundary_arc (GMT, this_side, &end_x[nx-2], &end_y[nx-2], &xarc, &yarc)) > 0) {
1966 if ((n + n_arc) >= n_alloc) gmt_M_malloc2 (GMT, xx, yy, n + n_arc, &n_alloc, double);
1967 gmt_M_memcpy (&xx[n], xarc, n_arc, double); /* Copy longitudes of arc */
1968 gmt_M_memcpy (&yy[n], yarc, n_arc, double); /* Copy latitudes of arc */
1969 n += n_arc; /* Number of arc points added (end points are done separately) */
1970 gmt_M_free (GMT, xarc); gmt_M_free (GMT, yarc);
1971 }
1972 add_boundary = false;
1973 nx -= 2; /* Done with those two crossings */
1974 }
1975 if (!this_side) { /* Crossing boundary and entering circle: Add entry point to the path */
1976 if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1977 xx[n] = xc[0]; yy[n] = yc[0]; n++;
1978 }
1979 }
1980 gmt_geo_to_xy (GMT, lon[i], lat[i], &xr, &yr);
1981 if (!this_side) { /* Only add points actually inside the map to the path */
1982 if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1983 xx[n] = xr; yy[n] = yr; n++;
1984 }
1985 }
1986
1987 if (nx == 2) { /* Must close polygon by adding boundary arc */
1988 if ((n_arc = gmtmap_radial_boundary_arc (GMT, this_side, end_x, end_y, &xarc, &yarc)) > 0) {
1989 if ((n + n_arc) >= n_alloc) gmt_M_malloc2 (GMT, xx, yy, n + n_arc, &n_alloc, double);
1990 gmt_M_memcpy (&xx[n], xarc, n_arc, double); /* Copy longitudes of arc */
1991 gmt_M_memcpy (&yy[n], yarc, n_arc, double); /* Copy latitudes of arc */
1992 n += n_arc; /* Number of arc points added (end points are done separately) */
1993 gmt_M_free (GMT, xarc); gmt_M_free (GMT, yarc);
1994 }
1995 if (n == n_alloc) gmt_M_malloc2 (GMT, xx, yy, n, &n_alloc, double);
1996 xx[n] = xx[0]; yy[n] = yy[0]; n++; /* Close the polygon */
1997 }
1998 n_alloc = n;
1999 gmt_M_malloc2 (GMT, xx, yy, 0U, &n_alloc, double);
2000 *x = xx;
2001 *y = yy;
2002 #ifdef DEBUG
2003 if (clip_dump) gmtmap_dumppol (n, xx, yy, &clip_id);
2004 #endif
2005
2006 return (n);
2007 }
2008
2009 /*! . */
gmtmap_cartesian_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2010 GMT_LOCAL bool gmtmap_cartesian_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2011 /* Return true if the projection of either (lon0,lat0) and (lon1,lat1) is inside (not on) the rectangular map boundary */
2012 /* Here, lon,lat etc are Cartesian and not geographic coordinates, otherwise gmtmap_rect_overlap is used */
2013 double x0, y0, x1, y1;
2014
2015 gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
2016 gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
2017
2018 if (x0 > x1) gmt_M_double_swap (x0, x1);
2019 if (y0 > y1) gmt_M_double_swap (y0, y1);
2020
2021 if (x1 - GMT->current.proj.rect[XLO] < -GMT_CONV8_LIMIT || x0 - GMT->current.proj.rect[XHI] > GMT_CONV8_LIMIT) return (false);
2022 if (y1 - GMT->current.proj.rect[YLO] < -GMT_CONV8_LIMIT || y0 - GMT->current.proj.rect[YHI] > GMT_CONV8_LIMIT) return (false);
2023 return (true);
2024 }
2025
2026 /*! . */
gmtmap_rect_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2027 GMT_LOCAL bool gmtmap_rect_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2028 /* Return true if the projection of either (lon0,lat0) and (lon1,lat1) is inside (not on) the rectangular map boundary */
2029 double x0, y0, x1, y1;
2030
2031 gmt_geo_to_xy (GMT, lon0, lat0, &x0, &y0);
2032 gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
2033
2034 if (x0 > x1) gmt_M_double_swap (x0, x1);
2035 if (y0 > y1) gmt_M_double_swap (y0, y1);
2036
2037 if (x1 - GMT->current.proj.rect[XLO] < -GMT_CONV8_LIMIT || x0 - GMT->current.proj.rect[XHI] > GMT_CONV8_LIMIT) return (false);
2038 if (y1 - GMT->current.proj.rect[YLO] < -GMT_CONV8_LIMIT || y0 - GMT->current.proj.rect[YHI] > GMT_CONV8_LIMIT) return (false);
2039 if (x0 < GMT->current.proj.rect[XLO] && x1 > GMT->current.proj.rect[XHI]) { /* Possibly a map jump but is it reasonable? */
2040 /* What can happen for a small non-360 map is that points that approach being +180 degrees in longitude away and the
2041 * next point that is +181 will be seen as -179 degrees away and suddenly the x-coordinate jumps from very positive
2042 * to very negative (or the other way). Before this attempt, the function would return true and give crossing lines
2043 * across the map. Here we check if the change in x is a large (> 10x) multiple of the map width. This is likely
2044 * not to be fool-proof. Works with current test scripts so we will give it a go. Paul Wessel, 7/31/2014 */
2045 if ((x1 - x0)/(GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]) > 10.0) return (false);
2046 }
2047 return (true);
2048 }
2049
2050 /*! . */
gmtmap_wesn_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2051 GMT_LOCAL bool gmtmap_wesn_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2052 /* Return true if either of the points (lon0,lat0) and (lon1,lat1) is inside (not on) the rectangular lon/lat boundaries */
2053 if (lon0 > lon1) gmt_M_double_swap (lon0, lon1);
2054 if (lat0 > lat1) gmt_M_double_swap (lat0, lat1);
2055 if (lon1 - GMT->common.R.wesn[XLO] < -GMT_CONV8_LIMIT) {
2056 lon0 += 360.0;
2057 lon1 += 360.0;
2058 }
2059 else if (lon0 - GMT->common.R.wesn[XHI] > GMT_CONV8_LIMIT) {
2060 lon0 -= 360.0;
2061 lon1 -= 360.0;
2062 }
2063
2064 if (lon1 - GMT->common.R.wesn[XLO] < -GMT_CONV8_LIMIT || lon0 - GMT->common.R.wesn[XHI] > GMT_CONV8_LIMIT) return (false);
2065 if (lat1 - GMT->common.R.wesn[YLO] < -GMT_CONV8_LIMIT || lat0 - GMT->common.R.wesn[YHI] > GMT_CONV8_LIMIT) return (false);
2066 return (true);
2067 }
2068
2069 /*! . */
gmtmap_radial_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2070 GMT_LOCAL bool gmtmap_radial_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2071 /* Dummy routine */
2072 gmt_M_unused(GMT); gmt_M_unused(lon0); gmt_M_unused(lat0); gmt_M_unused(lon1); gmt_M_unused(lat1);
2073 return (true);
2074 }
2075
2076 /*! . */
gmtmap_genperg_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2077 GMT_LOCAL bool gmtmap_genperg_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2078 /* Dummy routine */
2079 gmt_M_unused(lon0); gmt_M_unused(lat0); gmt_M_unused(lon1); gmt_M_unused(lat1);
2080 if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper_overlap: overlap called\n");
2081 return (true);
2082 }
2083
2084 /*! . */
gmtmap_genperw_overlap(struct GMT_CTRL * GMT,double lon0,double lat0,double lon1,double lat1)2085 GMT_LOCAL bool gmtmap_genperw_overlap (struct GMT_CTRL *GMT, double lon0, double lat0, double lon1, double lat1) {
2086 bool out0, out1;
2087 gmt_M_unused(lon0); gmt_M_unused(lat0); gmt_M_unused(lon1); gmt_M_unused(lat1);
2088 /* Return true if either of the points (lon0,lat0) and (lon1,lat1) is inside (not on) the windowed genper boundary */
2089 /* Check if point 1 is beyond horizon: */
2090 out0 = gmtmap_radial_outside (GMT, lon0, lat0); /* true if point 0 is beyond the horizon */
2091 if (!out0) out0 = gmtmap_rect_outside (GMT, lon0, lat0); /* true if point 0 is beyond the map box */
2092 out1 = gmtmap_radial_outside (GMT, lon1, lat1); /* true if point 1 is beyond the horizon */
2093 if (!out1) out1 = gmtmap_rect_outside (GMT, lon1, lat1); /* true if point 1 is beyond the map box */
2094 return (out0 != out1);
2095 }
2096
2097 #define GMTMAP_N_STEPS 500 /* To avoid repeating 500 in many places */
2098
2099 /*! . */
gmtmap_xy_search(struct GMT_CTRL * GMT,double * x0,double * x1,double * y0,double * y1,double w0,double e0,double s0,double n0)2100 GMT_LOCAL void gmtmap_xy_search (struct GMT_CTRL *GMT, double *x0, double *x1, double *y0, double *y1, double w0, double e0, double s0, double n0) {
2101 unsigned int i, j;
2102 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, w, s, x = 0.0, y = 0.0, dlon, dlat;
2103
2104 /* Find min/max forward values */
2105
2106 xmax = ymax = -DBL_MAX;
2107 xmin = ymin = DBL_MAX;
2108 dlon = fabs (e0 - w0) / GMTMAP_N_STEPS;
2109 dlat = fabs (n0 - s0) / GMTMAP_N_STEPS;
2110
2111 for (i = 0; i <= GMTMAP_N_STEPS; i++) {
2112 w = w0 + i * dlon;
2113 (*GMT->current.proj.fwd) (GMT, w, s0, &x, &y);
2114 if (x < xmin) xmin = x;
2115 if (y < ymin) ymin = y;
2116 if (x > xmax) xmax = x;
2117 if (y > ymax) ymax = y;
2118 (*GMT->current.proj.fwd) (GMT, w, n0, &x, &y);
2119 if (x < xmin) xmin = x;
2120 if (y < ymin) ymin = y;
2121 if (x > xmax) xmax = x;
2122 if (y > ymax) ymax = y;
2123 }
2124 for (j = 0; j <= GMTMAP_N_STEPS; j++) {
2125 s = s0 + j * dlat;
2126 (*GMT->current.proj.fwd) (GMT, w0, s, &x, &y);
2127 if (x < xmin) xmin = x;
2128 if (y < ymin) ymin = y;
2129 if (x > xmax) xmax = x;
2130 if (y > ymax) ymax = y;
2131 (*GMT->current.proj.fwd) (GMT, e0, s, &x, &y);
2132 if (x < xmin) xmin = x;
2133 if (y < ymin) ymin = y;
2134 if (x > xmax) xmax = x;
2135 if (y > ymax) ymax = y;
2136 }
2137
2138 *x0 = xmin; *x1 = xmax; *y0 = ymin; *y1 = ymax;
2139 }
2140
2141 /*! . */
gmtmap_adjust_panel_for_gaps(struct GMT_CTRL * GMT,struct GMT_SUBPLOT * P)2142 GMT_LOCAL void gmtmap_adjust_panel_for_gaps (struct GMT_CTRL *GMT, struct GMT_SUBPLOT *P) {
2143 /* Checks the caps array and makes adjustment to w/h and adjusts the x/y origin */
2144 gmt_M_unused (GMT);
2145
2146 /* Shrink the available panel dimensions based on the gaps */
2147 P->w -= (P->gap[XLO] + P->gap[XHI]);
2148 P->h -= (P->gap[YLO] + P->gap[YHI]);
2149 }
2150
2151 /*! . */
gmtmap_setxy(struct GMT_CTRL * GMT,double xmin,double xmax,double ymin,double ymax)2152 GMT_LOCAL void gmtmap_setxy (struct GMT_CTRL *GMT, double xmin, double xmax, double ymin, double ymax) {
2153 /* Set x/y parameters */
2154 struct GMT_SUBPLOT *P = &(GMT->current.plot.panel); /* P->active == 1 if a subplot */
2155 struct GMT_INSET *I = &(GMT->current.plot.inset); /* I->active == 1 if an inset */
2156 unsigned int no_scaling = P->no_scaling;
2157 bool update_parameters = false;
2158 double fw, fh, fx, fy, w, h;
2159
2160 /* Set up the original min/max values, the rectangular map dimensionsm and the projection offset */
2161 GMT->current.proj.rect_m[XLO] = xmin; GMT->current.proj.rect_m[XHI] = xmax; /* This is in original meters */
2162 GMT->current.proj.rect_m[YLO] = ymin; GMT->current.proj.rect_m[YHI] = ymax;
2163 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Projected values in meters: %g %g %g %g\n", xmin, xmax, ymin, ymax);
2164 GMT->current.proj.rect[XHI] = (xmax - xmin) * GMT->current.proj.scale[GMT_X];
2165 GMT->current.proj.rect[YHI] = (ymax - ymin) * GMT->current.proj.scale[GMT_Y];
2166 GMT->current.proj.origin[GMT_X] = -xmin * GMT->current.proj.scale[GMT_X];
2167 GMT->current.proj.origin[GMT_Y] = -ymin * GMT->current.proj.scale[GMT_Y];
2168 if (GMT->current.proj.obl_flip) {
2169 GMT->current.proj.origin[GMT_Y] = ymax * GMT->current.proj.scale[GMT_Y];
2170 GMT->current.proj.scale[GMT_Y] = -GMT->current.proj.scale[GMT_Y];
2171 }
2172
2173 if (!strncmp (GMT->init.module_name, "inset", 5U))
2174 no_scaling = 1; /* Don't scale yet if we are calling inset begin (inset end would come here too but not affected since no mapping done by that module) */
2175
2176 w = GMT->current.proj.rect[XHI]; h = GMT->current.proj.rect[YHI];
2177
2178 /* Check inset first since an inset may be inside a subplot but there are no subplots inside an inset */
2179 if (I->active && no_scaling == 0) { /* Must rescale to fit inside the inset dimensions and set dx,dy for centering */
2180 fw = w / I->w; fh = h / I->h;
2181 if (gmt_M_is_geographic (GMT, GMT_IN) || GMT->current.proj.projection == GMT_POLAR || GMT->current.proj.gave_map_width == 0) { /* Giving -Jx will end up here with map projections */
2182 if (fw > fh) { /* Wider than taller given inset dims; adjust width to fit exactly and set dy for centering */
2183 fx = fy = 1.0 / fw; I->dx = 0.0; I->dy = 0.5 * (I->h - h * fy);
2184 }
2185 else { /* Taller than wider given inset dims; adjust height to fit exactly and set dx for centering */
2186 fx = fy = 1.0 / fh; I->dy = 0.0; I->dx = 0.5 * (I->w - w * fx);
2187 }
2188 }
2189 else { /* Cartesian is scaled independently to fit the inset */
2190 fx = 1.0 / fw; fy = 1.0 / fh; I->dx = I->dy = 0.0;
2191 }
2192 update_parameters = true;
2193 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Rescaling map for inset by factors fx = %g fy = %g dx = %g dy = %g\n", fx, fy, I->dx, I->dy);
2194 }
2195 else if (P->active && no_scaling == 0) { /* Must rescale to fit inside subplot dimensions and set dx,dy for centering */
2196 gmtmap_adjust_panel_for_gaps (GMT, P); /* Deal with any gaps requested via subplot -C: shrink w/h and adjust origin */
2197 fw = w / P->w; fh = h / P->h;
2198 if (gmt_M_is_geographic (GMT, GMT_IN) || GMT->current.proj.projection == GMT_POLAR || GMT->current.proj.gave_map_width == 0) { /* Giving -Jx will end up here with map projections */
2199 if (fw > fh) { /* Wider than taller given panel dims; adjust width to fit exactly */
2200 fx = fy = 1.0 / fw; P->dx = 0.0; P->dy = 0.5 * (P->h - h * fy);
2201 }
2202 else { /* Taller than wider given panel dims; adjust height to fit exactly and set dx for centering */
2203 fx = fy = 1.0 / fh; P->dy = 0.0; P->dx = 0.5 * (P->w - w * fx);
2204 }
2205 }
2206 else { /* Cartesian is scaled independently to fit the subplot fully */
2207 fx = 1.0 / fw; fy = 1.0 / fh; P->dx = P->dy = 0.0;
2208 }
2209 update_parameters = true;
2210 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Rescaling map for subplot by factors fx = %g fy = %g dx = %g dy = %g\n", fx, fy, P->dx, P->dy);
2211 if (gmt_M_is_rect_graticule (GMT) && P->parallel) {
2212 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Force parallel latitude annotations\n");
2213 strcpy (GMT->current.setting.map_annot_ortho, ""); /* All annotations will be parallel to axes */
2214 GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_LAT_PARALLEL; /* Plot latitude parallel to frame for geo maps */
2215 }
2216 }
2217 if (update_parameters) { /* Scale the parameters due to inset or subplot adjustments */
2218 /* Update all projection parameters given the reduction factors fx, fy */
2219 GMT->current.proj.scale[GMT_X] *= fx;
2220 GMT->current.proj.scale[GMT_Y] *= fy;
2221 GMT->current.proj.w_r *= fx; /* Only matter for geographic where fx = fy anyway */
2222 GMT->current.proj.rect[XHI] = (xmax - xmin) * GMT->current.proj.scale[GMT_X];
2223 GMT->current.proj.rect[YHI] = (ymax - ymin) * GMT->current.proj.scale[GMT_Y];
2224 GMT->current.proj.origin[GMT_X] = -xmin * GMT->current.proj.scale[GMT_X];
2225 GMT->current.proj.origin[GMT_Y] = -ymin * GMT->current.proj.scale[GMT_Y];
2226 }
2227 }
2228
2229 /*! . */
gmtmap_setinfo(struct GMT_CTRL * GMT,double xmin,double xmax,double ymin,double ymax,double scl)2230 GMT_LOCAL void gmtmap_setinfo (struct GMT_CTRL *GMT, double xmin, double xmax, double ymin, double ymax, double scl) {
2231 /* Set [and rescale] parameters */
2232 double factor = 1.0, w, h;
2233
2234 if (GMT->current.map.is_world && doubleAlmostEqualZero (xmax, xmin)) { /* Safety valve for cases when w & e both project to the same side due to round-off */
2235 xmax = MAX (fabs (xmin), fabs (xmax));
2236 xmin =-xmax;
2237 }
2238 w = (xmax - xmin) * GMT->current.proj.scale[GMT_X];
2239 h = (ymax - ymin) * GMT->current.proj.scale[GMT_Y];
2240
2241 if (GMT->current.proj.gave_map_width == 1) /* Must rescale to given width */
2242 factor = scl / w;
2243 else if (GMT->current.proj.gave_map_width == 2) /* Must rescale to given height */
2244 factor = scl / h;
2245 else if (GMT->current.proj.gave_map_width == 3) /* Must rescale to max dimension */
2246 factor = scl / MAX (w, h);
2247 else if (GMT->current.proj.gave_map_width == 4) /* Must rescale to min dimension */
2248 factor = scl / MIN (w, h);
2249 GMT->current.proj.scale[GMT_X] *= factor;
2250 GMT->current.proj.scale[GMT_Y] *= factor;
2251 GMT->current.proj.w_r *= factor;
2252
2253 if (GMT->current.proj.g_debug > 1) {
2254 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "xmin %7.3f xmax %7.3f ymin %7.4f ymax %7.3f scale %6.3f\n", xmin/1000, xmax/1000, ymin/1000, ymax/1000, scl);
2255 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gave_map_width %d w %9.4e h %9.4e factor %9.4e\n", GMT->current.proj.gave_map_width, w, h, factor);
2256 }
2257
2258 gmtmap_setxy (GMT, xmin, xmax, ymin, ymax);
2259 }
2260
2261 /*! . */
gmtmap_mean_radius(struct GMT_CTRL * GMT,double a,double f)2262 GMT_LOCAL double gmtmap_mean_radius (struct GMT_CTRL *GMT, double a, double f) {
2263 double r = 0, b = a * (1 - f);
2264
2265 if (f == 0.0) return a; /* Not that hard */
2266
2267 switch (GMT->current.setting.proj_mean_radius) {
2268 case GMT_RADIUS_MEAN:
2269 r = a * (1.0 - f / 3.0);
2270 break;
2271 case GMT_RADIUS_AUTHALIC:
2272 r = sqrt (0.5 * a * a + 0.5 * b * b * atanh (GMT->current.proj.ECC) / GMT->current.proj.ECC);
2273 break;
2274 case GMT_RADIUS_VOLUMETRIC:
2275 r = pow (a*a*b, 1.0/3.0);
2276 break;
2277 case GMT_RADIUS_MERIDIONAL:
2278 r = pow (0.5 * (pow (a, 1.5) + pow (b, 1.5)), 2.0/3.0);
2279 break;
2280 case GMT_RADIUS_QUADRATIC:
2281 r = 0.5 * sqrt (3.0 * a * a + b * b);
2282 break;
2283 default: /* Cannot get here! Safety valve */
2284 GMT_Report (GMT->parent, GMT_MSG_WARNING, "GMT mean radius type not recognized - defaulting to mean radius\n");
2285 r = a * (1.0 - f / 3.0);
2286 break;
2287 }
2288
2289 return (r);
2290 }
2291
2292 /*! . */
gmtmap_set_spherical(struct GMT_CTRL * GMT,bool notify)2293 GMT_LOCAL void gmtmap_set_spherical (struct GMT_CTRL *GMT, bool notify) {
2294 /* Set up ellipsoid parameters using spherical approximation */
2295
2296 GMT->current.setting.ref_ellipsoid[GMT_N_ELLIPSOIDS - 1].eq_radius =
2297 gmtmap_mean_radius (GMT, GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius, GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening);
2298 GMT->current.setting.proj_ellipsoid = GMT_N_ELLIPSOIDS - 1; /* Custom ellipsoid */
2299 GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening = 0.0;
2300 if (notify) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Spherical approximation used\n");
2301 GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_NONE; /* No lat swapping for spherical */
2302
2303 gmtlib_init_ellipsoid (GMT);
2304 }
2305
2306 /*! . */
gmtmap_left_conic(struct GMT_CTRL * GMT,double y)2307 GMT_LOCAL double gmtmap_left_conic (struct GMT_CTRL *GMT, double y) {
2308 double x_ws, y_ws, x_wn, y_wn, dy;
2309
2310 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &x_ws, &y_ws);
2311 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &x_wn, &y_wn);
2312 dy = y_wn - y_ws;
2313 if (doubleAlmostEqualZero (y_wn, y_ws))
2314 return (0.0);
2315 return (x_ws + ((x_wn - x_ws) * (y - y_ws) / dy));
2316 }
2317
2318 /*! . */
gmtmap_right_conic(struct GMT_CTRL * GMT,double y)2319 GMT_LOCAL double gmtmap_right_conic (struct GMT_CTRL *GMT, double y) {
2320 double x_es, y_es, x_en, y_en, dy;
2321
2322 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], &x_es, &y_es);
2323 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &x_en, &y_en);
2324 dy = y_en - y_es;
2325 if (doubleAlmostEqualZero (y_en, y_es))
2326 return (GMT->current.map.width);
2327 return (x_es - ((x_es - x_en) * (y - y_es) / dy));
2328 }
2329
2330 /*! . */
gmtmap_left_rect(struct GMT_CTRL * GMT,double y)2331 GMT_LOCAL double gmtmap_left_rect (struct GMT_CTRL *GMT, double y) {
2332 gmt_M_unused(GMT); gmt_M_unused(y);
2333 return (0.0);
2334 }
2335
2336 /*! . */
gmtmap_right_rect(struct GMT_CTRL * GMT,double y)2337 GMT_LOCAL double gmtmap_right_rect (struct GMT_CTRL *GMT, double y) {
2338 gmt_M_unused(y);
2339 return (GMT->current.map.width);
2340 }
2341
2342 /*! . */
gmtmap_left_circle(struct GMT_CTRL * GMT,double y)2343 GMT_LOCAL double gmtmap_left_circle (struct GMT_CTRL *GMT, double y) {
2344 y -= GMT->current.proj.origin[GMT_Y];
2345 return (GMT->current.map.half_width - d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y));
2346 }
2347
2348 /*! . */
gmtmap_right_circle(struct GMT_CTRL * GMT,double y)2349 GMT_LOCAL double gmtmap_right_circle (struct GMT_CTRL *GMT, double y) {
2350 /* y -= GMT->current.proj.r; */
2351 y -= GMT->current.proj.origin[GMT_Y];
2352 return (GMT->current.map.half_width + d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y));
2353 }
2354
2355 /*! . */
gmtmap_left_ellipse(struct GMT_CTRL * GMT,double y)2356 GMT_LOCAL double gmtmap_left_ellipse (struct GMT_CTRL *GMT, double y) {
2357 /* Applies to Hammer and Mollweide only, where major axis = 2 * minor axis */
2358
2359 y = (y - GMT->current.proj.origin[GMT_Y]) / GMT->current.proj.w_r; /* Fraction, relative to Equator */
2360 return (GMT->current.map.half_width - 2.0 * GMT->current.proj.w_r * d_sqrt (1.0 - y * y));
2361 }
2362
2363 /*! . */
gmtmap_right_ellipse(struct GMT_CTRL * GMT,double y)2364 GMT_LOCAL double gmtmap_right_ellipse (struct GMT_CTRL *GMT, double y) {
2365 /* Applies to Hammer and Mollweide only, where major axis = 2 * minor axis */
2366
2367 y = (y - GMT->current.proj.origin[GMT_Y]) / GMT->current.proj.w_r; /* Fraction, relative to Equator */
2368 return (GMT->current.map.half_width + 2.0 * GMT->current.proj.w_r * d_sqrt (1.0 - y * y));
2369 }
2370
2371 /*! . */
gmtmap_az_backaz_cartesian(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2372 GMT_LOCAL double gmtmap_az_backaz_cartesian (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2373 /* Calculate azimuths or backazimuths. Cartesian case.
2374 * First point is considered "Event" and second "Station".
2375 * Azimuth is direction from Station to Event.
2376 * BackAzimuth is direction from Event to Station */
2377
2378 double az, dx, dy;
2379
2380 if (baz) { /* exchange point one and two */
2381 gmt_M_double_swap (lonS, lonE);
2382 gmt_M_double_swap (latS, latE);
2383 }
2384 dx = lonS - lonE;
2385 dy = latS - latE;
2386 az = (dx == 0.0 && dy == 0.0) ? GMT->session.d_NaN : 90.0 - atan2d (dy, dx);
2387 if (az < 0.0) az += 360.0;
2388 return (az);
2389 }
2390
2391 /*! . */
gmtmap_az_backaz_cartesian_proj(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2392 GMT_LOCAL double gmtmap_az_backaz_cartesian_proj (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2393 /* Calculate azimuths or backazimuths. Cartesian case.
2394 * First point is considered "Event" and second "Station".
2395 * Azimuth is direction from Station to Event.
2396 * BackAzimuth is direction from Event to Station */
2397
2398 double az, dx, dy, xE, yE, xS, yS;
2399
2400 if (baz) { /* exchange point one and two */
2401 gmt_M_double_swap (lonS, lonE);
2402 gmt_M_double_swap (latS, latE);
2403 }
2404 gmt_geo_to_xy (GMT, lonE, latE, &xE, &yE);
2405 gmt_geo_to_xy (GMT, lonS, latS, &xS, &yS);
2406 dx = xS - xE;
2407 dy = yS - yE;
2408 az = (dx == 0.0 && dy == 0.0) ? GMT->session.d_NaN : 90.0 - atan2d (dy, dx);
2409 if (az < 0.0) az += 360.0;
2410 return (az);
2411 }
2412
2413 /*! . */
gmtmap_az_backaz_flatearth(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2414 GMT_LOCAL double gmtmap_az_backaz_flatearth (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2415 /* Calculate azimuths or backazimuths. Flat earth code.
2416 * First point is considered "Event" and second "Station".
2417 * Azimuth is direction from Station to Event.
2418 * BackAzimuth is direction from Event to Station */
2419
2420 double az, dx, dy, dlon;
2421
2422 if (baz) { /* exchange point one and two */
2423 gmt_M_double_swap (lonS, lonE);
2424 gmt_M_double_swap (latS, latE);
2425 }
2426 gmt_M_set_delta_lon (lonE, lonS, dlon);
2427 dx = dlon * cosd (0.5 * (latS + latE));
2428 dy = latS - latE;
2429 az = (dx == 0.0 && dy == 0.0) ? GMT->session.d_NaN : 90.0 - atan2d (dy, dx);
2430 if (az < 0.0) az += 360.0;
2431 return (az);
2432 }
2433
2434 /*! . */
gmtmap_az_backaz_sphere(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2435 GMT_LOCAL double gmtmap_az_backaz_sphere (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2436 /* Calculate azimuths or backazimuths. Spherical code.
2437 * First point is considered "Event" and second "Station".
2438 * Azimuth is direction from Station to Event.
2439 * BackAzimuth is direction from Event to Station */
2440
2441 double az, sin_yS, cos_yS, sin_yE, cos_yE, sin_dlon, cos_dlon;
2442 gmt_M_unused(GMT);
2443
2444 if (baz) { /* exchange point one and two */
2445 gmt_M_double_swap (lonS, lonE);
2446 gmt_M_double_swap (latS, latE);
2447 }
2448 sincosd (latS, &sin_yS, &cos_yS);
2449 sincosd (latE, &sin_yE, &cos_yE);
2450 sincosd (lonS - lonE, &sin_dlon, &cos_dlon);
2451 az = atan2d (cos_yS * sin_dlon, cos_yE * sin_yS - sin_yE * cos_yS * cos_dlon);
2452 if (az < 0.0) az += 360.0;
2453 return (az);
2454 }
2455
2456 #define VINCENTY_EPS 5e-14
2457 #define VINCENTY_MAX_ITER 50
2458 /*! . */
gmtmap_az_backaz_vincenty(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool back_az)2459 GMT_LOCAL double gmtmap_az_backaz_vincenty (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool back_az) {
2460 /* Translation of NGS FORTRAN code for determination of true distance
2461 ** and respective forward and back azimuths between two points on the
2462 ** ellipsoid. Good for any pair of points that are not antipodal.
2463 **
2464 ** INPUT
2465 ** latS, lonS -- latitude and longitude of first point in radians.
2466 ** latE, lonE -- latitude and longitude of second point in radians.
2467 **
2468 ** OUTPUT
2469 ** faz -- azimuth from first point to second in radians clockwise from North.
2470 ** baz -- azimuth from second point back to first point.
2471 ** bool back_az controls which is returned
2472 ** Modified by P.W. from: http://article.gmane.org/gmane.comp.gis.proj-4.devel/3478
2473 */
2474 int n_iter = 0;
2475 double az, c, d, e, r, f, d_lon, dx, x, y, sa, cx, cy, cz, sx, sy, c2a, cu1, cu2, su1, tu1, tu2, ts, baz, faz;
2476
2477 f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
2478 r = 1.0 - f;
2479 tu1 = r * tand (latS);
2480 tu2 = r * tand (latE);
2481 cu1 = 1.0 / sqrt (tu1 * tu1 + 1.0);
2482 su1 = cu1 * tu1;
2483 cu2 = 1.0 / sqrt (tu2 * tu2 + 1.0);
2484 ts = cu1 * cu2;
2485 baz = ts * tu2;
2486 faz = baz * tu1;
2487 gmt_M_set_delta_lon (lonS, lonE, d_lon);
2488 if (gmt_M_is_zero (d_lon) && doubleAlmostEqualZero (latS, latE)) return GMT->session.d_NaN;
2489 x = dx = D2R * d_lon;
2490 do {
2491 n_iter++;
2492 sincos (x, &sx, &cx);
2493 tu1 = cu2 * sx;
2494 tu2 = baz - su1 * cu2 * cx;
2495 sy = sqrt (tu1 * tu1 + tu2 * tu2);
2496 cy = ts * cx + faz;
2497 y = atan2 (sy, cy);
2498 sa = ts * sx / sy;
2499 c2a = -sa * sa + 1.0;
2500 cz = faz + faz;
2501 if (c2a > 0.0) cz = -cz / c2a + cy;
2502 e = cz * cz * 2.0 - 1.0;
2503 c = ((c2a * -3.0 + 4.0) * f + 4.0) * c2a * f / 16.0;
2504 d = x;
2505 x = ((e * cy * c + cz) * sy * c + y) * sa;
2506 x = (1.0 - c) * x * f + dx;
2507 } while (fabs (d - x) > VINCENTY_EPS && n_iter <= VINCENTY_MAX_ITER);
2508 if (n_iter > VINCENTY_MAX_ITER) {
2509 GMT->current.proj.n_geodesic_approx++; /* Count inaccurate results */
2510 if (GMT->current.proj.n_geodesic_approx == 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "At least one near- or actual antipodal points encountered. Precision may be reduced slightly.\n");
2511 }
2512 GMT->current.proj.n_geodesic_calls++;
2513 /* To give the same sense of results as all other codes, we must basically swap baz and faz; here done in the ? test */
2514 az = (back_az) ? atan2 (tu1, tu2) : atan2 (cu1 * sx, baz * cx - su1 * cu2) + M_PI;
2515 return (R2D * az);
2516 }
2517
2518 /*! . */
gmtmap_az_backaz_rudoe(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)2519 GMT_LOCAL double gmtmap_az_backaz_rudoe (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
2520 /* Calculate azimuths or backazimuths for geodesics using geocentric latitudes.
2521 * First point is considered "Event" and second "Station".
2522 * Azimuth is direction from Station to Event.
2523 * BackAzimuth is direction from Event to Station */
2524
2525 double az, a, b, c, d, e, f, g, h, a1, b1, c1, d1, e1, f1, g1, h1, thg, ss, sc;
2526
2527 /* Equations are unstable for latitudes of exactly 0 degrees. */
2528 if (latE == 0.0) latE = 1.0e-08;
2529 if (latS == 0.0) latS = 1.0e-08;
2530
2531 /* Must convert from geographic to geocentric coordinates in order
2532 * to use the spherical trig equations. This requires a latitude
2533 * correction given by: 1-ECC2=1-2*f + f*f = GMT->current.proj.one_m_ECC2
2534 */
2535
2536 thg = atan (GMT->current.proj.one_m_ECC2 * tand (latE));
2537 sincos (thg, &c, &f); f = -f;
2538 sincosd (lonE, &d, &e); e = -e;
2539 a = f * e;
2540 b = -f * d;
2541 g = -c * e;
2542 h = c * d;
2543
2544 /* Calculating some trig constants. */
2545
2546 thg = atan (GMT->current.proj.one_m_ECC2 * tand (latS));
2547 sincos (thg, &c1, &f1); f1 = -f1;
2548 sincosd (lonS, &d1, &e1); e1 = -e1;
2549 a1 = f1 * e1;
2550 b1 = -f1 * d1;
2551 g1 = -c1 * e1;
2552 h1 = c1 * d1;
2553
2554 /* Spherical trig relationships used to compute angles. */
2555
2556 if (baz) { /* Get Backazimuth */
2557 ss = pow(a-d1,2.0) + pow(b-e1,2.0) + c * c - 2.0;
2558 sc = pow(a-g1,2.0) + pow(b-h1,2.0) + pow(c-f1,2.0) - 2.0;
2559 }
2560 else { /* Get Azimuth */
2561 ss = pow(a1-d, 2.0) + pow(b1-e, 2.0) + c1 * c1 - 2.0;
2562 sc = pow(a1-g, 2.0) + pow(b1-h, 2.0) + pow(c1-f, 2.0) - 2.0;
2563 }
2564 az = atan2d (ss,sc);
2565 if (az < 0.0) az += 360.0;
2566 return (az);
2567 }
2568
2569 /*
2570 * TRANSFORMATION ROUTINES FOR THE LINEAR PROJECTION (GMT_LINEAR)
2571 */
2572
2573 /*! . */
gmtmap_linearxy(struct GMT_CTRL * GMT,double x,double y,double * x_i,double * y_i)2574 GMT_LOCAL void gmtmap_linearxy (struct GMT_CTRL *GMT, double x, double y, double *x_i, double *y_i) {
2575 /* Transform both x and y linearly */
2576 (*GMT->current.proj.fwd_x) (GMT, x, x_i);
2577 (*GMT->current.proj.fwd_y) (GMT, y, y_i);
2578 }
2579
2580 /*! . */
gmtmap_ilinearxy(struct GMT_CTRL * GMT,double * x,double * y,double x_i,double y_i)2581 GMT_LOCAL void gmtmap_ilinearxy (struct GMT_CTRL *GMT, double *x, double *y, double x_i, double y_i) {
2582 /* Inversely transform both x and y linearly */
2583 (*GMT->current.proj.inv_x) (GMT, x, x_i);
2584 (*GMT->current.proj.inv_y) (GMT, y, y_i);
2585 }
2586
2587 GMT_LOCAL unsigned int gmtmap_wrap_around_check_tm (struct GMT_CTRL *GMT, double *angle, double last_x, double last_y, double this_x, double this_y, double *xx, double *yy, unsigned int *sides);
2588
2589 /*! . */
gmtmap_init_linear(struct GMT_CTRL * GMT,bool * search)2590 GMT_LOCAL int gmtmap_init_linear (struct GMT_CTRL *GMT, bool *search) {
2591 bool positive;
2592 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2593
2594 *search = false;
2595 GMT->current.map.left_edge = &gmtmap_left_rect;
2596 GMT->current.map.right_edge = &gmtmap_right_rect;
2597 GMT->current.proj.fwd = &gmtmap_linearxy;
2598 GMT->current.proj.inv = &gmtmap_ilinearxy;
2599 if (gmt_M_x_is_lon (GMT, GMT_IN)) { /* x is longitude */
2600 GMT->current.proj.central_meridian = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
2601 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2602 }
2603 else if (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_ABSTIME && GMT->current.io.cycle_col == GMT_X) {
2604 GMT->current.map.lon_wrap_range = GMT->current.io.cycle_range;
2605 GMT->current.map.is_world = doubleAlmostEqual (fabs(GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]), GMT->current.io.cycle_range);
2606 GMT->current.map.lat_wrap = false;
2607 }
2608 else if (gmt_M_type (GMT, GMT_IN, GMT_Y) == GMT_IS_ABSTIME && GMT->current.io.cycle_col == GMT_Y) {
2609 GMT->current.map.lat_wrap_range = GMT->current.io.cycle_range;
2610 GMT->current.map.is_world = doubleAlmostEqual (fabs(GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]), GMT->current.io.cycle_range);
2611 GMT->current.map.wrap_around_check = &gmtmap_wrap_around_check_tm;
2612 GMT->current.map.lon_wrap = false;
2613 }
2614 else
2615 GMT->current.map.lon_wrap = GMT->current.map.lat_wrap = false;
2616 if (gmt_M_y_is_lon (GMT, GMT_IN)) { /* y is longitude */
2617 GMT->current.proj.central_meridian = 0.5 * (GMT->common.R.wesn[YLO] + GMT->common.R.wesn[YHI]);
2618 }
2619 GMT->current.proj.scale[GMT_X] = GMT->current.proj.pars[0];
2620 GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
2621 GMT->current.proj.xyz_pos[GMT_X] = (GMT->current.proj.scale[GMT_X] >= 0.0); /* False if user wants x to increase left */
2622 GMT->current.proj.xyz_pos[GMT_Y] = (GMT->current.proj.scale[GMT_Y] >= 0.0); /* False if user wants y to increase down */
2623 switch ( (GMT->current.proj.xyz_projection[GMT_X]%3)) { /* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2624 case GMT_LINEAR: /* Regular scaling */
2625 if (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_ABSTIME && GMT->current.proj.xyz_projection[GMT_X] != GMT_TIME && !GMT->current.io.cycle_operator) {
2626 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Option -JX|x: Your x-column contains absolute time but -JX|x...T was not specified!\n");
2627 GMT->current.proj.xyz_projection[GMT_X] = GMT_TIME;
2628 }
2629 GMT->current.proj.fwd_x = ((gmt_M_x_is_lon (GMT, GMT_IN)) ? &gmtproj_translind : &gmtlib_translin);
2630 GMT->current.proj.inv_x = ((gmt_M_x_is_lon (GMT, GMT_IN)) ? &gmtproj_itranslind : &gmtlib_itranslin);
2631 if (GMT->current.proj.xyz_pos[GMT_X]) {
2632 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmin);
2633 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmax);
2634 }
2635 else {
2636 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmin);
2637 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmax);
2638 }
2639 break;
2640 case GMT_LOG10: /* Log10 transformation */
2641 if (GMT->common.R.wesn[XLO] <= 0.0 || GMT->common.R.wesn[XHI] <= 0.0) {
2642 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -JX|x: Limits must be positive for log10 option\n");
2643 return GMT_PROJECTION_ERROR;
2644 }
2645 xmin = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XLO]) : d_log10 (GMT, GMT->common.R.wesn[XHI]);
2646 xmax = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XHI]) : d_log10 (GMT, GMT->common.R.wesn[XLO]);
2647 GMT->current.proj.fwd_x = &gmtproj_translog10;
2648 GMT->current.proj.inv_x = &gmtproj_itranslog10;
2649 break;
2650 case GMT_POW: /* x^y transformation */
2651 GMT->current.proj.xyz_pow[GMT_X] = GMT->current.proj.pars[2];
2652 GMT->current.proj.xyz_ipow[GMT_X] = 1.0 / GMT->current.proj.pars[2];
2653 positive = !((GMT->current.proj.xyz_pos[GMT_X] + (GMT->current.proj.xyz_pow[GMT_X] > 0.0)) % 2);
2654 xmin = (positive) ? pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]);
2655 xmax = (positive) ? pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]);
2656 GMT->current.proj.fwd_x = &gmtproj_transpowx;
2657 GMT->current.proj.inv_x = &gmtproj_itranspowx;
2658 break;
2659 }
2660 switch (GMT->current.proj.xyz_projection[GMT_Y]%3) { /* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2661 case GMT_LINEAR: /* Regular scaling */
2662 if (gmt_M_type (GMT, GMT_IN, GMT_Y) == GMT_IS_ABSTIME && GMT->current.proj.xyz_projection[GMT_Y] != GMT_TIME && !GMT->current.io.cycle_operator) {
2663 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Option -JX|x: Your y-column contains absolute time but -JX|x...T was not specified!\n");
2664 GMT->current.proj.xyz_projection[GMT_Y] = GMT_TIME;
2665 }
2666 GMT->current.proj.fwd_y = ((gmt_M_y_is_lon (GMT, GMT_IN)) ? &gmtproj_translind : &gmtlib_translin);
2667 GMT->current.proj.inv_y = ((gmt_M_y_is_lon (GMT, GMT_IN)) ? &gmtproj_itranslind : &gmtlib_itranslin);
2668 if (GMT->current.proj.xyz_pos[GMT_Y]) {
2669 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymin);
2670 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymax);
2671 }
2672 else {
2673 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymin);
2674 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymax);
2675 }
2676 break;
2677 case GMT_LOG10: /* Log10 transformation */
2678 if (GMT->common.R.wesn[YLO] <= 0.0 || GMT->common.R.wesn[YHI] <= 0.0) {
2679 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -JX|x: Limits must be positive for log10 option\n");
2680 return GMT_PROJECTION_ERROR;
2681 }
2682 ymin = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YLO]) : d_log10 (GMT, GMT->common.R.wesn[YHI]);
2683 ymax = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YHI]) : d_log10 (GMT, GMT->common.R.wesn[YLO]);
2684 GMT->current.proj.fwd_y = &gmtproj_translog10;
2685 GMT->current.proj.inv_y = &gmtproj_itranslog10;
2686 break;
2687 case GMT_POW: /* x^y transformation */
2688 GMT->current.proj.xyz_pow[GMT_Y] = GMT->current.proj.pars[3];
2689 GMT->current.proj.xyz_ipow[GMT_Y] = 1.0 / GMT->current.proj.pars[3];
2690 positive = !((GMT->current.proj.xyz_pos[GMT_Y] + (GMT->current.proj.xyz_pow[GMT_Y] > 0.0)) % 2);
2691 ymin = (positive) ? pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]);
2692 ymax = (positive) ? pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]);
2693 GMT->current.proj.fwd_y = &gmtproj_transpowy;
2694 GMT->current.proj.inv_y = &gmtproj_itranspowy;
2695 }
2696
2697 /* Was given axes length instead of scale? */
2698
2699 if (GMT->current.proj.compute_scale[GMT_X]) GMT->current.proj.scale[GMT_X] /= fabs (xmin - xmax);
2700 if (GMT->current.proj.compute_scale[GMT_Y]) GMT->current.proj.scale[GMT_Y] /= fabs (ymin - ymax);
2701
2702 /* If either is zero, adjust width or height to the other */
2703
2704 if (GMT->current.proj.scale[GMT_X] == 0) { /* Must redo x-scaling by using y-scale */
2705 GMT->current.proj.scale[GMT_X] = GMT->current.proj.autoscl[GMT_X] * GMT->current.proj.scale[GMT_Y];
2706 if (GMT->current.proj.autoscl[GMT_X] == -1) GMT->current.proj.xyz_pos[GMT_X] = !GMT->current.proj.xyz_pos[GMT_Y];
2707 switch ( (GMT->current.proj.xyz_projection[GMT_X]%3)) { /* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2708 case GMT_LINEAR: /* Regular scaling */
2709 if (GMT->current.proj.xyz_pos[GMT_X]) {
2710 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmin);
2711 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmax);
2712 }
2713 else {
2714 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XHI], &xmin);
2715 (*GMT->current.proj.fwd_x) (GMT, GMT->common.R.wesn[XLO], &xmax);
2716 }
2717 break;
2718 case GMT_LOG10: /* Log10 transformation */
2719 xmin = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XLO]) : d_log10 (GMT, GMT->common.R.wesn[XHI]);
2720 xmax = (GMT->current.proj.xyz_pos[GMT_X]) ? d_log10 (GMT, GMT->common.R.wesn[XHI]) : d_log10 (GMT, GMT->common.R.wesn[XLO]);
2721 break;
2722 case GMT_POW: /* x^y transformation */
2723 positive = !((GMT->current.proj.xyz_pos[GMT_X] + (GMT->current.proj.xyz_pow[GMT_X] > 0.0)) % 2);
2724 xmin = (positive) ? pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]);
2725 xmax = (positive) ? pow (GMT->common.R.wesn[XHI], GMT->current.proj.xyz_pow[GMT_X]) : pow (GMT->common.R.wesn[XLO], GMT->current.proj.xyz_pow[GMT_X]);
2726 break;
2727 }
2728 GMT->current.proj.pars[0] = GMT->current.proj.scale[GMT_X] * fabs (xmin - xmax);
2729 }
2730 else if (GMT->current.proj.scale[GMT_Y] == 0) { /* Must redo y-scaling by using x-scale */
2731 GMT->current.proj.scale[GMT_Y] = GMT->current.proj.autoscl[GMT_Y] * GMT->current.proj.scale[GMT_X];
2732 if (GMT->current.proj.autoscl[GMT_Y] == -1) GMT->current.proj.xyz_pos[GMT_Y] = !GMT->current.proj.xyz_pos[GMT_X];
2733 switch (GMT->current.proj.xyz_projection[GMT_Y]%3) { /* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
2734 case GMT_LINEAR: /* Regular scaling */
2735 if (GMT->current.proj.xyz_pos[GMT_Y]) {
2736 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymin);
2737 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymax);
2738 }
2739 else {
2740 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YHI], &ymin);
2741 (*GMT->current.proj.fwd_y) (GMT, GMT->common.R.wesn[YLO], &ymax);
2742 }
2743 break;
2744 case GMT_LOG10: /* Log10 transformation */
2745 ymin = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YLO]) : d_log10 (GMT, GMT->common.R.wesn[YHI]);
2746 ymax = (GMT->current.proj.xyz_pos[GMT_Y]) ? d_log10 (GMT, GMT->common.R.wesn[YHI]) : d_log10 (GMT, GMT->common.R.wesn[YLO]);
2747 break;
2748 case GMT_POW: /* x^y transformation */
2749 positive = !((GMT->current.proj.xyz_pos[GMT_Y] + (GMT->current.proj.xyz_pow[GMT_Y] > 0.0)) % 2);
2750 ymin = (positive) ? pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]);
2751 ymax = (positive) ? pow (GMT->common.R.wesn[YHI], GMT->current.proj.xyz_pow[GMT_Y]) : pow (GMT->common.R.wesn[YLO], GMT->current.proj.xyz_pow[GMT_Y]);
2752 }
2753 GMT->current.proj.pars[1] = GMT->current.proj.scale[GMT_Y] * fabs (ymin - ymax);
2754 }
2755
2756 /* This override ensures that when using -J[x|X]...d degrees work as meters */
2757
2758 GMT->current.proj.M_PR_DEG = 1.0;
2759 GMT->current.proj.KM_PR_DEG = GMT->current.proj.M_PR_DEG / METERS_IN_A_KM;
2760
2761 gmtmap_setxy (GMT, xmin, xmax, ymin, ymax);
2762 if (gmt_M_type (GMT, GMT_IN, GMT_X) == GMT_IS_LON) { /* Using linear projection with longitudes */
2763 GMT->current.map.outside = &gmtmap_wesn_outside;
2764 GMT->current.map.crossing = &gmtmap_wesn_crossing;
2765 GMT->current.map.overlap = &gmtmap_wesn_overlap;
2766 GMT->current.map.clip = &map_wesn_clip;
2767 }
2768 else {
2769 GMT->current.proj.VE = GMT->current.proj.scale[GMT_Y] / GMT->current.proj.scale[GMT_X];
2770 if (GMT->current.proj.VE < 1.0)
2771 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Linear projection implies x-axis distance exaggeration relative to the y-axis by a factor of %g\n", GMT->current.proj.VE);
2772 else if (GMT->current.proj.VE > 1.0)
2773 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Linear projection implies y-axis distance exaggeration relative to the x-axis by a factor of %g\n", GMT->current.proj.VE);
2774
2775 GMT->current.map.outside = &gmtmap_rect_outside;
2776 GMT->current.map.crossing = &gmtmap_rect_crossing;
2777 GMT->current.map.overlap = &gmtmap_cartesian_overlap;
2778 GMT->current.map.clip = &gmtmap_rect_clip;
2779 }
2780 GMT->current.map.n_lat_nodes = 2;
2781 GMT->current.map.n_lon_nodes = 3; /* > 2 to avoid map-jumps */
2782 GMT->current.map.frame.check_side = true;
2783 GMT->current.map.frame.horizontal = 1;
2784 GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2785
2786 return (GMT_NOERROR);
2787 }
2788
2789 /*!
2790 * TRANSFORMATION ROUTINES FOR POLAR (theta,r) PROJECTION (GMT_POLAR)
2791 */
gmtmap_init_polar(struct GMT_CTRL * GMT,bool * search)2792 GMT_LOCAL int gmtmap_init_polar (struct GMT_CTRL *GMT, bool *search) {
2793 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2794
2795 *search = false;
2796 gmtproj_vpolar (GMT, GMT->current.proj.pars[1]);
2797 if (GMT->current.proj.angle_kind == GMT_IS_LAT && (GMT->common.R.wesn[XLO] < -90.0 || GMT->common.R.wesn[XHI] > 90.0)) {
2798 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R: Your +ky modifier for -Jp|P implies angles must be contained in the -90/+90 range (latitudes)\n");
2799 return GMT_PROJECTION_ERROR;
2800 }
2801 if (GMT->current.proj.flip) { /* Check restrictions */
2802 if (GMT->common.R.wesn[YLO] < 0.0 || GMT->common.R.wesn[YHI] > GMT->current.proj.flip_radius) {
2803 GMT_Report (GMT->parent, GMT_MSG_ERROR, "-JP...+f requires s >= 0 and n <= %g!\n", GMT->current.proj.flip_radius);
2804 return GMT_PROJECTION_ERROR;
2805 }
2806 if (doubleAlmostEqual (GMT->common.R.wesn[YHI], GMT->current.proj.flip_radius) && gmt_M_is_zero (GMT->current.proj.radial_offset))
2807 GMT->current.proj.edge[2] = false;
2808 }
2809 else {
2810 if (gmt_M_is_zero (GMT->common.R.wesn[YLO]) && gmt_M_is_zero (GMT->current.proj.radial_offset))
2811 GMT->current.proj.edge[0] = false;
2812 }
2813 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
2814 GMT->current.map.left_edge = &gmtmap_left_circle;
2815 GMT->current.map.right_edge = &gmtmap_right_circle;
2816 GMT->current.proj.fwd = &gmtproj_polar;
2817 GMT->current.proj.inv = &gmtproj_ipolar;
2818 GMT->current.map.is_world = false; /* There is no wrapping around here */
2819 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
2820 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[0];
2821 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[0]);
2822 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
2823 /* GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI]; */
2824 GMT->current.proj.r = GMT->current.proj.scale[GMT_Y] * GMT->common.R.wesn[YHI];
2825 GMT->current.map.outside = &gmtmap_polar_outside;
2826 GMT->current.map.crossing = &gmtmap_wesn_crossing;
2827 GMT->current.map.overlap = &gmtmap_wesn_overlap;
2828 GMT->current.map.clip = &map_wesn_clip;
2829 GMT->current.map.frame.horizontal = 1;
2830 if (!GMT->current.proj.got_elevations) GMT->current.plot.r_theta_annot = true; /* Special labeling case (see gmtlib_get_annot_label) */
2831 GMT->current.map.n_lat_nodes = 2;
2832 GMT->current.map.meridian_straight = 1;
2833
2834 return (GMT_NOERROR);
2835 }
2836
2837 /*!
2838 * TRANSFORMATION ROUTINES FOR THE MERCATOR PROJECTION (GMT_MERCATOR)
2839 */
gmtmap_init_merc(struct GMT_CTRL * GMT,bool * search)2840 GMT_LOCAL int gmtmap_init_merc (struct GMT_CTRL *GMT, bool *search) {
2841 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, D = 1.0;
2842
2843 *search = false; /* No need to search for wesn */
2844 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
2845 if (GMT->current.proj.GMT_convert_latitudes) { /* Set fudge factor */
2846 gmtlib_scale_eqrad (GMT);
2847 D = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius / GMT->current.proj.lat_swap_vals.rm;
2848 }
2849 if (doubleAlmostEqual (GMT->common.R.wesn[YLO], -90.0) || doubleAlmostEqual (GMT->common.R.wesn[YHI], 90.0)) {
2850 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R: Cannot include south/north poles with Mercator projection!\n");
2851 return GMT_PROJECTION_ERROR;
2852 }
2853 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2854 gmtmap_cyl_validate_clon (GMT, 0); /* Make sure the central longitude is valid */
2855 gmtproj_vmerc (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
2856 GMT->current.proj.j_x *= D;
2857 GMT->current.proj.j_yc *= D;
2858 GMT->current.proj.j_ix /= D;
2859 GMT->current.proj.fwd = &gmtproj_merc_sph;
2860 GMT->current.proj.inv = &gmtproj_imerc_sph;
2861 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2862 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2863 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= (D * GMT->current.proj.M_PR_DEG);
2864 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
2865 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
2866 GMT->current.map.n_lat_nodes = 2;
2867 GMT->current.map.n_lon_nodes = 3; /* > 2 to avoid map-jumps */
2868 GMT->current.map.outside = &gmtmap_wesn_outside;
2869 GMT->current.map.crossing = &gmtmap_wesn_crossing;
2870 GMT->current.map.overlap = &gmtmap_wesn_overlap;
2871 GMT->current.map.clip = &map_wesn_clip;
2872 GMT->current.map.left_edge = &gmtmap_left_rect;
2873 GMT->current.map.right_edge = &gmtmap_right_rect;
2874 GMT->current.map.frame.horizontal = 1;
2875 GMT->current.map.frame.check_side = true;
2876 GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2877
2878 return (GMT_NOERROR);
2879 }
2880
2881 /*!
2882 * TRANSFORMATION ROUTINES FOR CYLINDRICAL EQUAL-AREA PROJECTIONS (GMT_CYL_EQ)
2883 */
gmtmap_init_cyleq(struct GMT_CTRL * GMT,bool * search)2884 GMT_LOCAL int gmtmap_init_cyleq (struct GMT_CTRL *GMT, bool *search) {
2885 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2886
2887 *search = false; /* No need to search for wesn */
2888 GMT->current.proj.Dx = GMT->current.proj.Dy = 0.0;
2889 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
2890 if (GMT->current.proj.GMT_convert_latitudes) {
2891 double D, k0, qp, slat, e, e2;
2892 gmtlib_scale_eqrad (GMT);
2893 slat = GMT->current.proj.pars[1];
2894 GMT->current.proj.pars[1] = gmt_M_latg_to_lata (GMT, GMT->current.proj.pars[1]);
2895 e = GMT->current.proj.ECC;
2896 e2 = GMT->current.proj.ECC2;
2897 qp = 1.0 - 0.5 * (1.0 - e2) * log ((1.0 - e) / (1.0 + e)) / e;
2898 k0 = cosd (slat) / d_sqrt (1.0 - e2 * sind (GMT->current.proj.pars[1]) * sind (GMT->current.proj.pars[1]));
2899 D = k0 / cosd (GMT->current.proj.pars[1]);
2900 GMT->current.proj.Dx = D;
2901 GMT->current.proj.Dy = 0.5 * qp / D;
2902 }
2903 GMT->current.proj.iDx = 1.0 / GMT->current.proj.Dx;
2904 GMT->current.proj.iDy = 1.0 / GMT->current.proj.Dy;
2905 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2906 gmtmap_cyl_validate_clon (GMT, 1); /* Make sure the central longitude is valid */
2907 gmtproj_vcyleq (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
2908 gmtproj_cyleq (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2909 gmtproj_cyleq (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2910 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
2911 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
2912 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
2913 GMT->current.map.n_lat_nodes = 2;
2914 GMT->current.map.n_lon_nodes = 3; /* > 2 to avoid map-jumps */
2915 GMT->current.proj.fwd = &gmtproj_cyleq;
2916 GMT->current.proj.inv = &gmtproj_icyleq;
2917 GMT->current.map.outside = &gmtmap_wesn_outside;
2918 GMT->current.map.crossing = &gmtmap_wesn_crossing;
2919 GMT->current.map.overlap = &gmtmap_wesn_overlap;
2920 GMT->current.map.clip = &map_wesn_clip;
2921 GMT->current.map.left_edge = &gmtmap_left_rect;
2922 GMT->current.map.right_edge = &gmtmap_right_rect;
2923 GMT->current.map.frame.horizontal = 1;
2924 GMT->current.map.frame.check_side = true;
2925 GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2926
2927 return (GMT_NOERROR);
2928 }
2929
2930 /*!
2931 * TRANSFORMATION ROUTINES FOR CYLINDRICAL EQUIDISTANT PROJECTION (GMT_CYL_EQDIST)
2932 */
2933
gmtmap_init_cyleqdist(struct GMT_CTRL * GMT,bool * search)2934 GMT_LOCAL bool gmtmap_init_cyleqdist (struct GMT_CTRL *GMT, bool *search) {
2935 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
2936
2937 *search = false; /* No need to search for wesn */
2938
2939 gmtmap_set_spherical (GMT, true); /* Force spherical for now */
2940
2941 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2942 gmtmap_cyl_validate_clon (GMT, 1); /* Make sure the central longitude is valid */
2943 gmtproj_vcyleqdist (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
2944 gmtproj_cyleqdist (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2945 gmtproj_cyleqdist (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2946 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
2947 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
2948 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
2949 GMT->current.map.n_lat_nodes = 2;
2950 GMT->current.map.n_lon_nodes = 3; /* > 2 to avoid map-jumps */
2951 GMT->current.proj.fwd = &gmtproj_cyleqdist;
2952 GMT->current.proj.inv = &gmtproj_icyleqdist;
2953 GMT->current.map.outside = &gmtmap_wesn_outside;
2954 GMT->current.map.crossing = &gmtmap_wesn_crossing;
2955 GMT->current.map.overlap = &gmtmap_wesn_overlap;
2956 GMT->current.map.clip = &map_wesn_clip;
2957 GMT->current.map.left_edge = &gmtmap_left_rect;
2958 GMT->current.map.right_edge = &gmtmap_right_rect;
2959 GMT->current.map.frame.horizontal = 1;
2960 GMT->current.map.frame.check_side = true;
2961 GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
2962
2963
2964 return (GMT_NOERROR);
2965 }
2966
2967 /*!
2968 * TRANSFORMATION ROUTINES FOR MILLER CYLINDRICAL PROJECTION (GMT_MILLER)
2969 */
gmtmap_init_miller(struct GMT_CTRL * GMT,bool * search)2970 GMT_LOCAL int gmtmap_init_miller (struct GMT_CTRL *GMT, bool *search) {
2971 double xmin, xmax, ymin, ymax;
2972
2973 *search = false; /* No need to search for wesn */
2974
2975 gmtmap_set_spherical (GMT, true); /* Force spherical for now */
2976
2977 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
2978 gmtmap_cyl_validate_clon (GMT, 1); /* Make sure the central longitude is valid */
2979 gmtproj_vmiller (GMT, GMT->current.proj.pars[0]);
2980 gmtproj_miller (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2981 #ifdef CHRISTMAS
2982 if (GMT->common.R.wesn[YLO] > 0.0) {
2983 gmtproj_miller (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2984 gmtproj_miller (GMT, 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]), GMT->common.R.wesn[YHI], &xmax, &ymax);
2985 }
2986 else {
2987 gmtproj_miller (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmin, &ymin);
2988 gmtproj_miller (GMT, 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]), GMT->common.R.wesn[YLO], &xmax, &ymax);
2989 }
2990 #else
2991 gmtproj_miller (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
2992 gmtproj_miller (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
2993 #endif
2994 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
2995 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
2996 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
2997 GMT->current.map.n_lat_nodes = 2;
2998 GMT->current.map.n_lon_nodes = 3; /* > 2 to avoid map-jumps */
2999 GMT->current.proj.fwd = &gmtproj_miller;
3000 GMT->current.proj.inv = &gmtproj_imiller;
3001 GMT->current.map.outside = &gmtmap_wesn_outside;
3002 GMT->current.map.crossing = &gmtmap_wesn_crossing;
3003 GMT->current.map.overlap = &gmtmap_wesn_overlap;
3004 GMT->current.map.clip = &map_wesn_clip;
3005 GMT->current.map.left_edge = &gmtmap_left_rect;
3006 GMT->current.map.right_edge = &gmtmap_right_rect;
3007 GMT->current.map.frame.horizontal = 1;
3008 GMT->current.map.frame.check_side = true;
3009 GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
3010
3011 return (GMT_NOERROR);
3012 }
3013
3014 /*!
3015 * TRANSFORMATION ROUTINES FOR CYLINDRICAL STEREOGRAPHIC PROJECTIONS (GMT_CYL_STEREO)
3016 */
gmtmap_init_cylstereo(struct GMT_CTRL * GMT,bool * search)3017 GMT_LOCAL int gmtmap_init_cylstereo (struct GMT_CTRL *GMT, bool *search) {
3018 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
3019
3020 *search = false; /* No need to search for wesn */
3021 gmtmap_set_spherical (GMT, true); /* Force spherical for now */
3022
3023 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
3024 gmtmap_cyl_validate_clon (GMT, 1); /* Make sure the central longitude is valid */
3025 gmtproj_vcylstereo (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
3026 gmtproj_cylstereo (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3027 gmtproj_cylstereo (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3028 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
3029 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
3030 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
3031 GMT->current.map.n_lat_nodes = 2;
3032 GMT->current.map.n_lon_nodes = 3; /* > 2 to avoid map-jumps */
3033 GMT->current.proj.fwd = &gmtproj_cylstereo;
3034 GMT->current.proj.inv = &gmtproj_icylstereo;
3035 GMT->current.map.outside = &gmtmap_wesn_outside;
3036 GMT->current.map.crossing = &gmtmap_wesn_crossing;
3037 GMT->current.map.overlap = &gmtmap_wesn_overlap;
3038 GMT->current.map.clip = &map_wesn_clip;
3039 GMT->current.map.left_edge = &gmtmap_left_rect;
3040 GMT->current.map.right_edge = &gmtmap_right_rect;
3041 GMT->current.map.frame.horizontal = 1;
3042 GMT->current.map.frame.check_side = true;
3043 GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 1;
3044
3045 return (GMT_NOERROR);
3046 }
3047
3048
3049 /*!
3050 * TRANSFORMATION ROUTINES FOR THE POLAR STEREOGRAPHIC PROJECTION (GMT_STEREO)
3051 */
3052
gmtmap_init_stereo(struct GMT_CTRL * GMT,bool * search)3053 GMT_LOCAL int gmtmap_init_stereo (struct GMT_CTRL *GMT, bool *search) {
3054 unsigned int i;
3055 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0, latg, D = 1.0;
3056
3057 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
3058 latg = GMT->current.proj.pars[1];
3059
3060 gmtmap_set_polar (GMT);
3061
3062 if (GMT->current.setting.proj_scale_factor == -1.0) GMT->current.setting.proj_scale_factor = 0.9996; /* Select default map scale for Stereographic */
3063 if (GMT->current.proj.polar && (lrint (GMT->current.proj.pars[5]) == 1)) GMT->current.setting.proj_scale_factor = 1.0; /* Gave true scale at given parallel set below */
3064 /* Equatorial view has a problem with infinite loops. Until I find a cure
3065 we set projection center latitude to 0.001 so equatorial works for now */
3066
3067 if (fabs (GMT->current.proj.pars[1]) < GMT_CONV4_LIMIT) GMT->current.proj.pars[1] = 0.001;
3068
3069 gmtproj_vstereo (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3070
3071 if (GMT->current.proj.GMT_convert_latitudes) { /* Set fudge factors when conformal latitudes are used */
3072 double e1p, e1m, s, c;
3073
3074 D = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius / GMT->current.proj.lat_swap_vals.rm;
3075 if (GMT->current.proj.polar) {
3076 e1p = 1.0 + GMT->current.proj.ECC; e1m = 1.0 - GMT->current.proj.ECC;
3077 D /= d_sqrt (pow (e1p, e1p) * pow (e1m, e1m));
3078 if (lrint (GMT->current.proj.pars[5]) == 1) { /* Gave true scale at given parallel */
3079 double k_p, m_c, t_c, es;
3080
3081 sincosd (fabs (GMT->current.proj.pars[4]), &s, &c);
3082 es = GMT->current.proj.ECC * s;
3083 m_c = c / d_sqrt (1.0 - GMT->current.proj.ECC2 * s * s);
3084 t_c = d_sqrt (((1.0 - s) / (1.0 + s)) * pow ((1.0 + es) / (1.0 - es), GMT->current.proj.ECC));
3085 k_p = 0.5 * m_c * d_sqrt (pow (e1p, e1p) * pow (e1m, e1m)) / t_c;
3086 D *= k_p;
3087 }
3088 }
3089 else {
3090 sincosd (latg, &s, &c); /* Need original geographic pole coordinates */
3091 D *= (c / (GMT->current.proj.cosp * d_sqrt (1.0 - GMT->current.proj.ECC2 * s * s)));
3092 }
3093 }
3094 GMT->current.proj.Dx = GMT->current.proj.Dy = D;
3095
3096 GMT->current.proj.iDx = 1.0 / GMT->current.proj.Dx;
3097 GMT->current.proj.iDy = 1.0 / GMT->current.proj.Dy;
3098
3099 if (GMT->current.proj.polar) { /* Polar aspect */
3100 GMT->current.proj.fwd = &gmtproj_plrs_sph;
3101 GMT->current.proj.inv = &gmtproj_iplrs_sph;
3102 if (GMT->current.proj.units_pr_degree) {
3103 (*GMT->current.proj.fwd) (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[4], &dummy, &radius);
3104 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3105 }
3106 else
3107 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3108 GMT->current.map.meridian_straight = 1;
3109 }
3110 else {
3111 GMT->current.proj.fwd = (gmt_M_is_zero (GMT->current.proj.pole)) ? &gmtproj_stereo2_sph : &gmtproj_stereo1_sph;
3112 GMT->current.proj.inv = &gmtproj_istereo_sph;
3113 if (GMT->current.proj.units_pr_degree) {
3114 gmtproj_vstereo (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
3115 (*GMT->current.proj.fwd) (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
3116 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3117 }
3118 else
3119 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3120
3121 gmtproj_vstereo (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3122 }
3123
3124
3125 if (GMT->common.R.oblique) { /* Rectangular box given */
3126 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3127 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3128
3129 GMT->current.map.outside = &gmtmap_rect_outside2;
3130 GMT->current.map.crossing = &gmtmap_rect_crossing;
3131 GMT->current.map.overlap = &gmtmap_rect_overlap;
3132 GMT->current.map.clip = &gmtmap_rect_clip;
3133 GMT->current.map.left_edge = &gmtmap_left_rect;
3134 GMT->current.map.right_edge = &gmtmap_right_rect;
3135 GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3136 GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
3137 }
3138 else {
3139 if (GMT->current.proj.polar) { /* Polar aspect */
3140 if (GMT->current.proj.north_pole) {
3141 double hor_lat = 90.0 - GMT->current.proj.f_horizon;
3142 if (GMT->common.R.wesn[YLO] < hor_lat) {
3143 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "South boundary cannot be beyond the horizon for north polar stereographic projection (reset to horizon at %lg)\n", hor_lat);
3144 GMT->common.R.wesn[YLO] = hor_lat;
3145 }
3146 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
3147 }
3148 else {
3149 double hor_lat = GMT->current.proj.f_horizon - 90.0;
3150 if (GMT->common.R.wesn[YHI] > hor_lat) {
3151 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "North boundary cannot be beyond the horizon for south polar stereographic projection (reset to horizon at %lg)\n", hor_lat);
3152 GMT->common.R.wesn[YHI] = hor_lat;
3153 }
3154 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
3155 }
3156 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
3157 || doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
3158 GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
3159 GMT->current.map.outside = &gmtmap_polar_outside;
3160 GMT->current.map.crossing = &gmtmap_wesn_crossing;
3161 GMT->current.map.overlap = &gmtmap_wesn_overlap;
3162 GMT->current.map.clip = &map_wesn_clip;
3163 GMT->current.map.frame.horizontal = 1;
3164 GMT->current.map.n_lat_nodes = 2;
3165 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3166 }
3167 else { /* Global view only */
3168 /* No annotations or tickmarks in global mode */
3169 for (i = 0; i < GMT_GRID_UPPER; i++) {
3170 GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
3171 GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
3172 }
3173 GMT->common.R.wesn[XLO] = 0.0;
3174 GMT->common.R.wesn[XHI] = 360.0;
3175 GMT->common.R.wesn[YLO] = -90.0;
3176 GMT->common.R.wesn[YHI] = 90.0;
3177 xmax = ymax = GMT->current.proj.rho_max;
3178 xmin = ymin = -xmax;
3179 GMT->current.map.outside = &gmtmap_radial_outside;
3180 GMT->current.map.crossing = &gmtmap_radial_crossing;
3181 GMT->current.map.overlap = &gmtmap_radial_overlap;
3182 GMT->current.map.clip = &gmtmap_radial_clip;
3183 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3184 }
3185 GMT->current.map.left_edge = &gmtmap_left_circle;
3186 GMT->current.map.right_edge = &gmtmap_right_circle;
3187 }
3188
3189 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
3190 GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
3191 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
3192
3193 *search = GMT->common.R.oblique;
3194 return (GMT_NOERROR);
3195 }
3196
3197 /*!
3198 * TRANSFORMATION ROUTINES FOR THE LAMBERT CONFORMAL CONIC PROJECTION (GMT_LAMBERT)
3199 */
3200
gmtmap_init_lambert(struct GMT_CTRL * GMT,bool * search)3201 GMT_LOCAL int gmtmap_init_lambert (struct GMT_CTRL *GMT, bool *search) {
3202 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
3203
3204 GMT->current.proj.GMT_convert_latitudes = gmtmap_quickconic (GMT);
3205 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3206 gmtproj_vlamb (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
3207 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG;
3208 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
3209 if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) { /* Spherical code w/wo conformal latitudes */
3210 GMT->current.proj.fwd = &gmtproj_lamb_sph;
3211 GMT->current.proj.inv = &gmtproj_ilamb_sph;
3212 }
3213 else {
3214 GMT->current.proj.fwd = &gmtproj_lamb;
3215 GMT->current.proj.inv = &gmtproj_ilamb;
3216 }
3217
3218 if (GMT->common.R.oblique) { /* Rectangular box given*/
3219 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3220 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3221
3222 GMT->current.map.outside = &gmtmap_rect_outside;
3223 GMT->current.map.crossing = &gmtmap_rect_crossing;
3224 GMT->current.map.overlap = &gmtmap_rect_overlap;
3225 GMT->current.map.clip = &gmtmap_rect_clip;
3226 GMT->current.map.left_edge = &gmtmap_left_rect;
3227 GMT->current.map.right_edge = &gmtmap_right_rect;
3228 GMT->current.map.frame.check_side = true;
3229 }
3230 else {
3231 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3232 GMT->current.map.outside = &gmtmap_wesn_outside;
3233 GMT->current.map.crossing = &gmtmap_wesn_crossing;
3234 GMT->current.map.overlap = &gmtmap_wesn_overlap;
3235 GMT->current.map.clip = &map_wesn_clip;
3236 GMT->current.map.left_edge = &gmtmap_left_conic;
3237 GMT->current.map.right_edge = &gmtmap_right_conic;
3238 }
3239 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
3240 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
3241 GMT->current.map.n_lat_nodes = 2;
3242 GMT->current.map.frame.horizontal = 1;
3243 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
3244 GMT->current.map.meridian_straight = 1;
3245
3246 *search = GMT->common.R.oblique;
3247 return (GMT_NOERROR);
3248 }
3249
3250 /*!
3251 * TRANSFORMATION ROUTINES FOR THE OBLIQUE MERCATOR PROJECTION (GMT_OBLIQUE_MERC)
3252 */
3253
gmtmap_translate_point_spherical(struct GMT_CTRL * GMT,double lon,double lat,double azimuth,double distance,double * tlon,double * tlat,double * back_azimuth)3254 GMT_LOCAL void gmtmap_translate_point_spherical (struct GMT_CTRL *GMT, double lon, double lat, double azimuth, double distance, double *tlon, double *tlat, double *back_azimuth) {
3255 /* compute new point dist degrees from input point along azimuth */
3256 double sa, ca, sd, cd, sy, cy;
3257 gmt_M_unused (GMT);
3258 sincosd (lat, &sy, &cy);
3259 sincosd (azimuth, &sa, &ca);
3260 sincosd (distance, &sd, &cd);
3261 *tlon = lon + atand (sd * sa / (cy * cd - sy * sd * ca));
3262 *tlat = d_asind (sy * cd + cy * sd * ca);
3263 if (back_azimuth)
3264 *back_azimuth = gmtmap_az_backaz_sphere (GMT, lon, lat, *tlon, *tlat, true);
3265 }
3266
gmt_translate_point(struct GMT_CTRL * GMT,double lon,double lat,double azimuth,double distance,double * tlon,double * tlat,double * back_azimuth)3267 void gmt_translate_point (struct GMT_CTRL *GMT, double lon, double lat, double azimuth, double distance, double *tlon, double *tlat, double *back_azimuth) {
3268 /* compute new point dist degrees from input point along azimuth */
3269 gmtmap_translate_point_spherical (GMT, lon, lat, azimuth, distance, tlon, tlat, back_azimuth);
3270 }
3271
gmtmap_translate_point_geodesic(struct GMT_CTRL * GMT,double lon1,double lat1,double azimuth,double distance_m,double * lon2,double * lat2,double * back_azimuth)3272 GMT_LOCAL void gmtmap_translate_point_geodesic (struct GMT_CTRL *GMT, double lon1, double lat1, double azimuth, double distance_m, double *lon2, double *lat2, double *back_azimuth) {
3273 /* Use Vincenty (1975) solution to the direct geodesic problem. Unstable for near antipodal points */
3274 double a = GMT->current.proj.EQ_RAD, f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening, f1 = 1.0 - f;
3275 double b = a * f1, s = distance_m, alpha1 = azimuth * D2R, s_alpha1, c_alpha1, tan_U1, c_U1, s_U1, sigma1, s_alpha, cosSqAlpha, cos2SigmaM, tmp;
3276 double uSq, A, B, sigma, sigmaP, deltaSigma, sinSigma, cosSigma, lambda, C, L;
3277
3278 sincos (alpha1, &s_alpha1, &c_alpha1);
3279 tan_U1 = f1 * tand (lat1);
3280 c_U1 = 1.0 / sqrt ((1.0 + tan_U1 * tan_U1));
3281 s_U1 = tan_U1 * c_U1,
3282 sigma1 = atan2 (tan_U1, c_alpha1);
3283 s_alpha = c_U1 * s_alpha1;
3284 cosSqAlpha = 1.0 - s_alpha * s_alpha;
3285 uSq = cosSqAlpha * (a * a - b * b) / (b * b);
3286 A = 1.0 + uSq / 16384.0 * (4096.0 + uSq * (-768.0 + uSq * (320.0 - 175.0 * uSq)));
3287 B = uSq / 1024.0 * (256.0 + uSq * (-128.0 + uSq * (74.0 - 47.0 * uSq)));
3288 sigma = s / (b * A);
3289 sigmaP = 2 * M_PI;
3290 while (fabs (sigma - sigmaP) > 1e-12) {
3291 cos2SigmaM = cos (2.0 * sigma1 + sigma);
3292 sincos (sigma, &sinSigma, &cosSigma);
3293 deltaSigma = B * sinSigma * (cos2SigmaM + B / 4.0 * (cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM) - B / 6.0 * cos2SigmaM * (-3.0 + 4.0 * sinSigma * sinSigma) * (-3.0 + 4.0 * cos2SigmaM * cos2SigmaM)));
3294 sigmaP = sigma;
3295 sigma = s / (b * A) + deltaSigma;
3296 }
3297 tmp = s_U1 * sinSigma - c_U1 * cosSigma * c_alpha1;
3298 *lat2 = R2D * atan2 (s_U1 * cosSigma + c_U1 * sinSigma * c_alpha1, f1 * sqrt (s_alpha * s_alpha + tmp * tmp));
3299 lambda = atan2 (sinSigma * s_alpha1, c_U1 * cosSigma - s_U1 * sinSigma * c_alpha1);
3300 C = f / 16.0 * cosSqAlpha * (4.0 + f * (4.0 - 3.0 * cosSqAlpha));
3301 L = lambda - (1.0 - C) * f * s_alpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1.0 + 2.0 * cos2SigmaM * cos2SigmaM)));
3302 *lon2 = lon1 + L * R2D;
3303 if (back_azimuth) *back_azimuth = R2D * atan2 (s_alpha, -tmp); /* final back azimuth */
3304 }
3305
gmtmap_pole_rotate_forward(struct GMT_CTRL * GMT,double lon,double lat,double * tlon,double * tlat)3306 GMT_LOCAL void gmtmap_pole_rotate_forward (struct GMT_CTRL *GMT, double lon, double lat, double *tlon, double *tlat) {
3307 /* Given the pole position in GMT->current.proj, geographical coordinates
3308 * are computed from oblique coordinates assuming a spherical earth.
3309 * Latitutes and longitudes are in degrees.
3310 */
3311
3312 double sin_lat, cos_lat, cos_lon, sin_lon, cc;
3313
3314 sincosd (lat, &sin_lat, &cos_lat);
3315 sincosd (lon - GMT->current.proj.o_pole_lon, &sin_lon, &cos_lon);
3316 cc = cos_lat * cos_lon;
3317 *tlat = d_asind (GMT->current.proj.o_sin_pole_lat * sin_lat + GMT->current.proj.o_cos_pole_lat * cc);
3318 *tlon = GMT->current.proj.o_beta + d_atan2d (cos_lat * sin_lon, GMT->current.proj.o_sin_pole_lat * cc - GMT->current.proj.o_cos_pole_lat * sin_lat);
3319 }
3320
3321 #if 0
3322 /* Not currently used in GMT */
3323 GMT_LOCAL void gmtmap_pole_rotate_inverse (struct GMT_CTRL *GMT, double *lon, double *lat, double tlon, double tlat) {
3324 /* Given the pole position in GMT->current.proj, geographical coordinates
3325 * are computed from oblique coordinates assuming a spherical earth.
3326 * Latitutes and longitudes are in degrees.
3327 */
3328
3329 double sin_tlat, cos_tlat, cos_tlon, sin_tlon, cc;
3330
3331 sincosd (tlat, &sin_tlat, &cos_tlat);
3332 sincosd (tlon - GMT->current.proj.o_beta, &sin_tlon, &cos_tlon);
3333 cc = cos_tlat * cos_tlon;
3334 *lat = d_asind (GMT->current.proj.o_sin_pole_lat * sin_tlat - GMT->current.proj.o_cos_pole_lat * cc);
3335 *lon = GMT->current.proj.o_pole_lon + d_atan2d (cos_tlat * sin_tlon, GMT->current.proj.o_sin_pole_lat * cc + GMT->current.proj.o_cos_pole_lat * sin_tlat);
3336 }
3337 #endif
3338
3339
3340 /*! . */
gmtmap_get_origin(struct GMT_CTRL * GMT,double lon1,double lat1,double lon_p,double lat_p,double * lon2,double * lat2)3341 GMT_LOCAL void gmtmap_get_origin (struct GMT_CTRL *GMT, double lon1, double lat1, double lon_p, double lat_p, double *lon2, double *lat2) {
3342 double beta = 0.0, dummy = 0.0, d, az, c;
3343
3344 /* Now find origin that is 90 degrees from pole, let oblique lon=0 go through lon1/lat1 */
3345 c = cosd (lat_p) * cosd (lat1) * cosd (lon1-lon_p);
3346 c = d_acosd (sind (lat_p) * sind (lat1) + c);
3347
3348 if (c != 90.0) { /* Not true origin */
3349 d = fabs (90.0 - c);
3350 az = d_asind (sind (lon_p-lon1) * cosd (lat_p) / sind (c));
3351 if (c < 90.0) az += 180.0;
3352 *lat2 = d_asind (sind (lat1) * cosd (d) + cosd (lat1) * sind (d) * cosd (az));
3353 *lon2 = lon1 + d_atan2d (sind (d) * sind (az), cosd (lat1) * cosd (d) - sind (lat1) * sind (d) * cosd (az));
3354 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Correct projection origin = %g/%g\n", *lon2, *lat2);
3355 }
3356 else {
3357 *lon2 = lon1;
3358 *lat2 = lat1;
3359 }
3360
3361 gmtmap_pole_rotate_forward (GMT, *lon2, *lat2, &beta, &dummy);
3362
3363 GMT->current.proj.o_beta = -beta;
3364 }
3365
3366 /*! . */
gmtmap_get_rotate_pole(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)3367 GMT_LOCAL void gmtmap_get_rotate_pole (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
3368 double plon, plat, beta, dummy;
3369 double A[3], B[3], P[3];
3370 /* Given A = (lon1, lat1) and B = (lon2, lat2), get P = A x B */
3371 gmt_geo_to_cart (GMT, lat1, lon1, A, true); /* Cartesian vector for origin */
3372 gmt_geo_to_cart (GMT, lat2, lon2, B, true); /* Cartesian vector for 2nd point along oblique Equator */
3373 gmt_cross3v (GMT, A, B, P); /* Get pole position of plane through A and B (and center of Earth O) */
3374 gmt_normalize3v (GMT, P); /* Make sure P has unit length */
3375 gmt_cart_to_geo (GMT, &plat, &plon, P, true); /* Recover lon,lat of pole */
3376
3377 if (plat < 0.0 && GMT->hidden.func_level == 0 && !strncmp (GMT->init.module_name, "mapproject", 10U)) /* Only allowed in mapproject at top level */
3378 GMT->current.proj.o_spole = true;
3379 if (GMT->current.proj.N_hemi && plat < 0.0) { /* Insist on a Northern hemisphere pole */
3380 plat = -plat;
3381 plon += 180.0;
3382 if (plon >= 360.0) plon -= 360.0;
3383 }
3384 GMT->current.proj.o_pole_lon = plon;
3385 GMT->current.proj.o_pole_lat = plat;
3386 sincosd (plat, &GMT->current.proj.o_sin_pole_lat, &GMT->current.proj.o_cos_pole_lat);
3387 gmtmap_pole_rotate_forward (GMT, lon1, lat1, &beta, &dummy);
3388 GMT->current.proj.o_beta = -beta;
3389 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Oblique Mercator pole is %.12g %.12g, with beta = %.12g\n", plon, plat, -beta);
3390 }
3391
3392 /*! . */
gmtmap_init_oblique(struct GMT_CTRL * GMT,bool * search)3393 GMT_LOCAL int gmtmap_init_oblique (struct GMT_CTRL *GMT, bool *search) {
3394 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0;
3395 double o_x = 0.0, o_y = 0.0, p_x = 0.0, p_y = 0.0, az, b_x = 0.0, b_y = 0.0, w, e, s, n;
3396
3397 *search = true;
3398 gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
3399
3400 if (strncmp (GMT->init.module_name, "mapproject", 10U))
3401 GMT->current.proj.o_spole = false; /* Only used in mapproject */
3402
3403 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG; /* To get plot-units / m */
3404
3405 o_x = GMT->current.proj.pars[0]; o_y = GMT->current.proj.pars[1];
3406
3407 if (lrint (GMT->current.proj.pars[6]) == 1) { /* Must get correct origin, then get second point */
3408 double distance = 10.0; /* Default spherical length for gmt_translate_point */
3409 p_x = GMT->current.proj.pars[2]; p_y = GMT->current.proj.pars[3];
3410
3411 GMT->current.proj.o_pole_lon = p_x;
3412 GMT->current.proj.o_pole_lat = p_y;
3413 GMT->current.proj.o_sin_pole_lat = sind (p_y);
3414 GMT->current.proj.o_cos_pole_lat = cosd (p_y);
3415
3416 /* Find azimuth to pole, add 90, and compute second point 10 degrees away */
3417
3418 gmtmap_get_origin (GMT, o_x, o_y, p_x, p_y, &o_x, &o_y);
3419 az = atand (cosd (p_y) * sind (p_x - o_x) / (cosd (o_y) * sind (p_y) - sind (o_y) * cosd (p_y) * cosd (p_x - o_x))) + 90.0;
3420 if ((90.0 - fabs (o_y)) < distance)
3421 distance = 90.0 - fabs (o_y) - GMT_CONV4_LIMIT;
3422 /* compute point <distance> degrees from origin along azimuth */
3423 gmt_translate_point (GMT, o_x, o_y, az, distance, &b_x, &b_y, NULL);
3424
3425 GMT->current.proj.pars[0] = o_x; GMT->current.proj.pars[1] = o_y;
3426 GMT->current.proj.pars[2] = b_x; GMT->current.proj.pars[3] = b_y;
3427 }
3428 else { /* Just find pole */
3429 b_x = GMT->current.proj.pars[2]; b_y = GMT->current.proj.pars[3];
3430 gmtmap_get_rotate_pole (GMT, o_x, o_y, b_x, b_y);
3431 }
3432
3433 /* Here we have pole and origin */
3434
3435 gmtlib_set_oblique_pole_and_origin (GMT, GMT->current.proj.o_pole_lon, GMT->current.proj.o_pole_lat, o_x, o_y);
3436 gmtproj_vmerc (GMT, 0.0, 0.0);
3437 /* Internally, the origin is at the intersection between the meridian through the pole and the selected
3438 * origin and the oblique equator, but we need to shift that up to the selected oblique latitude through
3439 * the user's origin. */
3440 gmtproj_oblmrc (GMT, GMT->current.proj.lon0, GMT->current.proj.lat0, &dummy, &GMT->current.proj.o_shift);
3441 if (GMT->current.proj.o_spole) GMT->current.proj.o_shift = -GMT->current.proj.o_shift;
3442
3443 if (GMT->common.R.oblique) { /* wesn is lower left and upper right corners in normal lon/lats */
3444 gmtproj_oblmrc (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3445 gmtproj_oblmrc (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3446 }
3447 else { /* Gave oblique degrees */
3448 if (doubleAlmostEqual (GMT->common.R.wesn[YLO], -90.0) || doubleAlmostEqual (GMT->common.R.wesn[YHI], 90.0)) {
3449 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -R: Cannot include south/north oblique poles with Oblique Mercator projection!\n");
3450 return GMT_PROJECTION_ERROR;
3451 }
3452 /* Convert oblique wesn in degrees to meters using regular Mercator */
3453 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) {
3454 GMT->common.R.wesn[XLO] = -180.0;
3455 GMT->common.R.wesn[XHI] = +180.0;
3456 }
3457 gmtproj_merc_sph (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3458 gmtproj_merc_sph (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3459 GMT->common.R.oblique = true; /* Since wesn was oblique, not geographical wesn */
3460 }
3461
3462 gmtproj_imerc_sph (GMT, &w, &s, xmin, ymin); /* Get oblique wesn boundaries */
3463 gmtproj_imerc_sph (GMT, &e, &n, xmax, ymax);
3464 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
3465 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
3466 GMT->current.proj.fwd = &gmtproj_oblmrc;
3467 GMT->current.proj.inv = &gmtproj_ioblmrc;
3468 GMT->current.map.outside = &gmtmap_rect_outside;
3469 GMT->current.map.crossing = &gmtmap_rect_crossing;
3470 GMT->current.map.overlap = &gmtmap_rect_overlap;
3471 GMT->current.map.clip = &gmtmap_rect_clip;
3472 GMT->current.map.left_edge = &gmtmap_left_rect;
3473 GMT->current.map.right_edge = &gmtmap_right_rect;
3474
3475 GMT->current.map.is_world = gmt_M_360_range (w, e);
3476 #if 0
3477 if (GMT->current.map.is_world == false) { /* Check if one of the poles are inside the area */
3478 if (!gmt_map_outside (GMT, 0.0, -90.0)) /* South pole is inside map */
3479 GMT->current.map.is_world = true;
3480 else if (!gmt_map_outside (GMT, 0.0, +90.0)) /* North pole is inside map */
3481 GMT->current.map.is_world = true;
3482 }
3483 #endif
3484 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3485 GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3486 return (GMT_NOERROR);
3487 }
3488
3489 /*
3490 * TRANSFORMATION ROUTINES FOR THE TRANSVERSE MERCATOR PROJECTION (GMT_TM)
3491 */
3492
3493 /* For global TM maps */
3494
3495 /*! . */
gmtmap_wrap_around_check_tm(struct GMT_CTRL * GMT,double * angle,double last_x,double last_y,double this_x,double this_y,double * xx,double * yy,unsigned int * sides)3496 GMT_LOCAL unsigned int gmtmap_wrap_around_check_tm (struct GMT_CTRL *GMT, double *angle, double last_x, double last_y, double this_x, double this_y, double *xx, double *yy, unsigned int *sides) {
3497 double dx, dy, width, jump;
3498
3499 jump = this_y - last_y;
3500 width = 0.5 * GMT->current.map.height;
3501
3502 if (fabs (jump) - width <= GMT_CONV4_LIMIT || fabs (jump) <= GMT_CONV4_LIMIT) return (0);
3503 dx = this_x - last_x;
3504
3505 if (jump < -width) { /* Crossed top boundary */
3506 dy = GMT->current.map.height + jump;
3507 xx[0] = xx[1] = last_x + (GMT->current.map.height - last_y) * dx / dy;
3508 if (xx[0] < 0.0 || xx[0] > GMT->current.proj.rect[XHI]) return (0);
3509 yy[0] = GMT->current.map.height; yy[1] = 0.0;
3510 sides[0] = 2;
3511 angle[0] = d_atan2d (dy, dx);
3512 }
3513 else { /* Crossed bottom boundary */
3514 dy = GMT->current.map.height - jump;
3515 xx[0] = xx[1] = last_x + last_y * dx / dy;
3516 if (xx[0] < 0.0 || xx[0] > GMT->current.proj.rect[XHI]) return (0);
3517 yy[0] = 0.0; yy[1] = GMT->current.map.height;
3518 sides[0] = 0;
3519 angle[0] = d_atan2d (dy, -dx);
3520 }
3521 angle[1] = angle[0] + 180.0;
3522 sides[1] = 2 - sides[0];
3523
3524 return (2);
3525 }
3526
3527 /*! . */
gmtmap_this_point_wraps_tm(struct GMT_CTRL * GMT,double y0,double y1)3528 GMT_LOCAL bool gmtmap_this_point_wraps_tm (struct GMT_CTRL *GMT, double y0, double y1) {
3529 /* Returns true if the 2 y-points implies a jump at this x-level of the TM map */
3530
3531 double dy;
3532
3533 return ((dy = fabs (y1 - y0)) > GMT->current.map.half_height);
3534 }
3535
3536 /*! . */
gmtmap_will_it_wrap_tm(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t * start)3537 GMT_LOCAL bool gmtmap_will_it_wrap_tm (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t *start) {
3538 /* Determines if a polygon will wrap at edges for TM global projection */
3539 uint64_t i;
3540 bool wrap;
3541 gmt_M_unused(x);
3542
3543 if (!GMT->current.map.is_world) return (false);
3544
3545 for (i = 1, wrap = false; !wrap && i < n; i++) {
3546 wrap = gmtmap_this_point_wraps_tm (GMT, y[i-1], y[i]);
3547 }
3548 *start = i - 1;
3549 return (wrap);
3550 }
3551
3552 /*! . */
gmtmap_get_crossings_tm(struct GMT_CTRL * GMT,double * xc,double * yc,double x0,double y0,double x1,double y1)3553 GMT_LOCAL void gmtmap_get_crossings_tm (struct GMT_CTRL *GMT, double *xc, double *yc, double x0, double y0, double x1, double y1) {
3554 /* Finds crossings for wrap-arounds for global TM maps */
3555 double xa, xb, ya, yb, dy, c;
3556
3557 xa = x0; xb = x1;
3558 ya = y0; yb = y1;
3559 if (ya > yb) { /* Make A the minimum y point */
3560 gmt_M_double_swap (xa, xb);
3561 gmt_M_double_swap (ya, yb);
3562 }
3563
3564 yb -= GMT->current.map.height;
3565
3566 dy = ya - yb;
3567 c = (doubleAlmostEqualZero (ya, yb)) ? 0.0 : (xa - xb) / dy;
3568 xc[0] = xc[1] = xb - yb * c;
3569 if (y0 > y1) { /* First cut top */
3570 yc[0] = GMT->current.map.height;
3571 yc[1] = 0.0;
3572 }
3573 else {
3574 yc[0] = 0.0;
3575 yc[1] = GMT->current.map.height;
3576 }
3577 }
3578
3579 /*! . */
gmtmap_jump_tm(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)3580 GMT_LOCAL int gmtmap_jump_tm (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
3581 /* true if y-distance between points exceeds 1/2 map height at this x value */
3582 /* Only used for TM world maps */
3583
3584 double dy;
3585 gmt_M_unused(x0); gmt_M_unused(x1);
3586
3587 dy = y1 - y0;
3588 if (dy > GMT->current.map.half_height) return (-1); /* Cross bottom/south boundary */
3589 if (dy < (-GMT->current.map.half_height)) return (1); /* Cross top/north boundary */
3590 return (0);
3591 }
3592
3593 /*! . */
gmtmap_init_tm(struct GMT_CTRL * GMT,bool * search)3594 GMT_LOCAL int gmtmap_init_tm (struct GMT_CTRL *GMT, bool *search) {
3595 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
3596
3597 *search = GMT->common.R.oblique;
3598
3599 /* Wrap and truncations are in y, not x for TM */
3600
3601 GMT->current.map.wrap_around_check = &gmtmap_wrap_around_check_tm;
3602 GMT->current.map.jump = &gmtmap_jump_tm;
3603 GMT->current.map.will_it_wrap = &gmtmap_will_it_wrap_tm;
3604 #if 0
3605 GMT->current.map.this_point_wraps = &gmtmap_this_point_wraps_tm;
3606 #endif
3607 GMT->current.map.get_crossings = &gmtmap_get_crossings_tm;
3608
3609 if (GMT->current.setting.proj_scale_factor == -1.0) GMT->current.setting.proj_scale_factor = 1.0; /* Select default map scale for TM */
3610 GMT->current.proj.GMT_convert_latitudes = gmtmap_quicktm (GMT, GMT->current.proj.pars[0], 10.0);
3611 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3612 gmtproj_vtm (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
3613 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
3614 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
3615 if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) { /* Spherical code w/wo conformal latitudes */
3616 GMT->current.proj.fwd = &gmtproj_tm_sph;
3617 GMT->current.proj.inv = &gmtproj_itm_sph;
3618 }
3619 else {
3620 GMT->current.proj.fwd = &gmtproj_tm;
3621 GMT->current.proj.inv = &gmtproj_itm;
3622 }
3623
3624 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
3625 if (GMT->current.map.is_world) { /* Gave oblique degrees */
3626 double w, e, dummy = 0.0;
3627 w = GMT->current.proj.central_meridian + GMT->common.R.wesn[YLO];
3628 e = GMT->current.proj.central_meridian + GMT->common.R.wesn[YHI];
3629 GMT->common.R.wesn[YLO] = -90;
3630 GMT->common.R.wesn[YHI] = 90;
3631 GMT->common.R.wesn[XHI] = e;
3632 GMT->common.R.wesn[XLO] = w;
3633 gmtproj_vtm (GMT, GMT->current.proj.pars[0], 0.0);
3634 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], 0.0, &xmin, &dummy);
3635 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], 0.0, &xmax, &dummy);
3636 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &dummy, &ymin);
3637 ymax = ymin + (TWO_PI * GMT->current.proj.EQ_RAD * GMT->current.setting.proj_scale_factor);
3638 gmtproj_vtm (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
3639 GMT->current.map.outside = &gmtmap_rect_outside;
3640 GMT->current.map.crossing = &gmtmap_rect_crossing;
3641 GMT->current.map.overlap = &gmtmap_rect_overlap;
3642 GMT->current.map.clip = &gmtmap_rect_clip;
3643 GMT->current.map.left_edge = &gmtmap_left_rect;
3644 GMT->current.map.right_edge = &gmtmap_right_rect;
3645 GMT->current.map.frame.check_side = true;
3646 GMT->current.map.is_world_tm = true;
3647 GMT->common.R.oblique = true; /* Since wesn was oblique, not geographical wesn */
3648 GMT->common.R.wesn[XHI] = GMT->current.proj.central_meridian + 180.0;
3649 GMT->common.R.wesn[XLO] = GMT->current.proj.central_meridian - 180.0;
3650 }
3651 else if (!GMT->common.R.oblique) {
3652 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3653 GMT->current.map.outside = &gmtmap_wesn_outside;
3654 GMT->current.map.crossing = &gmtmap_wesn_crossing;
3655 GMT->current.map.overlap = &gmtmap_wesn_overlap;
3656 GMT->current.map.clip = &map_wesn_clip;
3657 GMT->current.map.left_edge = &gmtmap_left_rect;
3658 GMT->current.map.right_edge = &gmtmap_right_rect;
3659 GMT->current.map.is_world_tm = doubleAlmostEqualZero (GMT->common.R.wesn[YHI], GMT->common.R.wesn[YLO]);
3660 GMT->current.map.is_world = false;
3661 }
3662 else { /* Find min values */
3663 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3664 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3665 GMT->current.map.outside = &gmtmap_rect_outside;
3666 GMT->current.map.crossing = &gmtmap_rect_crossing;
3667 GMT->current.map.overlap = &gmtmap_rect_overlap;
3668 GMT->current.map.clip = &gmtmap_rect_clip;
3669 GMT->current.map.left_edge = &gmtmap_left_rect;
3670 GMT->current.map.right_edge = &gmtmap_right_rect;
3671 GMT->current.map.frame.check_side = true;
3672 GMT->current.map.is_world_tm = false;
3673 GMT->current.map.is_world = (fabs (GMT->common.R.wesn[YLO] - GMT->common.R.wesn[YHI]) < GMT_CONV4_LIMIT);
3674 }
3675
3676 GMT->current.map.frame.horizontal = 1;
3677 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
3678
3679 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3680
3681 return (GMT_NOERROR);
3682 }
3683
3684 /*
3685 * TRANSFORMATION ROUTINES FOR THE UNIVERSAL TRANSVERSE MERCATOR PROJECTION (GMT_UTM)
3686 */
3687
3688 /*! . */
gmtmap_set_utmzone(struct GMT_CTRL * GMT)3689 GMT_LOCAL void gmtmap_set_utmzone (struct GMT_CTRL *GMT) {
3690 /* When no UTM zone is given we determine the best one from midpoint of -R.
3691 * We pass back via GMT->current.proj.pars[0] the zone number.
3692 * Note that despite the funky non-standard zones around Norway
3693 * the central meridian still follows the simple 6-degree stepping.
3694 */
3695 int kx, ky;
3696 double clon, clat;
3697 char zone[4] = {""};
3698
3699 clon = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
3700 if (clon > 180.0) clon -= 360.0;
3701 else if (clon < -180.0) clon += 360.0;
3702 kx = irint (floor ((clon + 180.0) / 6.0)) + 1; /* Best UTM zone */
3703 GMT->current.proj.lon0 = -180.0 + kx * 6.0 - 3.0; /* Best centered longitude */
3704 if (GMT->common.R.wesn[YLO] >= 0.0) /* By doing >= we are implicitly saying that when no info we default to northern hemisphere */
3705 GMT->current.proj.utm_hemisphere = +1;
3706 else
3707 GMT->current.proj.utm_hemisphere = -1;
3708 GMT->current.proj.utm_zoney = 0;
3709 clat = 0.5 * (GMT->common.R.wesn[YLO] + GMT->common.R.wesn[YHI]);
3710 if (clat < -80.0) { /* A or B */
3711 zone[0] = (GMT->current.proj.lon0 < 0.0) ? 'A' : 'B';
3712 }
3713 else if (clat > 84.0) { /* Y or Z */
3714 zone[0] = (GMT->current.proj.lon0 < 0.0) ? 'Y' : 'Z';
3715 }
3716 else { /* In the general latitude range of 1-60 zones */
3717 ky = irint (floor ((clat + 80.0) / 8.0));
3718 if (ky == 20) ky = 19; /* Since band X is 12 degrees tall there is no 20 */
3719 if (ky >= 11) ky += 4; /* zone = (P-X) */
3720 else if (ky >= 6) ky += 3; /* zone = (J-N) */
3721 else ky += 2; /* zone = (C-H) */
3722 GMT->current.proj.utm_zoney = 'A' + (char)ky;
3723 if (GMT->current.proj.utm_zoney == 'X') { /* Deal with funky zone X */
3724 if (clon >= 0.0 && clon < 9.0)
3725 kx = 31;
3726 else if (clon >= 9.0 && clon < 21.0)
3727 kx = 33;
3728 else if (clon >= 21.0 && clon < 33.0)
3729 kx = 35;
3730 else if (clon >= 33.0 && clon < 42.0)
3731 kx = 37;
3732 }
3733 else if (GMT->current.proj.utm_zoney == 'V') { /* Deal with funky zone V */
3734 if (clon >= 3.0 && clon < 12.0)
3735 kx = 32;
3736 }
3737 snprintf (zone, 4, "%d%c", kx, GMT->current.proj.utm_zoney);
3738 }
3739 GMT->current.proj.pars[0] = (double)kx;
3740 GMT->current.proj.lat0 = 0.0;
3741 GMT_Report (GMT->parent, GMT_MSG_WARNING, "No UTM zone given; zone %s selected\n", zone);
3742 }
3743
3744 /*! . */
gmtmap_init_utm(struct GMT_CTRL * GMT,bool * search)3745 GMT_LOCAL int gmtmap_init_utm (struct GMT_CTRL *GMT, bool *search) {
3746 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, lon0;
3747
3748 *search = GMT->common.R.oblique;
3749
3750 if (GMT->current.setting.proj_scale_factor == -1.0) GMT->current.setting.proj_scale_factor = 0.9996; /* Select default map scale for UTM */
3751 if (GMT->current.proj.pars[0] < 0.0) gmtmap_set_utmzone (GMT); /* Determine UTM zone from -R */
3752 lon0 = 180.0 + 6.0 * GMT->current.proj.pars[0] - 3.0; /* Central meridian for this UTM zone */
3753 if (lon0 >= 360.0) lon0 -= 360.0;
3754 GMT->current.proj.GMT_convert_latitudes = gmtmap_quicktm (GMT, lon0, 10.0);
3755 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3756 gmtproj_vtm (GMT, lon0, 0.0); /* Central meridian for this zone */
3757 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
3758 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
3759 switch (GMT->current.proj.utm_hemisphere) { /* Set hemisphere */
3760 case -1:
3761 GMT->current.proj.north_pole = false;
3762 break;
3763 case +1:
3764 GMT->current.proj.north_pole = true;
3765 break;
3766 default:
3767 GMT->current.proj.north_pole = (GMT->common.R.wesn[YLO] >= 0.0);
3768 break;
3769 }
3770 if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) { /* Spherical code w/wo conformal latitudes */
3771 GMT->current.proj.fwd = &gmtproj_utm_sph;
3772 GMT->current.proj.inv = &gmtproj_iutm_sph;
3773 }
3774 else {
3775 GMT->current.proj.fwd = &gmtproj_utm;
3776 GMT->current.proj.inv = &gmtproj_iutm;
3777 }
3778
3779 if (fabs (GMT->common.R.wesn[XLO] - GMT->common.R.wesn[XHI]) > 360.0) { /* -R in UTM meters */
3780 (*GMT->current.proj.inv) (GMT, &GMT->common.R.wesn[XLO], &GMT->common.R.wesn[YLO], GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO]);
3781 (*GMT->current.proj.inv) (GMT, &GMT->common.R.wesn[XHI], &GMT->common.R.wesn[YHI], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI]);
3782 GMT->common.R.oblique = true;
3783 }
3784 if (GMT->common.R.oblique) {
3785 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3786 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3787 GMT->current.map.outside = &gmtmap_rect_outside;
3788 GMT->current.map.crossing = &gmtmap_rect_crossing;
3789 GMT->current.map.overlap = &gmtmap_rect_overlap;
3790 GMT->current.map.clip = &gmtmap_rect_clip;
3791 GMT->current.map.left_edge = &gmtmap_left_rect;
3792 GMT->current.map.right_edge = &gmtmap_right_rect;
3793 GMT->current.map.frame.check_side = true;
3794 }
3795 else {
3796 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3797 GMT->current.map.outside = &gmtmap_wesn_outside;
3798 GMT->current.map.crossing = &gmtmap_wesn_crossing;
3799 GMT->current.map.overlap = &gmtmap_wesn_overlap;
3800 GMT->current.map.clip = &map_wesn_clip;
3801 GMT->current.map.left_edge = &gmtmap_left_rect;
3802 GMT->current.map.right_edge = &gmtmap_right_rect;
3803 }
3804
3805 GMT->current.map.frame.horizontal = 1;
3806
3807 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
3808
3809 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3810
3811 return (GMT_NOERROR);
3812 }
3813
3814 #if 0
3815 GMT_LOCAL double gmtmap_UTMzone_to_clon (struct GMT_CTRL *GMT, unsigned int zone_x, char zone_y) {
3816 /* Return the central longitude of this UTM zone */
3817 double clon = 180.0 + 6.0 * zone_x - 3.0; /* This is valid for most zones */
3818
3819 if (zone_y == 0) return (clon); /* Latitude zone is not specified so we are done */
3820 if ((zone_y < 'A' || zone_y > 'Z') || zone_y < 'I' || zone_y < 'O') return (GMT->session.d_NaN); /* Bad latitude zone so return NaN*/
3821 if (zone_y <= 'B') return (-90.0 + 180.0 * (zone_y - 'A')); /* Either -90 or +90 */
3822 if (zone_y == 'V' && zone_x == 31) return (1.5); /* Center of 31V */
3823 if (zone_y == 'V' && zone_x == 32) return (7.5); /* Center of 32V */
3824 if (zone_y == 'X') {
3825 if (zone_x == 31) return (4.5); /* Center of 31X */
3826 if (zone_x == 33) return (15.0); /* Center of 33X */
3827 if (zone_x == 35) return (27.0); /* Center of 35X */
3828 if (zone_x == 37) return (37.5); /* Center of 37X */
3829 if (zone_x == 32 || zone_x == 34 || zone_x == 36) return (GMT->session.d_NaN); /* Bad latitude zone so return NaN*/
3830 return (clon);
3831 }
3832 /* Only Y or Z left */
3833 return (-90.0 + 180.0 * (zone_y - 'Y')); /* Either -90 or +90 */
3834 }
3835 #endif
3836
3837 /*
3838 * TRANSFORMATION ROUTINES FOR THE LAMBERT AZIMUTHAL EQUAL-AREA PROJECTION (GMT_LAMB_AZ_EQ)
3839 */
3840
3841 /*! . */
gmtmap_init_lambeq(struct GMT_CTRL * GMT,bool * search)3842 GMT_LOCAL int gmtmap_init_lambeq (struct GMT_CTRL *GMT, bool *search) {
3843 unsigned int i;
3844 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
3845
3846 *search = GMT->common.R.oblique;
3847 GMT->current.proj.Dx = GMT->current.proj.Dy = 1.0;
3848
3849 gmtmap_set_polar (GMT);
3850 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
3851 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
3852 gmtproj_vlambeq (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3853
3854 if (GMT->current.proj.GMT_convert_latitudes) {
3855 double D, s, c;
3856 sincosd (GMT->current.proj.pars[1], &s, &c);
3857 D = (GMT->current.proj.polar) ? 1.0 : (GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius / GMT->current.proj.lat_swap_vals.ra) * c / (GMT->current.proj.cosp * d_sqrt (1.0 - GMT->current.proj.ECC2 * s * s));
3858 GMT->current.proj.Dx = D;
3859 GMT->current.proj.Dy = 1.0 / D;
3860 }
3861 GMT->current.proj.iDx = 1.0 / GMT->current.proj.Dx;
3862 GMT->current.proj.iDy = 1.0 / GMT->current.proj.Dy;
3863
3864 GMT->current.proj.fwd = &gmtproj_lambeq;
3865 GMT->current.proj.inv = &gmtproj_ilambeq;
3866 if (GMT->current.proj.units_pr_degree) {
3867 gmtproj_vlambeq (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
3868 gmtproj_lambeq (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
3869 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3870 gmtproj_vlambeq (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3871 }
3872 else
3873 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3874
3875 if (GMT->common.R.oblique) { /* Rectangular box given */
3876 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3877 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3878
3879 GMT->current.map.outside = &gmtmap_rect_outside2;
3880 GMT->current.map.crossing = &gmtmap_rect_crossing;
3881 GMT->current.map.overlap = &gmtmap_rect_overlap;
3882 GMT->current.map.clip = &gmtmap_rect_clip;
3883 GMT->current.map.left_edge = &gmtmap_left_rect;
3884 GMT->current.map.right_edge = &gmtmap_right_rect;
3885 GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3886 GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
3887 }
3888 else {
3889 if (GMT->current.proj.polar) { /* Polar aspect */
3890 if (GMT->current.proj.north_pole) {
3891 double hor_lat = 90.0 - GMT->current.proj.f_horizon;
3892 if (GMT->common.R.wesn[YLO] < hor_lat) {
3893 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "South boundary cannot be beyond the horizon for north polar Lambert azimuthal projection (reset to horizon at %lg)\n", hor_lat);
3894 GMT->common.R.wesn[YLO] = hor_lat;
3895 }
3896 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
3897 }
3898 else {
3899 double hor_lat = GMT->current.proj.f_horizon - 90.0;
3900 if (GMT->common.R.wesn[YHI] > hor_lat) {
3901 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "North boundary cannot be beyond the horizon for south polar Lambert azimuthal projection (reset to horizon at %lg)\n", hor_lat);
3902 GMT->common.R.wesn[YHI] = hor_lat;
3903 }
3904 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
3905 }
3906 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
3907 || doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
3908 GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
3909 GMT->current.map.outside = &gmtmap_polar_outside;
3910 GMT->current.map.crossing = &gmtmap_wesn_crossing;
3911 GMT->current.map.overlap = &gmtmap_wesn_overlap;
3912 GMT->current.map.clip = &map_wesn_clip;
3913 GMT->current.map.frame.horizontal = 1;
3914 GMT->current.map.n_lat_nodes = 2;
3915 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
3916 }
3917 else { /* Global view only */
3918 /* No annotations or tickmarks in global mode */
3919 for (i = 0; i < GMT_GRID_UPPER; i++) {
3920 GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
3921 GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
3922 }
3923 GMT->common.R.wesn[XLO] = 0.0;
3924 GMT->common.R.wesn[XHI] = 360.0;
3925 GMT->common.R.wesn[YLO] = -90.0;
3926 GMT->common.R.wesn[YHI] = 90.0;
3927 xmax = ymax = GMT->current.proj.rho_max;
3928 xmin = ymin = -xmax;
3929 GMT->current.map.outside = &gmtmap_radial_outside;
3930 GMT->current.map.crossing = &gmtmap_radial_crossing;
3931 GMT->current.map.overlap = &gmtmap_radial_overlap;
3932 GMT->current.map.clip = &gmtmap_radial_clip;
3933 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
3934 }
3935 GMT->current.map.left_edge = &gmtmap_left_circle;
3936 GMT->current.map.right_edge = &gmtmap_right_circle;
3937 }
3938
3939 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
3940 GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
3941 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
3942 if (GMT->current.proj.polar) GMT->current.map.meridian_straight = 1;
3943
3944 return (GMT_NOERROR);
3945 }
3946
3947 /*
3948 * TRANSFORMATION ROUTINES FOR THE ORTHOGRAPHIC PROJECTION (GMT_ORTHO)
3949 */
3950
3951 /*! . */
gmtmap_init_ortho(struct GMT_CTRL * GMT,bool * search)3952 GMT_LOCAL int gmtmap_init_ortho (struct GMT_CTRL *GMT, bool *search) {
3953 unsigned int i;
3954 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
3955
3956 *search =GMT->common.R.oblique;
3957
3958 gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
3959
3960 gmtmap_set_polar (GMT);
3961
3962 if (GMT->current.proj.units_pr_degree) {
3963 gmtproj_vortho (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
3964 gmtproj_ortho (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
3965 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
3966 }
3967 else
3968 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
3969
3970 gmtproj_vortho (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
3971 GMT->current.proj.fwd = &gmtproj_ortho;
3972 GMT->current.proj.inv = &gmtproj_iortho;
3973
3974 if (GMT->common.R.oblique) { /* Rectangular box given */
3975 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
3976 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
3977
3978 GMT->current.map.outside = &gmtmap_rect_outside2;
3979 GMT->current.map.crossing = &gmtmap_rect_crossing;
3980 GMT->current.map.overlap = &gmtmap_rect_overlap;
3981 GMT->current.map.clip = &gmtmap_rect_clip;
3982 GMT->current.map.left_edge = &gmtmap_left_rect;
3983 GMT->current.map.right_edge = &gmtmap_right_rect;
3984 GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
3985 GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
3986 }
3987 else {
3988 if (GMT->current.proj.polar) { /* Polar aspect */
3989 if (GMT->current.proj.north_pole) {
3990 double hor_lat = 90.0 - GMT->current.proj.f_horizon;
3991 if (GMT->common.R.wesn[YLO] < hor_lat) {
3992 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "South boundary cannot be beyond the horizon for north polar orthographic projection (reset to horizon at %lg)\n", hor_lat);
3993 GMT->common.R.wesn[YLO] = hor_lat;
3994 }
3995 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
3996 }
3997 else {
3998 double hor_lat = GMT->current.proj.f_horizon - 90.0;
3999 if (GMT->common.R.wesn[YHI] > hor_lat) {
4000 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "North boundary cannot be beyond the horizon for south polar orthographic projection (reset to horizon at %lg)\n", hor_lat);
4001 GMT->common.R.wesn[YHI] = hor_lat;
4002 }
4003 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4004 }
4005 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
4006 || doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4007 GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4008 GMT->current.map.outside = &gmtmap_polar_outside;
4009 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4010 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4011 GMT->current.map.clip = &map_wesn_clip;
4012 GMT->current.map.frame.horizontal = 1;
4013 GMT->current.map.n_lat_nodes = 2;
4014 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4015 }
4016 else { /* Global view only */
4017 /* No annotations or tickmarks in global mode */
4018 for (i = 0; i < GMT_GRID_UPPER; i++) {
4019 GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
4020 GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
4021 }
4022 GMT->common.R.wesn[XLO] = 0.0;
4023 GMT->common.R.wesn[XHI] = 360.0;
4024 GMT->common.R.wesn[YLO] = -90.0;
4025 GMT->common.R.wesn[YHI] = 90.0;
4026 xmax = ymax = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4027 xmin = ymin = -xmax;
4028 GMT->current.map.outside = &gmtmap_radial_outside;
4029 GMT->current.map.crossing = &gmtmap_radial_crossing;
4030 GMT->current.map.overlap = &gmtmap_radial_overlap;
4031 GMT->current.map.clip = &gmtmap_radial_clip;
4032 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4033 }
4034 GMT->current.map.left_edge = &gmtmap_left_circle;
4035 GMT->current.map.right_edge = &gmtmap_right_circle;
4036 }
4037
4038 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
4039 GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4040 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4041 if (GMT->current.proj.polar) GMT->current.map.meridian_straight = 1;
4042
4043 return (GMT_NOERROR);
4044 }
4045
4046 /*! . */
gmtmap_left_genper(struct GMT_CTRL * GMT,double y)4047 GMT_LOCAL double gmtmap_left_genper (struct GMT_CTRL *GMT, double y) {
4048 /* Windowed genper may need to consider the inner of circle and rectangle */
4049 return (MAX (0.0, gmtmap_left_circle (GMT, y)));
4050 }
4051
4052 /*! . */
gmtmap_right_genper(struct GMT_CTRL * GMT,double y)4053 GMT_LOCAL double gmtmap_right_genper (struct GMT_CTRL *GMT, double y) {
4054 return (MIN (GMT->current.map.width, gmtmap_right_circle (GMT, y)));
4055 }
4056
4057 /*! . */
gmtmap_init_genper(struct GMT_CTRL * GMT,bool * search)4058 GMT_LOCAL int gmtmap_init_genper (struct GMT_CTRL *GMT, bool *search) {
4059 unsigned int i;
4060 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
4061 double alt, azimuth, tilt, width, height;
4062 double twist, scale, units;
4063
4064 units = GMT->current.proj.pars[2];
4065 scale = GMT->current.proj.pars[3];
4066 alt = GMT->current.proj.pars[4];
4067 azimuth = GMT->current.proj.pars[5];
4068 tilt = GMT->current.proj.pars[6];
4069 twist = GMT->current.proj.pars[7];
4070 width = GMT->current.proj.pars[8];
4071 height = GMT->current.proj.pars[9];
4072
4073 if (GMT->current.proj.g_sphere) gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
4074
4075 gmtmap_set_polar (GMT);
4076
4077 if (GMT->current.proj.units_pr_degree) {
4078 gmtproj_vgenper (GMT, 0.0, 90.0, alt, azimuth, tilt, twist, width, height);
4079 gmtproj_genper (GMT, 0.0, fabs (GMT->current.proj.pars[3]), &dummy, &radius);
4080 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[2] / radius);
4081 }
4082 else
4083 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
4084
4085 if (GMT->current.proj.g_debug > 1) {
4086 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: units_pr_degree %d\n", GMT->current.proj.units_pr_degree);
4087 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: radius %f\n", radius);
4088 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: scale %f units %f\n", scale, units);
4089 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: x scale %f y scale %f\n", GMT->current.proj.scale[GMT_X], GMT->current.proj.scale[GMT_Y]);
4090 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gave_map_width %d \n",GMT->current.proj.gave_map_width);
4091 }
4092
4093 gmtproj_vgenper (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], alt, azimuth, tilt, twist, width, height);
4094 GMT->current.proj.fwd = &gmtproj_genper;
4095 GMT->current.proj.inv = &gmtproj_igenper;
4096
4097 GMT->common.R.wesn[XLO] = 0.0;
4098 GMT->common.R.wesn[XHI] = 360.0;
4099 GMT->common.R.wesn[YLO] = -90.0;
4100 GMT->common.R.wesn[YHI] = 90.0;
4101
4102 xmin = GMT->current.proj.g_xmin;
4103 xmax = GMT->current.proj.g_xmax;
4104 ymin = GMT->current.proj.g_ymin;
4105 ymax = GMT->current.proj.g_ymax;
4106
4107 if (GMT->current.proj.g_width != 0.0) {
4108 GMT->common.R.oblique = false;
4109 GMT->current.proj.windowed = true;
4110 if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "using windowed region\n");
4111 GMT->current.map.outside = &gmtmap_rect_outside2;
4112 GMT->current.map.crossing = &gmtmap_rect_crossing;
4113 GMT->current.map.overlap = &gmtmap_rect_overlap;
4114 #if 0
4115 GMT->current.map.crossing = &gmtmap_genper_crossing;
4116 GMT->current.map.overlap = &gmtmap_genperw_overlap;
4117 GMT->current.map.left_edge = &gmtmap_left_rect;
4118 GMT->current.map.right_edge = &gmtmap_right_rect;
4119 #endif
4120 GMT->current.map.clip = &gmtmap_rect_clip_old;
4121 GMT->current.map.left_edge = &gmtmap_left_genper;
4122 GMT->current.map.right_edge = &gmtmap_right_genper;
4123 GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
4124 GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
4125 GMT->current.map.jump = &gmtmap_jump_not;
4126 *search = true;
4127 }
4128 else {
4129 if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "using global view\n");
4130 /* No annotations or tickmarks in global mode */
4131 for (i = 0; i < GMT_GRID_UPPER; i++) {
4132 GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
4133 GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
4134 }
4135 GMT->current.map.overlap = &gmtmap_genperg_overlap;
4136 GMT->current.map.crossing = &gmtmap_radial_crossing;
4137 GMT->current.map.clip = &gmtmap_radial_clip;
4138 GMT->current.map.outside = &gmtmap_radial_outside;
4139 GMT->current.map.left_edge = &gmtmap_left_circle;
4140 GMT->current.map.right_edge = &gmtmap_right_circle;
4141
4142 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4143
4144 *search = false;
4145 }
4146
4147 if (GMT->current.proj.polar) {
4148 if (GMT->current.proj.north_pole) {
4149 if (GMT->common.R.wesn[YLO] < (90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YLO] = 90.0 - GMT->current.proj.f_horizon;
4150 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4151 } else {
4152 if (GMT->common.R.wesn[YHI] > -(90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YHI] = -(90.0 - GMT->current.proj.f_horizon);
4153 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4154 }
4155 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
4156 || doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4157 GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4158 }
4159
4160 if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "xmin %f xmax %f ymin %f ymax %f\n", xmin/1000, xmax/1000, ymin/1000, ymax/1000);
4161
4162 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
4163
4164 GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4165
4166 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4167
4168 if (GMT->current.proj.g_debug > 0) {
4169 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x scale %e y scale %e\n", GMT->current.proj.scale[GMT_X], GMT->current.proj.scale[GMT_Y]);
4170 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x center %f y center %f\n", GMT->current.proj.c_x0, GMT->current.proj.c_y0);
4171 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x max %f y max %f\n", GMT->current.proj.rect[XHI], GMT->current.proj.rect[YHI]);
4172 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "x0 %f y0 %f\n\n", GMT->current.proj.origin[GMT_X], GMT->current.proj.origin[GMT_Y]);
4173 fflush(NULL);
4174 }
4175
4176 return (GMT_NOERROR);
4177 }
4178
4179 /*
4180 * TRANSFORMATION ROUTINES FOR THE GNOMONIC PROJECTION (GMT_GNOMONIC)
4181 */
4182
4183 /*! . */
gmtmap_init_gnomonic(struct GMT_CTRL * GMT,bool * search)4184 GMT_LOCAL int gmtmap_init_gnomonic (struct GMT_CTRL *GMT, bool *search) {
4185 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
4186
4187 *search = GMT->common.R.oblique;
4188
4189 gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */ gmtmap_set_polar (GMT);
4190
4191 if (GMT->current.proj.units_pr_degree) {
4192 gmtproj_vgnomonic (GMT, 0.0, 90.0, 60.0);
4193 gmtproj_gnomonic (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
4194 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
4195 }
4196 else
4197 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
4198
4199 gmtproj_vgnomonic (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
4200 GMT->current.proj.fwd = &gmtproj_gnomonic;
4201 GMT->current.proj.inv = &gmtproj_ignomonic;
4202
4203 if (GMT->common.R.oblique) { /* Rectangular box given */
4204 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4205 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4206
4207 GMT->current.map.outside = &gmtmap_rect_outside2;
4208 GMT->current.map.crossing = &gmtmap_rect_crossing;
4209 GMT->current.map.overlap = &gmtmap_rect_overlap;
4210 GMT->current.map.clip = &gmtmap_rect_clip;
4211 GMT->current.map.left_edge = &gmtmap_left_rect;
4212 GMT->current.map.right_edge = &gmtmap_right_rect;
4213 GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
4214 GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 30.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
4215 }
4216 else {
4217 if (GMT->current.proj.polar) { /* Polar aspect */
4218 if (GMT->current.proj.north_pole) {
4219 if (GMT->common.R.wesn[YLO] < (90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YLO] = 90.0 - GMT->current.proj.f_horizon;
4220 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4221 }
4222 else {
4223 if (GMT->common.R.wesn[YHI] > -(90.0 - GMT->current.proj.f_horizon)) GMT->common.R.wesn[YHI] = -(90.0 - GMT->current.proj.f_horizon);
4224 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4225 }
4226 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])
4227 || doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4228 GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4229 GMT->current.map.outside = &gmtmap_polar_outside;
4230 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4231 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4232 GMT->current.map.clip = &map_wesn_clip;
4233 GMT->current.map.frame.horizontal = 1;
4234 GMT->current.map.n_lat_nodes = 2;
4235 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4236 }
4237 else { /* Global view only */
4238 GMT->common.R.wesn[XLO] = 0.0;
4239 GMT->common.R.wesn[XHI] = 360.0;
4240 GMT->common.R.wesn[YLO] = -90.0;
4241 GMT->common.R.wesn[YHI] = 90.0;
4242 xmax = ymax = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4243 xmin = ymin = -xmax;
4244 GMT->current.map.outside = &gmtmap_radial_outside;
4245 GMT->current.map.crossing = &gmtmap_radial_crossing;
4246 GMT->current.map.overlap = &gmtmap_radial_overlap;
4247 GMT->current.map.clip = &gmtmap_radial_clip;
4248 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4249 }
4250 GMT->current.map.left_edge = &gmtmap_left_circle;
4251 GMT->current.map.right_edge = &gmtmap_right_circle;
4252 }
4253
4254 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
4255 GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4256 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4257
4258 return (GMT_NOERROR);
4259 }
4260
4261 /*
4262 * TRANSFORMATION ROUTINES FOR THE AZIMUTHAL EQUIDISTANT PROJECTION (GMT_AZ_EQDIST)
4263 */
4264
4265 /*! . */
gmtmap_init_azeqdist(struct GMT_CTRL * GMT,bool * search)4266 GMT_LOCAL int gmtmap_init_azeqdist (struct GMT_CTRL *GMT, bool *search) {
4267 unsigned int i;
4268 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dummy = 0.0, radius = 0.0;
4269
4270 *search = GMT->common.R.oblique;
4271
4272 gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
4273
4274 gmtmap_set_polar (GMT);
4275
4276 if (GMT->current.proj.units_pr_degree) {
4277 gmtproj_vazeqdist (GMT, 0.0, 90.0, GMT->current.proj.pars[2]);
4278 gmtproj_azeqdist (GMT, 0.0, fabs (GMT->current.proj.pars[4]), &dummy, &radius);
4279 if (gmt_M_is_zero (radius)) radius = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4280 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = fabs (GMT->current.proj.pars[3] / radius);
4281 }
4282 else
4283 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[3];
4284
4285 gmtproj_vazeqdist (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2]);
4286 GMT->current.proj.fwd = &gmtproj_azeqdist;
4287 GMT->current.proj.inv = &gmtproj_iazeqdist;
4288
4289 if (GMT->common.R.oblique) { /* Rectangular box given */
4290 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4291 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4292
4293 GMT->current.map.outside = &gmtmap_rect_outside2;
4294 GMT->current.map.crossing = &gmtmap_rect_crossing;
4295 GMT->current.map.overlap = &gmtmap_rect_overlap;
4296 GMT->current.map.clip = &gmtmap_rect_clip;
4297 GMT->current.map.left_edge = &gmtmap_left_rect;
4298 GMT->current.map.right_edge = &gmtmap_right_rect;
4299 GMT->current.map.frame.check_side = !(GMT->current.setting.map_annot_oblique & GMT_OBL_ANNOT_ANYWHERE);
4300 GMT->current.map.frame.horizontal = (fabs (GMT->current.proj.pars[1]) < 60.0 && fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 30.0) ? 1 : 0;
4301 }
4302 else {
4303 if (GMT->current.proj.polar && (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) < 180.0) { /* Polar aspect */
4304 if (!GMT->current.proj.north_pole && GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4305 if (GMT->current.proj.north_pole && GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4306 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4307 if (GMT->current.map.is_world || doubleAlmostEqualZero (GMT->common.R.wesn[XHI], GMT->common.R.wesn[XLO]))
4308 GMT->current.proj.edge[1] = GMT->current.proj.edge[3] = false;
4309 GMT->current.map.outside = &gmtmap_polar_outside;
4310 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4311 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4312 GMT->current.map.clip = &map_wesn_clip;
4313 GMT->current.map.frame.horizontal = 1;
4314 GMT->current.map.n_lat_nodes = 2;
4315 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4316 }
4317 else { /* Global view only, force wesn = 0/360/-90/90 */
4318 /* No annotations or tickmarks in global mode */
4319 GMT->current.map.is_world = true;
4320 for (i = 0; i < GMT_GRID_UPPER; i++) {
4321 GMT->current.map.frame.axis[GMT_X].item[i].active = GMT->current.map.frame.axis[GMT_Y].item[i].active = false,
4322 GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval = 0.0;
4323 }
4324 GMT->common.R.wesn[XLO] = 0.0;
4325 GMT->common.R.wesn[XHI] = 360.0;
4326 GMT->common.R.wesn[YLO] = -90.0;
4327 GMT->common.R.wesn[YHI] = 90.0;
4328 xmax = ymax = GMT->current.proj.rho_max * GMT->current.proj.EQ_RAD;
4329 xmin = ymin = -xmax;
4330 GMT->current.map.outside = &gmtmap_radial_outside;
4331 GMT->current.map.crossing = &gmtmap_radial_crossing;
4332 GMT->current.map.overlap = &gmtmap_radial_overlap;
4333 GMT->current.map.clip = &gmtmap_radial_clip;
4334 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4335 }
4336 GMT->current.map.left_edge = &gmtmap_left_circle;
4337 GMT->current.map.right_edge = &gmtmap_right_circle;
4338 }
4339
4340 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[3]);
4341 GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4342 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4343 if (GMT->current.proj.polar) GMT->current.map.meridian_straight = 1;
4344
4345 return (GMT_NOERROR);
4346 }
4347
4348 /*
4349 * TRANSFORMATION ROUTINES FOR THE MOLLWEIDE EQUAL AREA PROJECTION (GMT_MOLLWEIDE)
4350 */
4351
4352 /*! . */
gmtmap_init_mollweide(struct GMT_CTRL * GMT,bool * search)4353 GMT_LOCAL int gmtmap_init_mollweide (struct GMT_CTRL *GMT, bool *search) {
4354 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, y, dummy;
4355
4356 *search = GMT->common.R.oblique;
4357
4358 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4359 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4360
4361 if (gmtmap_central_meridian_not_set (GMT))
4362 gmtmap_set_default_central_meridian (GMT);
4363 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4364 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4365 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4366 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = M_PI * GMT->current.proj.pars[1] / sqrt (8.0);
4367 gmtproj_vmollweide (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4368 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4369 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4370
4371 if (GMT->common.R.oblique) {
4372 gmtproj_mollweide (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4373 gmtproj_mollweide (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4374 GMT->current.map.outside = &gmtmap_rect_outside;
4375 GMT->current.map.crossing = &gmtmap_rect_crossing;
4376 GMT->current.map.overlap = &gmtmap_rect_overlap;
4377 GMT->current.map.clip = &gmtmap_rect_clip;
4378 GMT->current.map.left_edge = &gmtmap_left_rect;
4379 GMT->current.map.right_edge = &gmtmap_right_rect;
4380 GMT->current.map.frame.check_side = true;
4381 }
4382 else {
4383 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4384 gmtproj_mollweide (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4385 gmtproj_mollweide (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4386 gmtproj_mollweide (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4387 gmtproj_mollweide (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4388 GMT->current.map.outside = &gmtmap_wesn_outside;
4389 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4390 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4391 GMT->current.map.clip = &map_wesn_clip;
4392 GMT->current.map.left_edge = &gmtmap_left_ellipse;
4393 GMT->current.map.right_edge = &gmtmap_right_ellipse;
4394 GMT->current.map.frame.horizontal = 2;
4395 GMT->current.proj.polar = true;
4396 }
4397 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4398 GMT->current.proj.fwd = &gmtproj_mollweide;
4399 GMT->current.proj.inv = &gmtproj_imollweide;
4400 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4401 GMT->current.map.parallel_straight = 1;
4402
4403 return (GMT_NOERROR);
4404 }
4405
4406
4407 /*
4408 * TRANSFORMATION ROUTINES FOR THE HAMMER-AITOFF EQUAL AREA PROJECTION (GMT_HAMMER)
4409 */
4410
4411 /*! . */
gmtmap_init_hammer(struct GMT_CTRL * GMT,bool * search)4412 GMT_LOCAL int gmtmap_init_hammer (struct GMT_CTRL *GMT, bool *search) {
4413 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4414
4415 *search = GMT->common.R.oblique;
4416
4417 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4418 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4419
4420 if (gmtmap_central_meridian_not_set (GMT))
4421 gmtmap_set_default_central_meridian (GMT);
4422 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4423 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4424 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4425 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = 0.5 * M_PI * GMT->current.proj.pars[1] / M_SQRT2;
4426 gmtproj_vhammer (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4427 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4428 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4429
4430 if (GMT->common.R.oblique) {
4431 gmtproj_hammer (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4432 gmtproj_hammer (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4433 GMT->current.map.outside = &gmtmap_rect_outside;
4434 GMT->current.map.crossing = &gmtmap_rect_crossing;
4435 GMT->current.map.overlap = &gmtmap_rect_overlap;
4436 GMT->current.map.clip = &gmtmap_rect_clip;
4437 GMT->current.map.left_edge = &gmtmap_left_rect;
4438 GMT->current.map.right_edge = &gmtmap_right_rect;
4439 GMT->current.map.frame.check_side = true;
4440 }
4441 else {
4442 double x, y, dummy = 0.0;
4443 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4444 x = (fabs (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) > fabs (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian)) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
4445 gmtproj_hammer (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4446 gmtproj_hammer (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4447 gmtproj_hammer (GMT, x, GMT->common.R.wesn[YLO], &dummy, &ymin);
4448 gmtproj_hammer (GMT, x, GMT->common.R.wesn[YHI], &dummy, &ymax);
4449 GMT->current.map.outside = &gmtmap_wesn_outside;
4450 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4451 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4452 GMT->current.map.clip = &map_wesn_clip;
4453 GMT->current.map.left_edge = &gmtmap_left_ellipse;
4454 GMT->current.map.right_edge = &gmtmap_right_ellipse;
4455 GMT->current.map.frame.horizontal = 2;
4456 GMT->current.proj.polar = true;
4457 }
4458 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4459 GMT->current.proj.fwd = &gmtproj_hammer;
4460 GMT->current.proj.inv = &gmtproj_ihammer;
4461 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4462 return (GMT_NOERROR);
4463 }
4464
4465 /*
4466 * TRANSFORMATION ROUTINES FOR THE VAN DER GRINTEN PROJECTION (GMT_VANGRINTEN)
4467 */
4468
4469 /*! . */
gmtmap_init_grinten(struct GMT_CTRL * GMT,bool * search)4470 GMT_LOCAL int gmtmap_init_grinten (struct GMT_CTRL *GMT, bool *search) {
4471 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4472
4473 *search = GMT->common.R.oblique;
4474
4475 gmtmap_set_spherical (GMT, true);
4476
4477 if (gmtmap_central_meridian_not_set (GMT))
4478 gmtmap_set_default_central_meridian (GMT);
4479 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4480 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4481 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4482 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
4483 gmtproj_vgrinten (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4484 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4485 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4486
4487 if (GMT->common.R.oblique) {
4488 gmtproj_grinten (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4489 gmtproj_grinten (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4490 GMT->current.map.outside = &gmtmap_rect_outside;
4491 GMT->current.map.crossing = &gmtmap_rect_crossing;
4492 GMT->current.map.overlap = &gmtmap_rect_overlap;
4493 GMT->current.map.clip = &gmtmap_rect_clip;
4494 GMT->current.map.left_edge = &gmtmap_left_rect;
4495 GMT->current.map.right_edge = &gmtmap_right_rect;
4496 GMT->current.map.frame.check_side = true;
4497 }
4498 else {
4499 double x, y, dummy = 0.0;
4500 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4501 x = (fabs (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) > fabs (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian)) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
4502 gmtproj_grinten (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4503 gmtproj_grinten (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4504 gmtproj_grinten (GMT, x, GMT->common.R.wesn[YLO], &dummy, &ymin);
4505 gmtproj_grinten (GMT, x, GMT->common.R.wesn[YHI], &dummy, &ymax);
4506 GMT->current.map.outside = &gmtmap_wesn_outside;
4507 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4508 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4509 GMT->current.map.clip = &map_wesn_clip;
4510 GMT->current.map.left_edge = &gmtmap_left_circle;
4511 GMT->current.map.right_edge = &gmtmap_right_circle;
4512 GMT->current.map.frame.horizontal = 2;
4513 GMT->current.proj.polar = true;
4514 }
4515 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4516 GMT->current.proj.r = 0.5 * GMT->current.proj.rect[XHI];
4517 GMT->current.proj.fwd = &gmtproj_grinten;
4518 GMT->current.proj.inv = &gmtproj_igrinten;
4519 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4520 return (GMT_NOERROR);
4521 }
4522
4523 /*
4524 * TRANSFORMATION ROUTINES FOR THE WINKEL-TRIPEL MODIFIED AZIMUTHAL PROJECTION (GMT_WINKEL)
4525 */
4526
4527 /*! . */
gmtmap_init_winkel(struct GMT_CTRL * GMT,bool * search)4528 GMT_LOCAL int gmtmap_init_winkel (struct GMT_CTRL *GMT, bool *search) {
4529 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4530
4531 *search = GMT->common.R.oblique;
4532
4533 gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
4534
4535 if (gmtmap_central_meridian_not_set (GMT))
4536 gmtmap_set_default_central_meridian (GMT);
4537 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4538 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4539 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4540 gmtproj_vwinkel (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4541 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = 2.0 * GMT->current.proj.pars[1] / (1.0 + GMT->current.proj.r_cosphi1);
4542
4543 if (GMT->common.R.oblique) {
4544 gmtproj_winkel (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4545 gmtproj_winkel (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4546 GMT->current.map.outside = &gmtmap_rect_outside;
4547 GMT->current.map.crossing = &gmtmap_rect_crossing;
4548 GMT->current.map.overlap = &gmtmap_rect_overlap;
4549 GMT->current.map.clip = &gmtmap_rect_clip;
4550 GMT->current.map.left_edge = &gmtmap_left_rect;
4551 GMT->current.map.right_edge = &gmtmap_right_rect;
4552 GMT->current.map.frame.check_side = true;
4553 }
4554 else {
4555 double x, y, dummy = 0.0;
4556 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4557 x = (fabs (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) > fabs (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian)) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
4558 gmtproj_winkel (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4559 gmtproj_winkel (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4560 gmtproj_winkel (GMT, x, GMT->common.R.wesn[YLO], &dummy, &ymin);
4561 gmtproj_winkel (GMT, x, GMT->common.R.wesn[YHI], &dummy, &ymax);
4562 GMT->current.map.outside = &gmtmap_wesn_outside;
4563 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4564 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4565 GMT->current.map.clip = &map_wesn_clip;
4566 GMT->current.map.left_edge = &gmtproj_left_winkel;
4567 GMT->current.map.right_edge = &gmtproj_right_winkel;
4568 GMT->current.map.frame.horizontal = 2;
4569 /* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4570 if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4571 }
4572 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4573 GMT->current.proj.fwd = &gmtproj_winkel;
4574 GMT->current.proj.inv = &gmtproj_iwinkel;
4575 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4576 return (GMT_NOERROR);
4577 }
4578
4579 /*
4580 * TRANSFORMATION ROUTINES FOR THE ECKERT IV PROJECTION (GMT_ECKERT4)
4581 */
4582
4583 /*! . */
gmtmap_init_eckert4(struct GMT_CTRL * GMT,bool * search)4584 GMT_LOCAL int gmtmap_init_eckert4 (struct GMT_CTRL *GMT, bool *search) {
4585 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4586
4587 *search = GMT->common.R.oblique;
4588
4589 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4590 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4591
4592 if (gmtmap_central_meridian_not_set (GMT))
4593 gmtmap_set_default_central_meridian (GMT);
4594 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4595 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4596 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4597 gmtproj_veckert4 (GMT, GMT->current.proj.pars[0]);
4598 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
4599
4600 if (GMT->common.R.oblique) {
4601 gmtproj_eckert4 (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4602 gmtproj_eckert4 (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4603 GMT->current.map.outside = &gmtmap_rect_outside;
4604 GMT->current.map.crossing = &gmtmap_rect_crossing;
4605 GMT->current.map.overlap = &gmtmap_rect_overlap;
4606 GMT->current.map.clip = &gmtmap_rect_clip;
4607 GMT->current.map.left_edge = &gmtmap_left_rect;
4608 GMT->current.map.right_edge = &gmtmap_right_rect;
4609 GMT->current.map.frame.check_side = true;
4610 }
4611 else {
4612 double y, dummy = 0.0;
4613 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4614 gmtproj_eckert4 (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4615 gmtproj_eckert4 (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4616 gmtproj_eckert4 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4617 gmtproj_eckert4 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4618 GMT->current.map.outside = &gmtmap_wesn_outside;
4619 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4620 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4621 GMT->current.map.clip = &map_wesn_clip;
4622 GMT->current.map.left_edge = &gmtproj_left_eckert4;
4623 GMT->current.map.right_edge = &gmtproj_right_eckert4;
4624 GMT->current.map.frame.horizontal = 2;
4625 /* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4626 if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4627 }
4628 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4629 GMT->current.proj.fwd = &gmtproj_eckert4;
4630 GMT->current.proj.inv = &gmtproj_ieckert4;
4631 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4632 GMT->current.map.parallel_straight = 1;
4633
4634 return (GMT_NOERROR);
4635 }
4636
4637 /*
4638 * TRANSFORMATION ROUTINES FOR THE ECKERT VI PROJECTION (GMT_ECKERT6)
4639 */
4640
4641 /*! . */
gmtmap_init_eckert6(struct GMT_CTRL * GMT,bool * search)4642 GMT_LOCAL int gmtmap_init_eckert6 (struct GMT_CTRL *GMT, bool *search) {
4643 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4644
4645 *search = GMT->common.R.oblique;
4646
4647 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4648 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4649
4650 if (gmtmap_central_meridian_not_set (GMT))
4651 gmtmap_set_default_central_meridian (GMT);
4652 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4653 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4654 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4655 gmtproj_veckert6 (GMT, GMT->current.proj.pars[0]);
4656 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = 0.5 * GMT->current.proj.pars[1] * sqrt (2.0 + M_PI);
4657
4658 if (GMT->common.R.oblique) {
4659 gmtproj_eckert6 (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4660 gmtproj_eckert6 (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4661 GMT->current.map.outside = &gmtmap_rect_outside;
4662 GMT->current.map.crossing = &gmtmap_rect_crossing;
4663 GMT->current.map.overlap = &gmtmap_rect_overlap;
4664 GMT->current.map.clip = &gmtmap_rect_clip;
4665 GMT->current.map.left_edge = &gmtmap_left_rect;
4666 GMT->current.map.right_edge = &gmtmap_right_rect;
4667 GMT->current.map.frame.check_side = true;
4668 }
4669 else {
4670 double y, dummy = 0.0;
4671 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4672 gmtproj_eckert6 (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4673 gmtproj_eckert6 (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4674 gmtproj_eckert6 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4675 gmtproj_eckert6 (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4676 GMT->current.map.outside = &gmtmap_wesn_outside;
4677 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4678 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4679 GMT->current.map.clip = &map_wesn_clip;
4680 GMT->current.map.left_edge = &gmtproj_left_eckert6;
4681 GMT->current.map.right_edge = &gmtproj_right_eckert6;
4682 GMT->current.map.frame.horizontal = 2;
4683 /* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4684 if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4685 }
4686 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4687 GMT->current.proj.fwd = &gmtproj_eckert6;
4688 GMT->current.proj.inv = &gmtproj_ieckert6;
4689 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4690 GMT->current.map.parallel_straight = 1;
4691
4692 return (GMT_NOERROR);
4693 }
4694
4695 /*
4696 * TRANSFORMATION ROUTINES FOR THE ROBINSON PSEUDOCYLINDRICAL PROJECTION (GMT_ROBINSON)
4697 */
4698
4699 /*! . */
gmtmap_init_robinson(struct GMT_CTRL * GMT,bool * search)4700 GMT_LOCAL int gmtmap_init_robinson (struct GMT_CTRL *GMT, bool *search) {
4701 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4702
4703 *search = GMT->common.R.oblique;
4704
4705 gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
4706
4707 if (gmtmap_central_meridian_not_set (GMT))
4708 gmtmap_set_default_central_meridian (GMT);
4709 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4710 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4711 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4712 gmtproj_vrobinson (GMT, GMT->current.proj.pars[0]);
4713 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1] / 0.8487;
4714
4715 if (GMT->common.R.oblique) {
4716 gmtproj_robinson (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4717 gmtproj_robinson (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4718 GMT->current.map.outside = &gmtmap_rect_outside;
4719 GMT->current.map.crossing = &gmtmap_rect_crossing;
4720 GMT->current.map.overlap = &gmtmap_rect_overlap;
4721 GMT->current.map.clip = &gmtmap_rect_clip;
4722 GMT->current.map.left_edge = &gmtmap_left_rect;
4723 GMT->current.map.right_edge = &gmtmap_right_rect;
4724 GMT->current.map.frame.check_side = true;
4725 }
4726 else {
4727 double y, dummy = 0.0;
4728 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4729 gmtproj_robinson (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4730 gmtproj_robinson (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4731 gmtproj_robinson (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4732 gmtproj_robinson (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4733 GMT->current.map.outside = &gmtmap_wesn_outside;
4734 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4735 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4736 GMT->current.map.clip = &map_wesn_clip;
4737 GMT->current.map.left_edge = &gmtproj_left_robinson;
4738 GMT->current.map.right_edge = &gmtproj_right_robinson;
4739 GMT->current.map.frame.horizontal = 2;
4740 /* Unless MAP_ANNOT_OBLIQUE is set manually, we must add normal ticks for this pole-is-line projection */
4741 if (!GMT->current.setting.map_annot_oblique_set) GMT->current.setting.map_annot_oblique |= GMT_OBL_ANNOT_NORMAL_TICKS;
4742 }
4743 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4744 GMT->current.proj.fwd = &gmtproj_robinson;
4745 GMT->current.proj.inv = &gmtproj_irobinson;
4746 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4747 GMT->current.map.parallel_straight = 1;
4748
4749 return (GMT_NOERROR);
4750 }
4751
4752 /*
4753 * TRANSFORMATION ROUTINES FOR THE SINUSOIDAL EQUAL AREA PROJECTION (GMT_SINUSOIDAL)
4754 */
4755
4756 /*! . */
gmtmap_init_sinusoidal(struct GMT_CTRL * GMT,bool * search)4757 GMT_LOCAL int gmtmap_init_sinusoidal (struct GMT_CTRL *GMT, bool *search) {
4758 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4759
4760 *search = GMT->common.R.oblique;
4761
4762 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4763 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4764
4765 if (gmtmap_central_meridian_not_set (GMT))
4766 gmtmap_set_default_central_meridian (GMT);
4767 if (GMT->current.proj.pars[0] < 0.0) GMT->current.proj.pars[0] += 360.0;
4768 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
4769 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
4770 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
4771 gmtproj_vsinusoidal (GMT, GMT->current.proj.pars[0]);
4772 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[1] /= GMT->current.proj.M_PR_DEG;
4773 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[1];
4774 GMT->current.proj.fwd = &gmtproj_sinusoidal;
4775 GMT->current.proj.inv = &gmtproj_isinusoidal;
4776 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4777
4778 if (GMT->common.R.oblique) {
4779 gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4780 gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4781 GMT->current.map.outside = &gmtmap_rect_outside;
4782 GMT->current.map.crossing = &gmtmap_rect_crossing;
4783 GMT->current.map.overlap = &gmtmap_rect_overlap;
4784 GMT->current.map.clip = &gmtmap_rect_clip;
4785 GMT->current.map.left_edge = &gmtmap_left_rect;
4786 GMT->current.map.right_edge = &gmtmap_right_rect;
4787 GMT->current.map.frame.check_side = true;
4788 }
4789 else {
4790 double dummy = 0.0, y;
4791 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
4792 gmtproj_sinusoidal (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
4793 gmtproj_sinusoidal (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
4794 gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
4795 gmtproj_sinusoidal (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
4796 GMT->current.map.outside = &gmtmap_wesn_outside;
4797 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4798 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4799 GMT->current.map.clip = &map_wesn_clip;
4800 GMT->current.map.left_edge = &gmtproj_left_sinusoidal;
4801 GMT->current.map.right_edge = &gmtproj_right_sinusoidal;
4802 GMT->current.map.frame.horizontal = 2;
4803 GMT->current.proj.polar = true;
4804 }
4805
4806 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[1]);
4807 GMT->current.map.parallel_straight = 1;
4808
4809 return (GMT_NOERROR);
4810 }
4811
4812 /*
4813 * TRANSFORMATION ROUTINES FOR THE CASSINI PROJECTION (GMT_CASSINI)
4814 */
4815
4816 /*! . */
gmtmap_init_cassini(struct GMT_CTRL * GMT,bool * search)4817 GMT_LOCAL int gmtmap_init_cassini (struct GMT_CTRL *GMT, bool *search) {
4818 bool too_big;
4819 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, L = 0.0, R = 0.0;
4820
4821 *search = GMT->common.R.oblique;
4822
4823 if (gmtmap_central_meridian_not_set (GMT))
4824 gmtmap_set_default_central_meridian (GMT);
4825 if ((L = (GMT->current.proj.pars[0] - GMT->common.R.wesn[XLO])) > 90.0 || (R = (GMT->common.R.wesn[XHI] - GMT->current.proj.pars[0])) > 90.0) {
4826 double D = 0.0;
4827 if (L > 0.0) D = 360.0; else if (R > 0.0) D = -360.0; /* Try the relevant 360-degree shift */
4828 GMT->common.R.wesn[XLO] += D; GMT->common.R.wesn[XHI] += D; /* Try to shift the range */
4829 if ((GMT->current.proj.pars[0] - GMT->common.R.wesn[XLO]) > 90.0 || (GMT->common.R.wesn[XHI] - GMT->current.proj.pars[0]) > 90.0) {
4830 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Max longitude extension away from central meridian is limited to +/- 90 degrees\n");
4831 return GMT_PROJECTION_ERROR;
4832 }
4833 }
4834 too_big = gmtmap_quicktm (GMT, GMT->current.proj.pars[0], 4.0);
4835 if (too_big) gmtmap_set_spherical (GMT, true); /* Cannot use ellipsoidal series for this area */
4836 gmtproj_vcassini (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
4837 if (gmt_M_is_spherical (GMT)) {
4838 GMT->current.proj.fwd = &gmtproj_cassini_sph;
4839 GMT->current.proj.inv = &gmtproj_icassini_sph;
4840 }
4841 else {
4842 GMT->current.proj.fwd = &gmtproj_cassini;
4843 GMT->current.proj.inv = &gmtproj_icassini;
4844 }
4845 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
4846 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
4847 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
4848
4849 if (GMT->common.R.oblique) {
4850 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4851 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4852 GMT->current.map.outside = &gmtmap_rect_outside;
4853 GMT->current.map.crossing = &gmtmap_rect_crossing;
4854 GMT->current.map.overlap = &gmtmap_rect_overlap;
4855 GMT->current.map.clip = &gmtmap_rect_clip;
4856 GMT->current.map.left_edge = &gmtmap_left_rect;
4857 GMT->current.map.right_edge = &gmtmap_right_rect;
4858 GMT->current.map.frame.check_side = true;
4859 }
4860 else {
4861 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4862 GMT->current.map.outside = &gmtmap_wesn_outside;
4863 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4864 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4865 GMT->current.map.clip = &map_wesn_clip;
4866 GMT->current.map.left_edge = &gmtmap_left_conic;
4867 GMT->current.map.right_edge = &gmtmap_right_conic;
4868 }
4869
4870 GMT->current.map.frame.horizontal = 1;
4871 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
4872
4873 return (GMT_NOERROR);
4874 }
4875
4876 /*
4877 * TRANSFORMATION ROUTINES FOR THE ALBERS PROJECTION (GMT_ALBERS)
4878 */
4879
4880 /*! . */
gmtmap_init_albers(struct GMT_CTRL * GMT,bool * search)4881 GMT_LOCAL int gmtmap_init_albers (struct GMT_CTRL *GMT, bool *search) {
4882 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dy, az, x1, y1;
4883
4884 *search = GMT->common.R.oblique;
4885
4886 GMT->current.proj.GMT_convert_latitudes = gmtmap_quickconic (GMT);
4887 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4888 if (gmt_M_is_spherical (GMT) || GMT->current.proj.GMT_convert_latitudes) { /* Spherical code w/wo authalic latitudes */
4889 gmtproj_valbers_sph (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
4890 GMT->current.proj.fwd = &gmtproj_albers_sph;
4891 GMT->current.proj.inv = &gmtproj_ialbers_sph;
4892 }
4893 else {
4894 gmtproj_valbers (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
4895 GMT->current.proj.fwd = &gmtproj_albers;
4896 GMT->current.proj.inv = &gmtproj_ialbers;
4897 }
4898 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG;
4899 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
4900
4901 if (GMT->common.R.oblique) {
4902 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4903 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4904 GMT->current.map.outside = &gmtmap_rect_outside;
4905 GMT->current.map.crossing = &gmtmap_rect_crossing;
4906 GMT->current.map.overlap = &gmtmap_rect_overlap;
4907 GMT->current.map.clip = &gmtmap_rect_clip;
4908 GMT->current.map.left_edge = &gmtmap_left_rect;
4909 GMT->current.map.right_edge = &gmtmap_right_rect;
4910 GMT->current.map.frame.check_side = true;
4911 }
4912 else {
4913 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4914 GMT->current.map.outside = &gmtmap_wesn_outside;
4915 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4916 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4917 GMT->current.map.clip = &map_wesn_clip;
4918 GMT->current.map.left_edge = &gmtmap_left_conic;
4919 GMT->current.map.right_edge = &gmtmap_right_conic;
4920 }
4921 GMT->current.map.frame.horizontal = 1;
4922 GMT->current.map.n_lat_nodes = 2;
4923 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
4924
4925 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4926 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian + 90., GMT->current.proj.pole, &x1, &y1);
4927 dy = y1 - GMT->current.proj.c_y0;
4928 az = 2.0 * d_atan2 (dy, x1 - GMT->current.proj.c_x0);
4929 dy /= (1.0 - cos (az));
4930 GMT->current.proj.c_y0 += dy;
4931 GMT->current.map.meridian_straight = 1;
4932
4933 return (GMT_NOERROR);
4934 }
4935
4936
4937 /*
4938 * TRANSFORMATION ROUTINES FOR THE EQUIDISTANT CONIC PROJECTION (GMT_ECONIC)
4939 */
4940
4941
4942 /*! . */
gmtmap_init_econic(struct GMT_CTRL * GMT,bool * search)4943 GMT_LOCAL int gmtmap_init_econic (struct GMT_CTRL *GMT, bool *search) {
4944 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0, dy, az, x1, y1;
4945
4946 *search = GMT->common.R.oblique;
4947
4948 GMT->current.proj.GMT_convert_latitudes = !gmt_M_is_spherical (GMT);
4949 if (GMT->current.proj.GMT_convert_latitudes) gmtlib_scale_eqrad (GMT);
4950 gmtproj_veconic (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1], GMT->current.proj.pars[2], GMT->current.proj.pars[3]);
4951 GMT->current.proj.fwd = &gmtproj_econic;
4952 GMT->current.proj.inv = &gmtproj_ieconic;
4953 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[4] /= GMT->current.proj.M_PR_DEG;
4954 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[4];
4955
4956 if (GMT->common.R.oblique) {
4957 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
4958 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
4959 GMT->current.map.outside = &gmtmap_rect_outside;
4960 GMT->current.map.crossing = &gmtmap_rect_crossing;
4961 GMT->current.map.overlap = &gmtmap_rect_overlap;
4962 GMT->current.map.clip = &gmtmap_rect_clip;
4963 GMT->current.map.left_edge = &gmtmap_left_rect;
4964 GMT->current.map.right_edge = &gmtmap_right_rect;
4965 }
4966 else {
4967 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
4968 GMT->current.map.outside = &gmtmap_wesn_outside;
4969 GMT->current.map.crossing = &gmtmap_wesn_crossing;
4970 GMT->current.map.overlap = &gmtmap_wesn_overlap;
4971 GMT->current.map.clip = &map_wesn_clip;
4972 GMT->current.map.left_edge = &gmtmap_left_conic;
4973 GMT->current.map.right_edge = &gmtmap_right_conic;
4974 }
4975 GMT->current.map.frame.horizontal = 1;
4976 GMT->current.map.n_lat_nodes = 2;
4977 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[4]);
4978
4979 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, &GMT->current.proj.c_x0, &GMT->current.proj.c_y0);
4980 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian + 90., GMT->current.proj.pole, &x1, &y1);
4981 dy = y1 - GMT->current.proj.c_y0;
4982 az = 2.0 * d_atan2 (dy, x1 - GMT->current.proj.c_x0);
4983 dy /= (1.0 - cos (az));
4984 GMT->current.proj.c_y0 += dy;
4985 GMT->current.map.meridian_straight = 1;
4986
4987 return (GMT_NOERROR);
4988 }
4989
4990 /*
4991 * TRANSFORMATION ROUTINES FOR THE POLYCONIC PROJECTION (GMT_POLYCONIC)
4992 */
4993
4994 /*! . */
gmtmap_init_polyconic(struct GMT_CTRL * GMT,bool * search)4995 GMT_LOCAL int gmtmap_init_polyconic (struct GMT_CTRL *GMT, bool *search) {
4996 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
4997
4998 *search = GMT->common.R.oblique;
4999
5000 gmtmap_set_spherical (GMT, true); /* PW: Force spherical for now */
5001
5002 if (gmtmap_central_meridian_not_set (GMT))
5003 gmtmap_set_default_central_meridian (GMT);
5004 GMT->current.map.is_world = gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]);
5005 if (GMT->common.R.wesn[YLO] <= -90.0) GMT->current.proj.edge[0] = false;
5006 if (GMT->common.R.wesn[YHI] >= 90.0) GMT->current.proj.edge[2] = false;
5007 gmtproj_vpolyconic (GMT, GMT->current.proj.pars[0], GMT->current.proj.pars[1]);
5008 if (GMT->current.proj.units_pr_degree) GMT->current.proj.pars[2] /= GMT->current.proj.M_PR_DEG;
5009 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.pars[2];
5010 GMT->current.proj.fwd = &gmtproj_polyconic;
5011 GMT->current.proj.inv = &gmtproj_ipolyconic;
5012 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
5013
5014 if (GMT->common.R.oblique) {
5015 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
5016 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
5017 GMT->current.map.outside = &gmtmap_rect_outside;
5018 GMT->current.map.crossing = &gmtmap_rect_crossing;
5019 GMT->current.map.overlap = &gmtmap_rect_overlap;
5020 GMT->current.map.clip = &gmtmap_rect_clip;
5021 GMT->current.map.left_edge = &gmtmap_left_rect;
5022 GMT->current.map.right_edge = &gmtmap_right_rect;
5023 GMT->current.map.frame.check_side = true;
5024 }
5025 else {
5026 double y, dummy = 0.0;
5027 y = (GMT->common.R.wesn[YLO] * GMT->common.R.wesn[YHI] <= 0.0) ? 0.0 : MIN (fabs (GMT->common.R.wesn[YLO]), fabs (GMT->common.R.wesn[YHI]));
5028 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XLO], y, &xmin, &dummy);
5029 (*GMT->current.proj.fwd) (GMT, GMT->common.R.wesn[XHI], y, &xmax, &dummy);
5030 (*GMT->current.proj.fwd) (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YLO], &dummy, &ymin);
5031 (*GMT->current.proj.fwd) (GMT, GMT->current.proj.central_meridian, GMT->common.R.wesn[YHI], &dummy, &ymax);
5032 GMT->current.map.outside = &gmtmap_wesn_outside;
5033 GMT->current.map.crossing = &gmtmap_wesn_crossing;
5034 GMT->current.map.overlap = &gmtmap_wesn_overlap;
5035 GMT->current.map.clip = &map_wesn_clip;
5036 GMT->current.map.left_edge = &gmtproj_left_polyconic;
5037 GMT->current.map.right_edge = &gmtproj_right_polyconic;
5038 GMT->current.proj.polar = true;
5039 }
5040
5041 GMT->current.map.frame.horizontal = 1;
5042 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.pars[2]);
5043
5044 return (GMT_NOERROR);
5045 }
5046
5047 #ifdef HAVE_GDAL
5048 /*!
5049 * TRANSFORMATION ROUTINES FOR PROJ4 TRANSFORMATIONS (GMT_PROJ4_PROJS)
5050 */
map_init_proj4(struct GMT_CTRL * GMT,bool * search)5051 GMT_LOCAL int map_init_proj4 (struct GMT_CTRL *GMT, bool *search) {
5052 /*
5053 * Here we use the trick of letting the previous GMT functions do the necessary initializations
5054 * and at the end just replace the pointers to the FWD & INV transform functions to those of GDAL.
5055 */
5056 int error = GMT_NOERROR;
5057 double xmin = 0.0, xmax = 0.0, ymin = 0.0, ymax = 0.0;
5058
5059 *search = false;
5060 switch (GMT->current.proj.projection_GMT) {
5061 case GMT_LINEAR: error = gmtmap_init_linear (GMT, search); break; /* Linear transformations */
5062 case GMT_POLAR: error = gmtmap_init_polar (GMT, search); break; /* Both lon/lat are actually theta, radius */
5063 case GMT_MERCATOR: error = gmtmap_init_merc (GMT, search); break; /* Standard Mercator projection */
5064 case GMT_STEREO: error = gmtmap_init_stereo (GMT, search); break; /* Stereographic projection */
5065 case GMT_LAMBERT: error = gmtmap_init_lambert (GMT, search); break; /* Lambert Conformal Conic */
5066 case GMT_OBLIQUE_MERC: error = gmtmap_init_oblique (GMT, search); break; /* Oblique Mercator */
5067 case GMT_TM: error = gmtmap_init_tm (GMT, search); break; /* Transverse Mercator */
5068 case GMT_UTM: error = gmtmap_init_utm (GMT, search); break; /* Universal Transverse Mercator */
5069 case GMT_LAMB_AZ_EQ: error = gmtmap_init_lambeq (GMT, search); break; /* Lambert Azimuthal Equal-Area */
5070 case GMT_ORTHO: error = gmtmap_init_ortho (GMT, search); break; /* Orthographic Projection */
5071 case GMT_GENPER: error = gmtmap_init_genper (GMT, search); break; /* General Perspective Projection */
5072 case GMT_AZ_EQDIST: error = gmtmap_init_azeqdist (GMT, search); break; /* Azimuthal Equal-Distance Projection */
5073 case GMT_GNOMONIC: error = gmtmap_init_gnomonic (GMT, search); break; /* Azimuthal Gnomonic Projection */
5074 case GMT_MOLLWEIDE: error = gmtmap_init_mollweide (GMT, search); break; /* Mollweide Equal-Area */
5075 case GMT_HAMMER: error = gmtmap_init_hammer (GMT, search); break; /* Hammer-Aitoff Equal-Area */
5076 case GMT_VANGRINTEN: error = gmtmap_init_grinten (GMT, search); break; /* Van der Grinten */
5077 case GMT_WINKEL: error = gmtmap_init_winkel (GMT, search); break; /* Winkel Tripel */
5078 case GMT_ECKERT4: error = gmtmap_init_eckert4 (GMT, search); break; /* Eckert IV */
5079 case GMT_ECKERT6: error = gmtmap_init_eckert6 (GMT, search); break; /* Eckert VI */
5080 case GMT_CYL_EQ: error = gmtmap_init_cyleq (GMT, search); break; /* Cylindrical Equal-Area */
5081 case GMT_CYL_STEREO: error = gmtmap_init_cylstereo (GMT, search); break; /* Cylindrical Stereographic */
5082 case GMT_MILLER: error = gmtmap_init_miller (GMT, search); break; /* Miller Cylindrical */
5083 case GMT_CYL_EQDIST: error = gmtmap_init_cyleqdist (GMT, search); break; /* Cylindrical Equidistant */
5084 case GMT_ROBINSON: error = gmtmap_init_robinson (GMT, search); break; /* Robinson */
5085 case GMT_SINUSOIDAL: error = gmtmap_init_sinusoidal (GMT, search); break; /* Sinusoidal Equal-Area */
5086 case GMT_CASSINI: error = gmtmap_init_cassini (GMT, search); break; /* Cassini cylindrical */
5087 case GMT_ALBERS: error = gmtmap_init_albers (GMT, search); break; /* Albers Equal-Area Conic */
5088 case GMT_ECONIC: error = gmtmap_init_econic (GMT, search); break; /* Equidistant Conic */
5089 case GMT_POLYCONIC: error = gmtmap_init_polyconic (GMT, search); break; /* Polyconic */
5090 default: /* Non-GMT proj4 projection. Try to assign functions */
5091 GMT->current.proj.fwd = &gmt_proj4_fwd;
5092 GMT->current.proj.inv = &gmt_proj4_inv;
5093 GMT->current.proj.scale[GMT_X] = GMT->current.proj.scale[GMT_Y] = GMT->current.proj.proj4_scl;
5094 GMT->current.map.n_lon_nodes = 360; GMT->current.map.n_lat_nodes = 180;
5095 if (GMT->common.R.oblique) {
5096 gmt_proj4_fwd (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &xmin, &ymin);
5097 gmt_proj4_fwd (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], &xmax, &ymax);
5098 GMT->current.map.outside = &gmtmap_rect_outside;
5099 GMT->current.map.crossing = &gmtmap_rect_crossing;
5100 GMT->current.map.overlap = &gmtmap_rect_overlap;
5101 GMT->current.map.clip = &gmtmap_rect_clip;
5102 GMT->current.map.left_edge = &gmtmap_left_rect;
5103 GMT->current.map.right_edge = &gmtmap_right_rect;
5104 GMT->current.map.frame.check_side = true;
5105 }
5106 else {
5107 gmtmap_xy_search (GMT, &xmin, &xmax, &ymin, &ymax, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
5108 GMT->current.map.outside = &gmtmap_wesn_outside;
5109 GMT->current.map.crossing = &gmtmap_wesn_crossing;
5110 GMT->current.map.overlap = &gmtmap_wesn_overlap;
5111 GMT->current.map.clip = &map_wesn_clip;
5112 GMT->current.map.left_edge = &gmtmap_left_rect;
5113 GMT->current.map.right_edge = &gmtmap_right_rect;
5114 GMT->current.map.frame.horizontal = 2;
5115 }
5116 gmtmap_setinfo (GMT, xmin, xmax, ymin, ymax, GMT->current.proj.proj4_scl);
5117 if (GMT->current.setting.map_frame_type & GMT_IS_FANCY) GMT->current.setting.map_frame_type = GMT_IS_PLAIN;
5118 break;
5119 }
5120 /* Now we only have to replace the pointers to the FWD and INV transform functions */
5121 GMT->current.proj.fwd = &gmt_proj4_fwd;
5122 GMT->current.proj.inv = &gmt_proj4_inv;
5123 return error;
5124 }
5125 #endif
5126
5127 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5128 *
5129 * S E C T I O N 1.1 : S U P P O R T I N G R O U T I N E S
5130 *
5131 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
5132
5133 /*! . */
gmt_wesn_search(struct GMT_CTRL * GMT,double xmin,double xmax,double ymin,double ymax,double * west,double * east,double * south,double * north,bool add_pad)5134 void gmt_wesn_search (struct GMT_CTRL *GMT, double xmin, double xmax, double ymin, double ymax, double *west, double *east, double *south, double *north, bool add_pad) {
5135 double dx, dy, w, e, s, n, x, y, lat, *lon = NULL;
5136 unsigned int i, j, k;
5137
5138 /* Search for extreme lon/lat coordinates by matching along the rectangular boundary */
5139
5140 if (!GMT->current.map.n_lon_nodes) GMT->current.map.n_lon_nodes = urint (GMT->current.map.width / GMT->current.setting.map_line_step);
5141 if (!GMT->current.map.n_lat_nodes) GMT->current.map.n_lat_nodes = urint (GMT->current.map.height / GMT->current.setting.map_line_step);
5142
5143 dx = (xmax - xmin) / GMT->current.map.n_lon_nodes;
5144 dy = (ymax - ymin) / GMT->current.map.n_lat_nodes;
5145 /* Need temp array to hold all the longitudes we compute */
5146 lon = gmt_M_memory (GMT, NULL, 2 * (GMT->current.map.n_lon_nodes + GMT->current.map.n_lat_nodes + 2), double);
5147 w = s = DBL_MAX; e = n = -DBL_MAX;
5148 for (i = k = 0; i <= GMT->current.map.n_lon_nodes; i++) {
5149 x = (i == GMT->current.map.n_lon_nodes) ? xmax : xmin + i * dx;
5150 gmt_xy_to_geo (GMT, &lon[k++], &lat, x, ymin);
5151 if (lat < s) s = lat;
5152 if (lat > n) n = lat;
5153 gmt_xy_to_geo (GMT, &lon[k++], &lat, x, ymax);
5154 if (lat < s) s = lat;
5155 if (lat > n) n = lat;
5156 }
5157 for (j = 0; j <= GMT->current.map.n_lat_nodes; j++) {
5158 y = (j == GMT->current.map.n_lat_nodes) ? ymax : ymin + j * dy;
5159 gmt_xy_to_geo (GMT, &lon[k++], &lat, xmin, y);
5160 if (lat < s) s = lat;
5161 if (lat > n) n = lat;
5162 gmt_xy_to_geo (GMT, &lon[k++], &lat, xmax, y);
5163 if (lat < s) s = lat;
5164 if (lat > n) n = lat;
5165 }
5166 gmtlib_get_lon_minmax (GMT, lon, k, &w, &e); /* Determine lon-range by robust quadrant check */
5167 gmt_M_free (GMT, lon);
5168
5169 /* Then check if one or both poles are inside map; then the above won't be correct */
5170
5171 if (GMT->current.proj.pole_in_map[0]) { s = -90.0; w = 0.0; e = 360.0; }
5172 if (GMT->current.proj.pole_in_map[1]) { n = +90.0; w = 0.0; e = 360.0; }
5173
5174 if (add_pad) { /* Extend by 0.1 degrees, if possible */
5175 s -= 0.1; if (s < -90.0) s = -90.0; /* Make sure point is not inside area, 0.1 is just a small arbitrary number */
5176 n += 0.1; if (n > 90.0) n = 90.0; /* But don't go crazy beyond the pole */
5177 w -= 0.1; e += 0.1; if (fabs (w - e) > 360.0) { w = 0.0; e = 360.0; } /* Ensure max 360 range */
5178 }
5179 *west = w; *east = e; *south = s; *north = n; /* Pass back our findings */
5180 }
5181
5182 /*! . */
gmtmap_genper_search(struct GMT_CTRL * GMT,double * west,double * east,double * south,double * north,bool add_pad)5183 GMT_LOCAL void gmtmap_genper_search (struct GMT_CTRL *GMT, double *west, double *east, double *south, double *north, bool add_pad) {
5184 double w, e, s = 90.0, n = -90.0, *work_x = NULL, *work_y = NULL;
5185 uint64_t np, k;
5186 struct GMT_DATASEGMENT *S = gmt_get_segment (GMT, 2);
5187 /* Because the genper clip path may be a mix of straight borders and a curved horizon, we must determine this
5188 * clip path and search along it, getting lon,lat along the way, and find the extreme values to use. Before this
5189 * function was added, we ended up in the gmt_wesn_search function which would fail along the horizon in many
5190 * cases. P. Wessel, July 20, 2019. */
5191
5192 np = (GMT->current.proj.polar && (GMT->common.R.wesn[YLO] <= -90.0 || GMT->common.R.wesn[YHI] >= 90.0)) ? GMT->current.map.n_lon_nodes + 2: 2 * (GMT->current.map.n_lon_nodes + 1);
5193 work_x = gmt_M_memory (GMT, NULL, np, double);
5194 work_y = gmt_M_memory (GMT, NULL, np, double);
5195 gmtlib_genper_map_clip_path (GMT, np, work_x, work_y);
5196
5197 /* Search for extreme lon/lat coordinates by matching along the genper boundary */
5198
5199 /* Need temp datasegment to hold all the longitudes and latitudes we compute */
5200 gmt_alloc_segment (GMT, S, np, 2, 0, true);
5201 for (k = 0; k < np; k++) {
5202 gmt_xy_to_geo (GMT, &S->data[GMT_X][k], &S->data[GMT_Y][k], work_x[k], work_y[k]);
5203 if (S->data[GMT_Y][k] < s) s = S->data[GMT_Y][k];
5204 if (S->data[GMT_Y][k] > n) n = S->data[GMT_Y][k];
5205 }
5206 gmt_M_free (GMT, work_x);
5207 gmt_M_free (GMT, work_y);
5208 gmt_set_seg_minmax (GMT, GMT_IS_POLY, 2, S); /* Update min/max of x/y */
5209
5210 gmtlib_get_lon_minmax (GMT, S->data[GMT_X], np, &w, &e); /* Determine lon-range by robust quadrant check */
5211
5212 /* Then check if one or both poles are inside map; then the above won't be correct.
5213 * Because the clip path for genper may be some weird rounded octagon we must use the polygon directly. */
5214
5215 gmt_set_inside_mode (GMT, NULL, GMT_IOO_SPHERICAL); /* Ensure spherical treatment */
5216 if (gmt_inonout (GMT, GMT->current.proj.central_meridian, -90.0, S) == GMT_INSIDE) { s = -90.0; w = 0.0; e = 360.0; }
5217 if (gmt_inonout (GMT, GMT->current.proj.central_meridian, +90.0, S) == GMT_INSIDE) { n = +90.0; w = 0.0; e = 360.0; }
5218
5219 gmt_free_segment (GMT, &S);
5220
5221 if (add_pad) { /* Extend by 0.1 degrees */
5222 s -= 0.1; if (s < -90.0) s = -90.0; /* Make sure point is not inside area, 0.1 is just a small arbitrary number */
5223 n += 0.1; if (n > 90.0) n = 90.0; /* But don't go crazy beyond the pole */
5224 w -= 0.1; e += 0.1; if (fabs (w - e) > 360.0) { w = 0.0; e = 360.0; } /* Ensure max 360 range */
5225 }
5226 *west = w; *east = e; *south = s; *north = n; /* Pass back our findings */
5227 }
5228
5229 /*! . */
gmtmap_horizon_search(struct GMT_CTRL * GMT,double w,double e,double s,double n,double xmin,double xmax,double ymin,double ymax)5230 GMT_LOCAL int gmtmap_horizon_search (struct GMT_CTRL *GMT, double w, double e, double s, double n, double xmin, double xmax, double ymin, double ymax) {
5231 double dx, dy, d, x, y, lon, lat;
5232 unsigned int i, j;
5233 bool beyond = false;
5234
5235 /* Search for extreme original coordinates lon/lat and see if any fall beyond the horizon */
5236
5237 dx = (xmax - xmin) / GMT->current.map.n_lon_nodes;
5238 dy = (ymax - ymin) / GMT->current.map.n_lat_nodes;
5239 if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, w, s)) > GMT->current.proj.f_horizon) beyond = true;
5240 if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, e, n)) > GMT->current.proj.f_horizon) beyond = true;
5241 for (i = 0; !beyond && i <= GMT->current.map.n_lon_nodes; i++) {
5242 x = (i == GMT->current.map.n_lon_nodes) ? xmax : xmin + i * dx;
5243 gmt_xy_to_geo (GMT, &lon, &lat, x, ymin);
5244 if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5245 gmt_xy_to_geo (GMT, &lon, &lat, x, ymax);
5246 if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5247 }
5248 for (j = 0; !beyond && j <= GMT->current.map.n_lat_nodes; j++) {
5249 y = (j == GMT->current.map.n_lat_nodes) ? ymax : ymin + j * dy;
5250 gmt_xy_to_geo (GMT, &lon, &lat, xmin, y);
5251 if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5252 gmt_xy_to_geo (GMT, &lon, &lat, xmax, y);
5253 if ((d = gmtlib_great_circle_dist_degree (GMT, GMT->current.proj.central_meridian, GMT->current.proj.pole, lon, lat)) > GMT->current.proj.f_horizon) beyond = true;
5254 }
5255 if (beyond) {
5256 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Rectangular region for azimuthal projection extends beyond the horizon\n");
5257 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Please select a region that is completely within the visible hemisphere\n");
5258 return GMT_PROJECTION_ERROR;
5259 }
5260 return (GMT_NOERROR);
5261 }
5262
5263 /*! . */
gmtmap_wrap_around_check_x(struct GMT_CTRL * GMT,double * angle,double last_x,double last_y,double this_x,double this_y,double * xx,double * yy,unsigned int * sides)5264 GMT_LOCAL unsigned int gmtmap_wrap_around_check_x (struct GMT_CTRL *GMT, double *angle, double last_x, double last_y, double this_x, double this_y, double *xx, double *yy, unsigned int *sides) {
5265 double dx, dy, width, jump, gmt_half_map_width (struct GMT_CTRL *GMT, double y);
5266
5267 jump = this_x - last_x;
5268 width = MAX (gmt_half_map_width (GMT, this_y), gmt_half_map_width (GMT, last_y));
5269
5270 if (fabs (jump) - width <= GMT_CONV4_LIMIT || fabs (jump) <= GMT_CONV4_LIMIT) return (0);
5271 dy = this_y - last_y;
5272
5273 if (jump < -width) { /* Crossed right boundary */
5274 dx = GMT->current.map.width + jump;
5275 yy[0] = yy[1] = last_y + (GMT->current.map.width - last_x) * dy / dx;
5276 if (yy[0] < 0.0 || yy[0] > GMT->current.proj.rect[YHI]) return (0);
5277 xx[0] = gmtlib_right_boundary (GMT, yy[0]); xx[1] = gmtlib_left_boundary (GMT, yy[0]);
5278 sides[0] = 1;
5279 angle[0] = d_atan2d (dy, dx);
5280 }
5281 else { /* Crossed left boundary */
5282 dx = GMT->current.map.width - jump;
5283 yy[0] = yy[1] = last_y + last_x * dy / dx;
5284 if (yy[0] < 0.0 || yy[0] > GMT->current.proj.rect[YHI]) return (0);
5285 xx[0] = gmtlib_left_boundary (GMT, yy[0]); xx[1] = gmtlib_right_boundary (GMT, yy[0]);
5286 sides[0] = 3;
5287 angle[0] = d_atan2d (dy, -dx);
5288 }
5289 sides[1] = 4 - sides[0];
5290 angle[1] = angle[0] + 180.0;
5291
5292 return (2);
5293 }
5294
5295 /*! . */
gmtmap_get_crossings_x(struct GMT_CTRL * GMT,double * xc,double * yc,double x0,double y0,double x1,double y1)5296 GMT_LOCAL void gmtmap_get_crossings_x (struct GMT_CTRL *GMT, double *xc, double *yc, double x0, double y0, double x1, double y1) {
5297 /* Finds crossings for wrap-arounds */
5298 double xa, xb, ya, yb, dxa, dxb, dyb, left_yb, c;
5299
5300 xa = x0; xb = x1;
5301 ya = y0; yb = y1;
5302 if (xa > xb) { /* Make A the minimum x point */
5303 gmt_M_double_swap (xa, xb);
5304 gmt_M_double_swap (ya, yb);
5305 }
5306
5307 xb -= 2.0 * gmt_half_map_width (GMT, yb);
5308
5309 dxa = xa - gmtlib_left_boundary (GMT, ya);
5310 left_yb = gmtlib_left_boundary (GMT, yb);
5311 dxb = left_yb - xb;
5312 c = (doubleAlmostEqualZero (left_yb, xb)) ? 0.0 : 1.0 + dxa/dxb;
5313 dyb = (gmt_M_is_zero (c)) ? 0.0 : fabs (yb - ya) / c;
5314 yc[0] = yc[1] = (ya > yb) ? yb + dyb : yb - dyb;
5315 xc[0] = gmtlib_left_boundary (GMT, yc[0]);
5316 xc[1] = gmtlib_right_boundary (GMT, yc[0]);
5317 }
5318
5319 /*! . */
gmtmap_get_crossings_y(struct GMT_CTRL * GMT,double * xc,double * yc,double x0,double y0,double x1,double y1)5320 GMT_LOCAL void gmtmap_get_crossings_y (struct GMT_CTRL *GMT, double *xc, double *yc, double x0, double y0, double x1, double y1) {
5321 /* Finds crossings for wrap-arounds in y, assuming rectangular domain */
5322 double xa, xb, ya, yb, c;
5323
5324 xa = x0; xb = x1;
5325 ya = y0; yb = y1;
5326 if (ya > yb) { /* Make A the minimum y point */
5327 gmt_M_double_swap (xa, xb);
5328 gmt_M_double_swap (ya, yb);
5329 }
5330
5331 yb -= GMT->current.map.height; /* Now below the y = 0 line */
5332 c = (doubleAlmostEqualZero (ya, yb)) ? 0.0 : (xa - xb) / (ya - yb);
5333 xc[0] = xc[1] = xa - c * ya;
5334 yc[0] = 0.0;
5335 yc[1] = GMT->current.map.height;
5336 }
5337
5338 /*! . */
gmtmap_this_point_wraps_x(struct GMT_CTRL * GMT,double x0,double x1,double w_last,double w_this)5339 GMT_LOCAL bool gmtmap_this_point_wraps_x (struct GMT_CTRL *GMT, double x0, double x1, double w_last, double w_this) {
5340 /* Returns true if the 2 x-points implies a jump at this y-level of the map */
5341
5342 double w_min, w_max, dx;
5343 gmt_M_unused(GMT);
5344
5345 if (w_this > w_last) {
5346 w_max = w_this;
5347 w_min = w_last;
5348 }
5349 else {
5350 w_max = w_last;
5351 w_min = w_this;
5352 }
5353
5354 /* Second condition deals with points near bottom/top of maps
5355 where map width may shrink to zero */
5356
5357 return ((dx = fabs (x1 - x0)) > w_max && w_min > GMT_CONV4_LIMIT);
5358 }
5359
5360 /*! . */
gmtmap_will_it_wrap_x(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t * start)5361 GMT_LOCAL bool gmtmap_will_it_wrap_x (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t *start) {
5362 /* Determines if a polygon will wrap at edges */
5363 uint64_t i;
5364 bool wrap;
5365 double w_last, w_this;
5366
5367 if (!GMT->current.map.is_world) return (false);
5368 //if (!GMT->current.map.is_world)
5369 // f = (2.0 - GMT_CONV8_LIMIT);
5370 w_this = gmt_half_map_width (GMT, y[0]);
5371 for (i = 1, wrap = false; !wrap && i < n; i++) {
5372 w_last = w_this;
5373 w_this = gmt_half_map_width (GMT, y[i]);
5374 wrap = gmtmap_this_point_wraps_x (GMT, x[i-1], x[i], w_last, w_this);
5375 }
5376 *start = i - 1;
5377 return (wrap);
5378 }
5379
5380 /*! . */
gmtmap_truncate_x(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t start,int l_or_r)5381 GMT_LOCAL uint64_t gmtmap_truncate_x (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t start, int l_or_r) {
5382 /* Truncates a wrapping polygon against left or right edge */
5383
5384 uint64_t i, i1, j, k;
5385 double xc[4], yc[4], w_last, w_this;
5386
5387 /* First initialize variables that differ for left and right truncation */
5388
5389 if (l_or_r == -1) /* Left truncation (-1) */
5390 /* Find first point that is left of map center */
5391 i = (x[start] < GMT->current.map.half_width) ? start : start - 1;
5392 else /* Right truncation (+1) */
5393 /* Find first point that is right of map center */
5394 i = (x[start] > GMT->current.map.half_width) ? start : start - 1;
5395
5396 if (!GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5397
5398 GMT->current.plot.x[0] = x[i]; GMT->current.plot.y[0] = y[i];
5399 w_this = gmt_half_map_width (GMT, y[i]);
5400 k = j = 1;
5401 while (k <= n) {
5402 i1 = i;
5403 i = (i + 1)%n; /* Next point */
5404 w_last = w_this;
5405 w_this = gmt_half_map_width (GMT, y[i]);
5406 if (gmtmap_this_point_wraps_x (GMT, x[i1], x[i], w_last, w_this)) {
5407 (*GMT->current.map.get_crossings) (GMT, xc, yc, x[i1], y[i1], x[i], y[i]);
5408 if (l_or_r == -1)
5409 GMT->current.plot.x[j] = gmtlib_left_boundary (GMT, yc[0]);
5410 else
5411 GMT->current.plot.x[j] = gmtlib_right_boundary (GMT, yc[0]);
5412 GMT->current.plot.y[j] = yc[0];
5413 j++;
5414 if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5415 }
5416 if (l_or_r == -1) /* Left */
5417 GMT->current.plot.x[j] = (x[i] >= GMT->current.map.half_width) ? gmtlib_left_boundary (GMT, y[i]) : x[i];
5418 else /* Right */
5419 GMT->current.plot.x[j] = (x[i] < GMT->current.map.half_width) ? gmtlib_right_boundary (GMT, y[i]) : x[i];
5420 GMT->current.plot.y[j] = y[i];
5421 j++, k++;
5422 if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5423 }
5424 return (j);
5425 }
5426
5427 /*! . */
gmtmap_truncate_tm(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t start,int b_or_t)5428 GMT_LOCAL uint64_t gmtmap_truncate_tm (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t start, int b_or_t) {
5429 /* Truncates a wrapping polygon against bottom or top edge for global TM maps */
5430
5431 uint64_t i, i1, j, k;
5432 double xc[4], yc[4], trunc_y;
5433
5434 /* First initialize variables that differ for bottom and top truncation */
5435
5436 if (b_or_t == -1) { /* Bottom truncation (-1) */
5437 /* Find first point that is below map center */
5438 i = (y[start] < GMT->current.map.half_height) ? start : start - 1;
5439 trunc_y = 0.0;
5440 }
5441 else { /* Top truncation (+1) */
5442 /* Find first point that is above map center */
5443 i = (y[start] > GMT->current.map.half_height) ? start : start - 1;
5444 trunc_y = GMT->current.map.height;
5445 }
5446
5447 if (!GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5448
5449 GMT->current.plot.x[0] = x[i]; GMT->current.plot.y[0] = y[i];
5450 k = j = 1;
5451 while (k <= n) {
5452 i1 = i;
5453 i = (i + 1)%n; /* Next point */
5454 if (gmtmap_this_point_wraps_tm (GMT, y[i1], y[i])) {
5455 gmtmap_get_crossings_tm (GMT, xc, yc, x[i1], y[i1], x[i], y[i]);
5456 GMT->current.plot.x[j] = xc[0];
5457 GMT->current.plot.y[j] = trunc_y;
5458 j++;
5459 if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5460 }
5461 if (b_or_t == -1) /* Bottom */
5462 GMT->current.plot.y[j] = (y[i] >= GMT->current.map.half_height) ? 0.0 : y[i];
5463 else /* Top */
5464 GMT->current.plot.y[j] = (y[i] < GMT->current.map.half_height) ? GMT->current.map.height : y[i];
5465 GMT->current.plot.x[j] = x[i];
5466 j++, k++;
5467 if (j >= GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
5468 }
5469 return (j);
5470 }
5471
5472 /*! . */
gmtmap_cartesian_dist2(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)5473 GMT_LOCAL double gmtmap_cartesian_dist2 (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
5474 /* Calculates the good-old straight line distance squared in users units */
5475 gmt_M_unused(GMT);
5476 x1 -= x0; y1 -= y0;
5477 return (x1*x1 + y1*y1);
5478 }
5479
5480 /*! . */
gmtmap_cartesian_dist_proj2(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5481 GMT_LOCAL double gmtmap_cartesian_dist_proj2 (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5482 /* Calculates the good-old straight line distance squared after first projecting the data */
5483 double x0, y0, x1, y1;
5484 gmt_geo_to_xy (GMT, lon1, lat1, &x0, &y0);
5485 gmt_geo_to_xy (GMT, lon2, lat2, &x1, &y1);
5486 x1 -= x0; y1 -= y0;
5487 return (x1*x1 + y1*y1);
5488 }
5489
5490 /*! . */
gmtmap_flatearth_dist_degree(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)5491 GMT_LOCAL double gmtmap_flatearth_dist_degree (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
5492 /* Calculates the approximate flat earth distance in degrees.
5493 If difference in longitudes exceeds 180 we pick the other
5494 offset (360 - offset)
5495 */
5496 double dlon;
5497 gmt_M_unused(GMT);
5498
5499 gmt_M_set_delta_lon (x0, x1, dlon);
5500 return (hypot ( dlon * cosd (0.5 * (y1 + y0)), (y1 - y0)));
5501 }
5502
5503 /*! . */
gmtmap_flatearth_dist_meter(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)5504 GMT_LOCAL double gmtmap_flatearth_dist_meter (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
5505 /* Calculates the approximate flat earth distance in km.
5506 If difference in longitudes exceeds 180 we pick the other
5507 offset (360 - offset)
5508 */
5509 return (gmtmap_flatearth_dist_degree (GMT, x0, y0, x1, y1) * GMT->current.proj.DIST_M_PR_DEG);
5510 }
5511
5512 /*! . */
gmtmap_haversine(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5513 GMT_LOCAL double gmtmap_haversine (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5514 /* Haversine formula for great circle distance. Intermediate function that returns sin^2 (half_angle).
5515 * This avoids problems with short distances where cos(c) is close to 1 and acos is inaccurate.
5516 */
5517
5518 double sx, sy, sin_half_squared;
5519
5520 if (lat1 == lat2 && lon1 == lon2) return (0.0);
5521
5522 if (GMT->current.setting.proj_aux_latitude != GMT_LATSWAP_NONE) { /* Use selected auxiliary latitude */
5523 lat1 = gmt_lat_swap (GMT, lat1, GMT->current.setting.proj_aux_latitude);
5524 lat2 = gmt_lat_swap (GMT, lat2, GMT->current.setting.proj_aux_latitude);
5525 }
5526
5527 sy = sind (0.5 * (lat2 - lat1));
5528 sx = sind (0.5 * (lon2 - lon1)); /* If there is a 360 wrap here then the sign of sx is wrong but we only use sx^2 */
5529 sin_half_squared = sy * sy + cosd (lat2) * cosd (lat1) * sx * sx;
5530
5531 return (sin_half_squared);
5532 }
5533
5534 /*! . */
gmtmap_geodesic_dist_degree(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)5535 GMT_LOCAL double gmtmap_geodesic_dist_degree (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
5536 /* Compute the great circle arc length in degrees on an ellipsoidal
5537 * Earth. We do this by converting to geocentric coordinates.
5538 */
5539
5540 double a, b, c, d, e, f, a1, b1, c1, d1, e1, f1, thg, sc, sd, dist;
5541
5542 /* Equations are unstable for latitudes of exactly 0 degrees. */
5543
5544 if (latE == 0.0) latE = 1.0e-08;
5545 if (latS == 0.0) latS = 1.0e-08;
5546
5547 /* Must convert from geographic to geocentric coordinates in order
5548 * to use the spherical trig equations. This requires a latitude
5549 * correction given by: 1-ECC2=1-2*F + F*F = GMT->current.proj.one_m_ECC2
5550 */
5551
5552 thg = atan (GMT->current.proj.one_m_ECC2 * tand (latE));
5553 sincos (thg, &c, &f); f = -f;
5554 sincosd (lonE, &d, &e); e = -e;
5555 a = f * e;
5556 b = -f * d;
5557
5558 /* Calculate some trig constants. */
5559
5560 thg = atan (GMT->current.proj.one_m_ECC2 * tand (latS));
5561 sincos (thg, &c1, &f1); f1 = -f1;
5562 sincosd (lonS, &d1, &e1); e1 = -e1;
5563 a1 = f1 * e1;
5564 b1 = -f1 * d1;
5565
5566 /* Spherical trig relationships used to compute angles. */
5567
5568 sc = a * a1 + b * b1 + c * c1;
5569 sd = 0.5 * sqrt ((pow (a-a1,2.0) + pow (b-b1,2.0) + pow (c-c1,2.0)) * (pow (a+a1,2.0) + pow (b+b1, 2.0) + pow (c+c1, 2.0)));
5570 dist = atan2d (sd, sc);
5571 if (dist < 0.0) dist += 360.0;
5572
5573 return (dist);
5574 }
5575
5576 /*! . */
gmtmap_andoyer_dist_degree(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5577 GMT_LOCAL double gmtmap_andoyer_dist_degree (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5578 /* Approximate geodesic distance on an ellipsoid in degrees
5579 * H. Andoyer from Astronomical Algorithms, Jean Meeus, second edition.
5580 */
5581 double sg = sind (0.5 * (lat2 - lat1));
5582 double sf = sind (0.5 * (lat2 + lat1));
5583 double sl = sind (0.5 * (lon2 - lon1)); /* Might have wrong sign if 360 wrap but we only use sl^2 */
5584 double s, c, w, r, h1, h2;
5585 sg *= sg;
5586 sl *= sl;
5587 sf *= sf;
5588 s = sg * (1.0 - sl) + (1.0 - sf) * sl;
5589 c = (1.0 - sg) * (1.0 - sl) + sf * sl;
5590
5591 w = atan (sqrt (s/c));
5592 r = sqrt (s*c) / w;
5593 h1 = 0.5 * (3.0 * r - 1.0) / c;
5594 h2 = 0.5 * (3.0 * r + 1.0) / s;
5595 return (2.0 * w * (1.0 + GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening * (h1 * sf * (1.0 - sg) - h2 * (1.0 - sf) * sg)));
5596 }
5597
5598 /*! . */
gmtmap_andoyer_dist_meter(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5599 GMT_LOCAL double gmtmap_andoyer_dist_meter (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5600 return (GMT->current.proj.EQ_RAD * gmtmap_andoyer_dist_degree (GMT, lon1, lat1, lon2, lat2));
5601 }
5602
5603 /*! . */
gmtmap_vincenty_dist_meter(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)5604 GMT_LOCAL double gmtmap_vincenty_dist_meter (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
5605 /* Translation of NGS FORTRAN code for determination of true distance
5606 ** and respective forward and back azimuths between two points on the
5607 ** ellipsoid. Good for any pair of points that are not antipodal.
5608 **
5609 ** INPUT
5610 ** latS, lonS -- latitude and longitude of first point in radians.
5611 ** latE, lonE -- latitude and longitude of second point in radians.
5612 **
5613 ** OUTPUT
5614 ** s -- distance between points in meters.
5615 ** Modified by P.W. from: http://article.gmane.org/gmane.comp.gis.proj-4.devel/3478
5616 */
5617 double s, c, d, e, r, f, d_lon, dx, x, y, sa, cx, cy, cz, sx, sy, c2a, cu1, cu2, su1, tu1, tu2, ts, baz, faz;
5618 int n_iter = 0;
5619
5620 f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
5621 r = 1.0 - f;
5622 tu1 = r * tand (latS);
5623 tu2 = r * tand (latE);
5624 cu1 = 1.0 / sqrt (tu1 * tu1 + 1.0);
5625 su1 = cu1 * tu1;
5626 cu2 = 1.0 / sqrt (tu2 * tu2 + 1.0);
5627 ts = cu1 * cu2;
5628 baz = ts * tu2;
5629 faz = baz * tu1;
5630 gmt_M_set_delta_lon (lonS, lonE, d_lon);
5631 if (gmt_M_is_zero (d_lon) && doubleAlmostEqualZero (latS, latE)) return 0.0;
5632 x = dx = D2R * d_lon;
5633 do {
5634 n_iter++;
5635 sincos (x, &sx, &cx);
5636 tu1 = cu2 * sx;
5637 tu2 = baz - su1 * cu2 * cx;
5638 sy = sqrt (tu1 * tu1 + tu2 * tu2);
5639 cy = ts * cx + faz;
5640 y = atan2 (sy, cy);
5641 sa = ts * sx / sy;
5642 c2a = -sa * sa + 1.0;
5643 cz = faz + faz;
5644 if (c2a > 0.0) cz = -cz / c2a + cy;
5645 e = cz * cz * 2.0 - 1.0;
5646 c = ((c2a * -3.0 + 4.0) * f + 4.0) * c2a * f / 16.0;
5647 d = x;
5648 x = ((e * cy * c + cz) * sy * c + y) * sa;
5649 x = (1.0 - c) * x * f + dx;
5650 } while (fabs (d - x) > VINCENTY_EPS && n_iter <= 50);
5651 if (n_iter > VINCENTY_MAX_ITER) {
5652 GMT->current.proj.n_geodesic_approx++; /* Count inaccurate results */
5653 if (GMT->current.proj.n_geodesic_approx == 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "At least one near- or actual antipodal points encountered. Precision may be reduced slightly.\n");
5654 s = M_PI;
5655 }
5656 else {
5657 x = sqrt ((1.0 / r / r - 1.0) * c2a + 1.0) + 1.0;
5658 x = (x - 2.0) / x;
5659 c = (x * x / 4.0 + 1.0) / (1.0 - x);
5660 d = (x * 0.375 * x - 1.0) * x;
5661 s = ((((sy * sy * 4.0 - 3.0) * (1.0 - e - e) * cz * d / 6.0 - e * cy) * d / 4.0 + cz) * sy * d + y) * c * r;
5662 if (s > M_PI) {
5663 GMT->current.proj.n_geodesic_approx++; /* Count inaccurate results */
5664 if (GMT->current.proj.n_geodesic_approx == 1) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "At least one near- or actual antipodal points encountered. Precision may be reduced slightly.\n");
5665 s = M_PI;
5666 }
5667 }
5668 GMT->current.proj.n_geodesic_calls++;
5669 return (s * GMT->current.proj.EQ_RAD);
5670 }
5671
5672 /*! . */
gmtmap_rudoe_dist_meter(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)5673 GMT_LOCAL double gmtmap_rudoe_dist_meter (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
5674 /* Compute length of geodesic between locations in meters
5675 * We use Rudoe's equation from Bomford.
5676 */
5677
5678 double e1, el, sinthi, costhi, sinthk, costhk, tanthi, tanthk, sina12, cosa12, d_lon;
5679 double al, a12top, a12bot, a12, e2, e3, c0, c2, c4, v1, v2, z1, z2, x2, y2, dist;
5680 double e1p1, sqrte1p1, sin_dl, cos_dl, u1bot, u1, u2top, u2bot, u2, b0, du, pdist;
5681
5682
5683 /* Equations are unstable for latitudes of exactly 0 degrees. */
5684
5685 if (latE == 0.0) latE = 1.0e-08;
5686 if (latS == 0.0) latS = 1.0e-08;
5687
5688 /* Now compute the distance between the two points using Rudoe's
5689 * formula given in Bomford's GEODESY, section 2.15(b).
5690 * (Unclear if it is 1971 or 1980 edition)
5691 * (There is some numerical problem with the following formulae.
5692 * If the station is in the southern hemisphere and the event in
5693 * in the northern, these equations give the longer, not the
5694 * shorter distance between the two locations. Since the equations
5695 * are fairly messy, the simplest solution is to reverse the
5696 * meanings of the two locations for this case.)
5697 */
5698
5699 if (latS < 0.0) { /* Station in southern hemisphere, swap */
5700 gmt_M_double_swap (lonS, lonE);
5701 gmt_M_double_swap (latS, latE);
5702 }
5703 el = GMT->current.proj.ECC2 / GMT->current.proj.one_m_ECC2;
5704 e1 = 1.0 + el;
5705 sincosd (latE, &sinthi, &costhi);
5706 sincosd (latS, &sinthk, &costhk);
5707 gmt_M_set_delta_lon (lonS, lonE, d_lon);
5708 sincosd (d_lon, &sin_dl, &cos_dl);
5709 tanthi = sinthi / costhi;
5710 tanthk = sinthk / costhk;
5711 al = tanthi / (e1 * tanthk) + GMT->current.proj.ECC2 * sqrt ((e1 + tanthi * tanthi) / (e1 + tanthk * tanthk));
5712 a12top = sin_dl;
5713 a12bot = (al - cos_dl) * sinthk;
5714 a12 = atan2 (a12top,a12bot);
5715 sincos (a12, &sina12, &cosa12);
5716 e1 = el * (pow (costhk * cosa12, 2.0) + sinthk * sinthk);
5717 e2 = e1 * e1;
5718 e3 = e1 * e2;
5719 c0 = 1.0 + 0.25 * e1 - (3.0 / 64.0) * e2 + (5.0 / 256.0) * e3;
5720 c2 = -0.125 * e1 + (1.0 / 32) * e2 - (15.0 / 1024.0) * e3;
5721 c4 = -(1.0 / 256.0) * e2 + (3.0 / 1024.0) * e3;
5722 v1 = GMT->current.proj.EQ_RAD / sqrt (1.0 - GMT->current.proj.ECC2 * sinthk * sinthk);
5723 v2 = GMT->current.proj.EQ_RAD / sqrt (1.0 - GMT->current.proj.ECC2 * sinthi * sinthi);
5724 z1 = v1 * (1.0 - GMT->current.proj.ECC2) * sinthk;
5725 z2 = v2 * (1.0 - GMT->current.proj.ECC2) * sinthi;
5726 x2 = v2 * costhi * cos_dl;
5727 y2 = v2 * costhi * sin_dl;
5728 e1p1 = e1 + 1.0;
5729 sqrte1p1 = sqrt (e1p1);
5730 u1bot = sqrte1p1 * cosa12;
5731 u1 = atan2 (tanthk, u1bot);
5732 u2top = v1 * sinthk + e1p1 * (z2 - z1);
5733 u2bot = sqrte1p1 * (x2 * cosa12 - y2 * sinthk * sina12);
5734 u2 = atan2 (u2top, u2bot);
5735 b0 = v1 * sqrt (1.0 + el * pow (costhk * cosa12, 2.0)) / e1p1;
5736 du = u2 - u1;
5737 if (fabs (du) > M_PI) du = copysign (TWO_PI - fabs (du), du);
5738 pdist = b0 * (c2 * (sin (2.0 * u2) - sin(2.0 * u1)) + c4 * (sin (4.0 * u2) - sin (4.0 * u1)));
5739 dist = fabs (b0 * c0 * du + pdist);
5740
5741 return (dist);
5742 }
5743
5744 /*! . */
gmtmap_loxodrome_dist_degree(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5745 GMT_LOCAL double gmtmap_loxodrome_dist_degree (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5746 /* Calculates the distance along the loxodrome, in meter */
5747 double dist, d_lon;
5748 gmt_M_set_delta_lon (lon1, lon2, d_lon);
5749 if (doubleAlmostEqualZero (lat1, lat2)) { /* Along parallel */
5750 if (GMT->current.proj.GMT_convert_latitudes) lat1 = gmt_M_latg_to_latc (GMT, lat1);
5751 dist = fabs (d_lon) * cosd (lat1);
5752 }
5753 else { /* General case */
5754 double dx, dy, Az;
5755 if (GMT->current.proj.GMT_convert_latitudes) {
5756 lat1 = gmt_M_latg_to_latc (GMT, lat1);
5757 lat2 = gmt_M_latg_to_latc (GMT, lat2);
5758 }
5759 dx = D2R * d_lon;
5760 dy = d_log (GMT, tand (45.0 + 0.5 * lat2)) - d_log (GMT, tand (45.0 + 0.5 * lat1));
5761 Az = atan2 (dx, dy);
5762 dist = fabs (dx / cos (Az));
5763 }
5764 return (dist);
5765 }
5766
5767 /*! . */
gmtmap_loxodrome_dist_meter(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)5768 GMT_LOCAL double gmtmap_loxodrome_dist_meter (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
5769 /* Calculates the loxodrome distance in meter */
5770 return (gmtmap_loxodrome_dist_degree (GMT, lon1, lat1, lon2, lat2) * GMT->current.proj.DIST_M_PR_DEG);
5771 }
5772
5773 /*! . */
gmtmap_az_backaz_loxodrome(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)5774 GMT_LOCAL double gmtmap_az_backaz_loxodrome (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
5775 /* Calculate azimuths or backazimuths. Loxodrome mode.
5776 * First point is considered "Event" and second "Station".
5777 * Azimuth is direction from Station to Event.
5778 * BackAzimuth is direction from Event to Station */
5779
5780 double az, d_lon;
5781
5782 if (baz) { /* exchange point one and two */
5783 gmt_M_double_swap (lonS, lonE);
5784 gmt_M_double_swap (latS, latE);
5785 }
5786 gmt_M_set_delta_lon (lonE, lonS, d_lon);
5787 if (doubleAlmostEqualZero (latS, latE)) /* Along parallel */
5788 az = (d_lon > 0.0) ? 90 : -90.0;
5789 else { /* General case */
5790 double dx, dy;
5791 if (GMT->current.proj.GMT_convert_latitudes) {
5792 latS = gmt_M_latg_to_latc (GMT, latS);
5793 latE = gmt_M_latg_to_latc (GMT, latE);
5794 }
5795 dx = D2R * d_lon;
5796 dy = d_log (GMT, tand (45.0 + 0.5 * latS)) - d_log (GMT, tand (45.0 + 0.5 * latE));
5797 az = atan2d (dx, dy);
5798 if (az < 0.0) az += 360.0;
5799 }
5800 return (az);
5801 }
5802
5803 /*! . */
gmtmap_near_a_point_spherical(struct GMT_CTRL * GMT,double x,double y,struct GMT_DATATABLE * T,double dist)5804 GMT_LOCAL bool gmtmap_near_a_point_spherical (struct GMT_CTRL *GMT, double x, double y, struct GMT_DATATABLE *T, double dist) {
5805 uint64_t row, seg;
5806 bool each_point_has_distance;
5807 double d;
5808
5809 each_point_has_distance = (dist <= 0.0 && T->segment[0]->n_columns > 2);
5810 for (seg = 0; seg < T->n_segments; seg++) {
5811 for (row = 0; row < T->segment[seg]->n_rows; row++) {
5812 d = gmt_distance (GMT, x, y, T->segment[seg]->data[GMT_X][row], T->segment[seg]->data[GMT_Y][row]);
5813 if (each_point_has_distance) dist = T->segment[seg]->data[GMT_Z][row];
5814 if (d <= dist) return (true);
5815 }
5816 }
5817 return (false);
5818 }
5819
5820 /*! . */
gmtmap_near_a_point_cartesian(struct GMT_CTRL * GMT,double x,double y,struct GMT_DATATABLE * T,double dist)5821 GMT_LOCAL bool gmtmap_near_a_point_cartesian (struct GMT_CTRL *GMT, double x, double y, struct GMT_DATATABLE *T, double dist) {
5822 /* Since Cartesian we use a gmt_distance set to return distances^2 (avoiding hypot) */
5823 bool each_point_has_distance;
5824 uint64_t row, seg;
5825 double d, x0, y0, xn, d0, d02 = 0.0, dn;
5826
5827 each_point_has_distance = (dist <= 0.0 && T->segment[0]->n_columns > 2);
5828
5829 /* Assumes the points have been sorted so xp[0] is xmin and xp[n-1] is xmax] !!! */
5830
5831 /* See if we are safely outside the range */
5832 x0 = T->segment[0]->data[GMT_X][0];
5833 d0 = (each_point_has_distance) ? T->segment[0]->data[GMT_Z][0] : dist;
5834 xn = T->segment[T->n_segments-1]->data[GMT_X][T->segment[T->n_segments-1]->n_rows-1];
5835 dn = (each_point_has_distance) ? T->segment[T->n_segments-1]->data[GMT_Z][T->segment[T->n_segments-1]->n_rows-1] : dist;
5836 if ((x < (x0 - d0)) || (x > (xn) + dn)) return (false);
5837
5838 /* No, must search the points */
5839 if (!each_point_has_distance) d02 = dist * dist;
5840
5841 for (seg = 0; seg < T->n_segments; seg++) {
5842 for (row = 0; row < T->segment[seg]->n_rows; row++) {
5843 x0 = T->segment[seg]->data[GMT_X][row];
5844 d0 = (each_point_has_distance) ? T->segment[seg]->data[GMT_Z][row] : dist;
5845 if (fabs (x - x0) <= d0) { /* Simple x-range test first */
5846 y0 = T->segment[seg]->data[GMT_Y][row];
5847 if (fabs (y - y0) <= d0) { /* Simple y-range test next */
5848 /* Here we must compute distance */
5849 if (each_point_has_distance) d02 = d0 * d0;
5850 d = gmt_distance (GMT, x, y, x0, y0);
5851 if (d <= d02) return (true);
5852 }
5853 }
5854 }
5855 }
5856 return (false);
5857 }
5858
5859 /* Functions involving distance from arbitrary points to a line */
5860
5861 /*! . */
gmtmap_near_a_line_cartesian(struct GMT_CTRL * GMT,double lon,double lat,uint64_t seg,struct GMT_DATASEGMENT * S,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)5862 GMT_LOCAL bool gmtmap_near_a_line_cartesian (struct GMT_CTRL *GMT, double lon, double lat, uint64_t seg, struct GMT_DATASEGMENT *S, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
5863 bool perpendicular_only = false, interior, within;
5864 uint64_t row0, row1;
5865 double edge, dx, dy, xc, yc, s, s_inv, d, dist_AB, fraction, S_dist, S_dist2;
5866 struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
5867 /* gmtmap_near_a_line_cartesian works in one of two modes, depending on return_mindist.
5868 Since return_mindist is composed of two settings we must first set
5869 perpendicular_only = (return_mindist >= 10);
5870 return_mindist -= 10 * perpendicular_only;
5871 That is, if 10 was added it means perpendicular_only is set and then the 10 is
5872 removed. We now consider what is left of return_mindist:
5873 (1) return_mindist == 0:
5874 We expect each segment to have its dist variable set to a minimum distance,
5875 and if the point is within this distance from the line then we return true;
5876 otherwise we return false. If the segments have not set their distances then
5877 it will have been initialized at 0 and only a point on the line will return true.
5878 If perpendicular_only we ignore a point that is within the distance of the
5879 linesegment endpoints but project onto the extension of the line (i.e., it is
5880 "outside" the extent of the line). We return false in that case.
5881 (2) return_mindist != 0:
5882 Return the minimum distance via dist_min. In addition, if > 1:
5883 If == 2 we also return the coordinate of nearest point via x_near, y_near.
5884 If == 3 we instead return segment number and point number (fractional) of that point via x_near, y_near.
5885 The function will always return true, except if perpendicular_only is set: then we
5886 return false if the point projects onto the extension of the line (i.e., it is "outside"
5887 the extent of the line). */
5888
5889 if (return_mindist >= 10) { /* Exclude circular region surrounding line endpoints */
5890 perpendicular_only = true;
5891 return_mindist -= 10;
5892 }
5893
5894 if (S->n_rows <= 0) return (false); /* empty; skip */
5895
5896 if (return_mindist)
5897 S_dist = S_dist2 = 0.0; /* Explicitly set dist to zero so the shortest distance can be found */
5898 else {
5899 S_dist = SH->dist;
5900 S_dist2 = S_dist * S_dist; /* Since we compute distances squared for Cartesian cases */
5901 }
5902 /* Find nearest point on this line */
5903
5904 for (row0 = 0; row0 < S->n_rows; row0++) { /* loop over nodes on current line */
5905 d = gmt_distance (GMT, lon, lat, S->data[GMT_X][row0], S->data[GMT_Y][row0]); /* Distance between our point and j'th node on seg'th line */
5906 if (return_mindist && d < (*dist_min)) { /* Update min distance */
5907 *dist_min = d;
5908 if (return_mindist == 2) { *x_near = S->data[GMT_X][row0]; *y_near = S->data[GMT_Y][row0]; } /* Also update (x,y) of nearest point on the line */
5909 else if (return_mindist == 3) { *x_near = (double)seg; *y_near = (double)row0;} /* Instead update (seg, pt) of nearest point on the line */
5910 }
5911 interior = (row0 > 0 && row0 < (S->n_rows - 1)); /* Only false if we are processing one of the end points */
5912 if (d <= S_dist2 && (interior || !perpendicular_only)) return (true); /* Node inside the critical distance; we are done */
5913 }
5914
5915 if (S->n_rows < 2) return (false); /* 1-point "line" is a point; skip segment check */
5916
5917 /* If we get here we must check for intermediate points along the straight lines between segment nodes.
5918 * However, since we know all nodes are outside the circle, we first check if the pair of nodes making
5919 * up the next line segment are outside of the circumscribing square before we need to solve for the
5920 * intersection between the line segment and the normal from our point. */
5921
5922 for (row0 = 0, row1 = 1, within = false; row1 < S->n_rows; row0++, row1++) { /* loop over straight segments on current line */
5923 if (!return_mindist) {
5924 edge = lon - S_dist;
5925 if (S->data[GMT_X][row0] < edge && S->data[GMT_X][row1] < edge) continue; /* Left of square */
5926 edge = lon + S_dist;
5927 if (S->data[GMT_X][row0] > edge && S->data[GMT_X][row1] > edge) continue; /* Right of square */
5928 edge = lat - S_dist;
5929 if (S->data[GMT_Y][row0] < edge && S->data[GMT_Y][row1] < edge) continue; /* Below square */
5930 edge = lat + S_dist;
5931 if (S->data[GMT_Y][row0] > edge && S->data[GMT_Y][row1] > edge) continue; /* Above square */
5932 }
5933
5934 /* Here there is potential for the line segment crossing inside the circle */
5935
5936 dx = S->data[GMT_X][row1] - S->data[GMT_X][row0];
5937 dy = S->data[GMT_Y][row1] - S->data[GMT_Y][row0];
5938 if (dx == 0.0) { /* Line segment is vertical, our normal is thus horizontal */
5939 if (dy == 0.0) continue; /* Dummy segment with no length */
5940 xc = S->data[GMT_X][row0];
5941 yc = lat;
5942 if (S->data[GMT_Y][row0] < yc && S->data[GMT_Y][row1] < yc ) continue; /* Cross point is on extension */
5943 if (S->data[GMT_Y][row0] > yc && S->data[GMT_Y][row1] > yc ) continue; /* Cross point is on extension */
5944 }
5945 else { /* Line segment is not vertical */
5946 if (dy == 0.0) { /* Line segment is horizontal, our normal is thus vertical */
5947 xc = lon;
5948 yc = S->data[GMT_Y][row0];
5949 }
5950 else { /* General case of oblique line */
5951 s = dy / dx;
5952 s_inv = -1.0 / s;
5953 xc = (lat - S->data[GMT_Y][row0] + s * S->data[GMT_X][row0] - s_inv * lon ) / (s - s_inv);
5954 yc = S->data[GMT_Y][row0] + s * (xc - S->data[GMT_X][row0]);
5955
5956 }
5957 /* To be inside, (xc, yc) must (1) be on the line segment and not its extension and (2) be within dist of our point */
5958
5959 if (S->data[GMT_X][row0] < xc && S->data[GMT_X][row1] < xc ) continue; /* Cross point is on extension */
5960 if (S->data[GMT_X][row0] > xc && S->data[GMT_X][row1] > xc ) continue; /* Cross point is on extension */
5961 }
5962
5963 /* OK, here we must check how close the crossing point is */
5964
5965 d = gmt_distance (GMT, lon, lat, xc, yc); /* Distance between our point and intersection */
5966 if (return_mindist && d < (*dist_min)) { /* Update min distance */
5967 *dist_min = d;
5968 if (return_mindist == 2) { *x_near = xc; *y_near = yc;} /* Also update nearest point on the line */
5969 else if (return_mindist == 3) { /* Instead update (seg, pt) of nearest point on the line */
5970 *x_near = (double)seg;
5971 dist_AB = gmt_distance (GMT, S->data[GMT_X][row0], S->data[GMT_Y][row0], S->data[GMT_X][row1], S->data[GMT_Y][row1]);
5972 fraction = (dist_AB > 0.0) ? gmt_distance (GMT, S->data[GMT_X][row0], S->data[GMT_Y][row0], xc, yc) / dist_AB : 0.0;
5973 *y_near = (double)row0 + fraction;
5974 }
5975 within = true;
5976 }
5977 if (d <= S_dist2) return (true); /* Node inside the critical distance; we are done */
5978 }
5979
5980 if (return_mindist) *dist_min = sqrt (*dist_min); /* Undo the squared distances */
5981
5982 return (within); /* All tests failed, we are not close to the line(s), or we just return distance and interior (see comments above) */
5983 }
5984
5985 /*! . */
gmtmap_near_lines_cartesian(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)5986 GMT_LOCAL bool gmtmap_near_lines_cartesian (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
5987 uint64_t seg;
5988 int mode = return_mindist, status;
5989 bool OK = false;
5990 if (mode >= 10) mode -= 10; /* Exclude (or flag) circular region surrounding line endpoints */
5991 if (mode) *dist_min = DBL_MAX; /* Want to find the minimum distance so init to huge */
5992
5993 for (seg = 0; seg < T->n_segments; seg++) { /* Loop over each line segment */
5994 status = gmtmap_near_a_line_cartesian (GMT, lon, lat, seg, T->segment[seg], return_mindist, dist_min, x_near, y_near);
5995 if (status) { /* Got a min distance or satisfied the min dist requirement */
5996 if (!return_mindist) return (true); /* Done, we are within distance of one of the lines */
5997 OK = true;
5998 }
5999 }
6000 return (OK);
6001 }
6002
6003 /*! . */
gmtmap_near_a_line_spherical(struct GMT_CTRL * P,double lon,double lat,uint64_t seg,struct GMT_DATASEGMENT * S,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)6004 GMT_LOCAL bool gmtmap_near_a_line_spherical (struct GMT_CTRL *P, double lon, double lat, uint64_t seg, struct GMT_DATASEGMENT *S, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
6005 bool perpendicular_only = false, interior, within;
6006 uint64_t row, prev_row;
6007 double d, A[3], B[3], GMT[3], X[3], xlon, xlat, cx_dist, cos_dist, dist_AB, fraction;
6008 struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
6009
6010 /* gmtmap_near_a_line_spherical works in one of two modes, depending on return_mindist.
6011 Since return_mindist is composed of two settings we must first set
6012 perpendicular_only = (return_mindist >= 10);
6013 return_mindist -= 10 * perpendicular_only;
6014 That is, if 10 was added it means perpendicular_only is set and then the 10 is
6015 removed. We now consider what is left of return_mindist:
6016 (1) return_mindist == 0:
6017 We expect each segment to have its dist variable set to a minimum distance,
6018 and if the point is within this distance from the line then we return true;
6019 otherwise we return false. If the segments have not set their distances then
6020 it will have been initialized at 0 and only a point on the line will return true.
6021 If perpendicular_only we ignore a point that is within the distance of the
6022 linesegment endpoints but project onto the extension of the line (i.e., it is
6023 "outside" the extent of the line). We return false in that case.
6024 (2) return_mindist != 0:
6025 Return the minimum distance via dist_min. In addition, if > 1:
6026 If == 2 we also return the coordinate of nearest point via x_near, y_near.
6027 If == 3 we instead return segment number and point number (fractional) of that point via x_near, y_near.
6028 The function will always return true, except if perpendicular_only is set: then we
6029 return false if the point projects onto the extension of the line (i.e., it is "outside"
6030 the extent of the line). */
6031
6032 if (return_mindist >= 10) { /* Exclude (or flag) circular region surrounding line endpoints */
6033 perpendicular_only = true;
6034 return_mindist -= 10;
6035 }
6036 gmt_geo_to_cart (P, lat, lon, GMT, true); /* Our point to test is now GMT */
6037
6038 if (S->n_rows <= 0) return (false); /* Empty ; skip */
6039
6040 /* Find nearest point on this line */
6041
6042 if (return_mindist) SH->dist = 0.0; /* Explicitly set dist to zero so the shortest distance can be found */
6043
6044 for (row = 0; row < S->n_rows; row++) { /* loop over nodes on current line */
6045 d = gmt_distance (P, lon, lat, S->data[GMT_X][row], S->data[GMT_Y][row]); /* Distance between our point and row'th node on seg'th line */
6046 if (return_mindist && d < (*dist_min)) { /* Update minimum distance */
6047 *dist_min = d;
6048 if (return_mindist == 2) *x_near = S->data[GMT_X][row], *y_near = S->data[GMT_Y][row]; /* Also update (x,y) of nearest point on the line */
6049 else if (return_mindist == 3) *x_near = (double)seg, *y_near = (double)row; /* Also update (seg, pt) of nearest point on the line */
6050 }
6051 interior = (row > 0 && row < (S->n_rows - 1)); /* Only false if we are processing one of the end points */
6052 if (d <= SH->dist && (interior || !perpendicular_only)) return (true); /* Node inside the critical distance; we are done */
6053 }
6054
6055 if (S->n_rows < 2) return (false); /* 1-point "line" is a point; skip segment check */
6056
6057 /* If we get here we must check for intermediate points along the great circle lines between segment nodes.*/
6058
6059 if (return_mindist) /* Cosine of the great circle distance we are checking for. 2 ensures failure to be closer */
6060 cos_dist = 2.0;
6061 else if (P->current.map.dist[GMT_MAP_DIST].arc) /* Used angular distance measure */
6062 cos_dist = cosd (SH->dist / P->current.map.dist[GMT_MAP_DIST].scale);
6063 else /* Used distance units (e.g., meter, km). Conv to meters, then to degrees */
6064 cos_dist = cosd ((SH->dist / P->current.map.dist[GMT_MAP_DIST].scale) / P->current.proj.DIST_M_PR_DEG);
6065 gmt_geo_to_cart (P, S->data[GMT_Y][0], S->data[GMT_X][0], B, true); /* 3-D vector of end of last segment */
6066
6067 for (row = 1, within = false; row < S->n_rows; row++) { /* loop over great circle segments on current line */
6068 gmt_M_memcpy (A, B, 3, double); /* End of last segment is start of new segment */
6069 gmt_geo_to_cart (P, S->data[GMT_Y][row], S->data[GMT_X][row], B, true); /* 3-D vector of end of this segment */
6070 if (gmtlib_great_circle_intersection (P, A, B, GMT, X, &cx_dist)) continue; /* X not between A and B */
6071 if (return_mindist) { /* Get lon, lat of X, calculate distance, and update min_dist if needed */
6072 gmt_cart_to_geo (P, &xlat, &xlon, X, true);
6073 d = gmt_distance (P, xlon, xlat, lon, lat); /* Distance between our point and closest perpendicular point on seg'th line */
6074 if (d < (*dist_min)) { /* Update minimum distance */
6075 *dist_min = d;
6076 if (return_mindist == 2) { *x_near = xlon; *y_near = xlat;} /* Also update (x,y) of nearest point on the line */
6077 else if (return_mindist == 3) { /* Also update (seg, pt) of nearest point on the line */
6078 *x_near = (double)seg;
6079 prev_row = row - 1;
6080 dist_AB = gmt_distance (P, S->data[GMT_X][prev_row], S->data[GMT_Y][prev_row], S->data[GMT_X][row], S->data[GMT_Y][row]);
6081 fraction = (dist_AB > 0.0) ? gmt_distance (P, S->data[GMT_X][prev_row], S->data[GMT_Y][prev_row], xlon, xlat) / dist_AB : 0.0;
6082 *y_near = (double)prev_row + fraction;
6083 }
6084 within = true; /* Found at least one segment with a valid inside distance */
6085 }
6086 }
6087 if (cx_dist >= cos_dist) return (true); /* X is on the A-B extension AND within specified distance */
6088 }
6089
6090 if (return_mindist && *dist_min < GMT_CONV8_LIMIT) *dist_min = 0.0;
6091
6092 return (within); /* All tests failed, we are not close to the line(s), or we return a mindist (see comments above) */
6093 }
6094
6095 /*! . */
gmtmap_near_lines_spherical(struct GMT_CTRL * P,double lon,double lat,struct GMT_DATATABLE * T,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)6096 GMT_LOCAL bool gmtmap_near_lines_spherical (struct GMT_CTRL *P, double lon, double lat, struct GMT_DATATABLE *T, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
6097 uint64_t seg;
6098 int mode = return_mindist, status;
6099 bool OK = false;
6100 if (mode >= 10) mode -= 10; /* Exclude (or flag) circular region surrounding line endpoints */
6101 if (mode) *dist_min = DBL_MAX; /* Want to find the minimum distance so init to huge */
6102
6103 for (seg = 0; seg < T->n_segments; seg++) { /* Loop over each line segment */
6104 status = gmtmap_near_a_line_spherical (P, lon, lat, seg, T->segment[seg], return_mindist, dist_min, x_near, y_near);
6105 if (status) { /* Got a min distance or satisfied the min dist requirement */
6106 if (!return_mindist) return (true); /* Done, we are within distance of one of the lines */
6107 OK = true;
6108 }
6109 }
6110 return (OK);
6111 }
6112
6113 /*! . */
gmtmap_init_three_D(struct GMT_CTRL * GMT)6114 GMT_LOCAL int gmtmap_init_three_D (struct GMT_CTRL *GMT) {
6115 unsigned int i;
6116 bool easy, positive;
6117 double x, y, zmin = 0.0, zmax = 0.0, z_range;
6118
6119 GMT->current.proj.three_D = (GMT->current.proj.z_project.view_azimuth != 180.0 || GMT->current.proj.z_project.view_elevation != 90.0);
6120 GMT->current.proj.scale[GMT_Z] = GMT->current.proj.z_pars[0];
6121 GMT->current.proj.xyz_pos[GMT_Z] = (GMT->current.proj.scale[GMT_Z] >= 0.0); /* Increase z up or not */
6122 /* z_level == DBL_MAX is signaling that it was not set by the user. In that case we change it to the lower z level */
6123 if (GMT->current.proj.z_level == DBL_MAX) GMT->current.proj.z_level = (GMT->current.proj.xyz_pos[GMT_Z]) ? GMT->common.R.wesn[ZLO] : GMT->common.R.wesn[ZHI];
6124
6125 switch (GMT->current.proj.xyz_projection[GMT_Z]%3) { /* Modulo 3 so that GMT_TIME (3) maps to GMT_LINEAR (0) */
6126 case GMT_LINEAR: /* Regular scaling */
6127 zmin = (GMT->current.proj.xyz_pos[GMT_Z]) ? GMT->common.R.wesn[ZLO] : GMT->common.R.wesn[ZHI];
6128 zmax = (GMT->current.proj.xyz_pos[GMT_Z]) ? GMT->common.R.wesn[ZHI] : GMT->common.R.wesn[ZLO];
6129 GMT->current.proj.fwd_z = &gmtlib_translin;
6130 GMT->current.proj.inv_z = &gmtlib_itranslin;
6131 break;
6132 case GMT_LOG10: /* Log10 transformation */
6133 if (GMT->common.R.wesn[ZLO] <= 0.0 || GMT->common.R.wesn[ZHI] <= 0.0) {
6134 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Option -Jz -JZ: limits must be positive for log10 projection\n");
6135 return GMT_PROJECTION_ERROR;
6136 }
6137 zmin = (GMT->current.proj.xyz_pos[GMT_Z]) ? d_log10 (GMT, GMT->common.R.wesn[ZLO]) : d_log10 (GMT, GMT->common.R.wesn[ZHI]);
6138 zmax = (GMT->current.proj.xyz_pos[GMT_Z]) ? d_log10 (GMT, GMT->common.R.wesn[ZHI]) : d_log10 (GMT, GMT->common.R.wesn[ZLO]);
6139 GMT->current.proj.fwd_z = &gmtproj_translog10;
6140 GMT->current.proj.inv_z = &gmtproj_itranslog10;
6141 break;
6142 case GMT_POW: /* x^y transformation */
6143 GMT->current.proj.xyz_pow[GMT_Z] = GMT->current.proj.z_pars[1];
6144 GMT->current.proj.xyz_ipow[GMT_Z] = 1.0 / GMT->current.proj.z_pars[1];
6145 positive = !((GMT->current.proj.xyz_pos[GMT_Z] + (GMT->current.proj.xyz_pow[GMT_Z] > 0.0)) % 2);
6146 zmin = (positive) ? pow (GMT->common.R.wesn[ZLO], GMT->current.proj.xyz_pow[GMT_Z]) : pow (GMT->common.R.wesn[ZHI], GMT->current.proj.xyz_pow[GMT_Z]);
6147 zmax = (positive) ? pow (GMT->common.R.wesn[ZHI], GMT->current.proj.xyz_pow[GMT_Z]) : pow (GMT->common.R.wesn[ZLO], GMT->current.proj.xyz_pow[GMT_Z]);
6148 GMT->current.proj.fwd_z = &gmtproj_transpowz;
6149 GMT->current.proj.inv_z = &gmtproj_itranspowz;
6150 }
6151 z_range = zmax - zmin;
6152 if (z_range == 0.0 && GMT->current.proj.compute_scale[GMT_Z])
6153 GMT->current.proj.scale[GMT_Z] = 0.0; /* No range given, just flat projected map */
6154 else if (GMT->current.proj.compute_scale[GMT_Z])
6155 GMT->current.proj.scale[GMT_Z] /= fabs (z_range);
6156 GMT->current.proj.zmax = z_range * GMT->current.proj.scale[GMT_Z];
6157 GMT->current.proj.origin[GMT_Z] = -zmin * GMT->current.proj.scale[GMT_Z];
6158
6159 if (GMT->current.proj.z_project.view_azimuth >= 360.0) GMT->current.proj.z_project.view_azimuth -= 360.0;
6160 if (GMT->current.proj.z_project.view_azimuth < 0.0) GMT->current.proj.z_project.view_azimuth += 360.0;
6161 GMT->current.proj.z_project.quadrant = urint (floor (GMT->current.proj.z_project.view_azimuth / 90.0)) + 1;
6162 sincosd (GMT->current.proj.z_project.view_azimuth, &GMT->current.proj.z_project.sin_az, &GMT->current.proj.z_project.cos_az);
6163 sincosd (GMT->current.proj.z_project.view_elevation, &GMT->current.proj.z_project.sin_el, &GMT->current.proj.z_project.cos_el);
6164
6165 /* Determine min/max y of plot */
6166
6167 /* easy = true means we can use 4 corner points to find min x and y, false
6168 means we must search along wesn perimeter the hard way */
6169
6170 switch (GMT->current.proj.projection_GMT) {
6171 case GMT_LINEAR:
6172 case GMT_MERCATOR:
6173 case GMT_OBLIQUE_MERC:
6174 case GMT_CYL_EQ:
6175 case GMT_CYL_EQDIST:
6176 case GMT_CYL_STEREO:
6177 case GMT_MILLER:
6178 easy = true;
6179 break;
6180 case GMT_POLAR:
6181 case GMT_LAMBERT:
6182 case GMT_TM:
6183 case GMT_UTM:
6184 case GMT_CASSINI:
6185 case GMT_STEREO:
6186 case GMT_ALBERS:
6187 case GMT_ECONIC:
6188 case GMT_POLYCONIC:
6189 case GMT_LAMB_AZ_EQ:
6190 case GMT_ORTHO:
6191 case GMT_GENPER:
6192 case GMT_GNOMONIC:
6193 case GMT_AZ_EQDIST:
6194 case GMT_SINUSOIDAL:
6195 case GMT_MOLLWEIDE:
6196 case GMT_HAMMER:
6197 case GMT_VANGRINTEN:
6198 case GMT_WINKEL:
6199 case GMT_ECKERT4:
6200 case GMT_ECKERT6:
6201 case GMT_ROBINSON:
6202 easy = GMT->common.R.oblique;
6203 break;
6204 default:
6205 easy = false;
6206 break;
6207 }
6208
6209 if (!GMT->current.proj.three_D) easy = true;
6210
6211 GMT->current.proj.z_project.xmin = GMT->current.proj.z_project.ymin = DBL_MAX;
6212 GMT->current.proj.z_project.xmax = GMT->current.proj.z_project.ymax = -DBL_MAX;
6213
6214 if (easy) {
6215 double xx[4], yy[4];
6216
6217 xx[0] = xx[3] = GMT->current.proj.rect[XLO]; xx[1] = xx[2] = GMT->current.proj.rect[XHI];
6218 yy[0] = yy[1] = GMT->current.proj.rect[YLO]; yy[2] = yy[3] = GMT->current.proj.rect[YHI];
6219
6220 for (i = 0; i < 4; i++) {
6221 gmt_xy_to_geo (GMT, &GMT->current.proj.z_project.corner_x[i], &GMT->current.proj.z_project.corner_y[i], xx[i], yy[i]);
6222 gmt_xyz_to_xy (GMT, xx[i], yy[i], gmt_z_to_zz(GMT, GMT->common.R.wesn[ZLO]), &x, &y);
6223 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6224 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6225 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6226 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6227 gmt_xyz_to_xy (GMT, xx[i], yy[i], gmt_z_to_zz(GMT, GMT->common.R.wesn[ZHI]), &x, &y);
6228 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6229 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6230 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6231 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6232 }
6233 }
6234 else if (GMT->current.proj.r > 0.0) { /* Do not think the next four lines mean anything in this case, just copied from the general case */
6235 GMT->current.proj.z_project.corner_x[0] = GMT->current.proj.z_project.corner_x[3] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
6236 GMT->current.proj.z_project.corner_x[1] = GMT->current.proj.z_project.corner_x[2] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO];
6237 GMT->current.proj.z_project.corner_y[0] = GMT->current.proj.z_project.corner_y[1] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
6238 GMT->current.proj.z_project.corner_y[2] = GMT->current.proj.z_project.corner_y[3] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO];
6239 for (i = 0; i < 360; i++) { /* Go around the circle */
6240 sincosd (i * 1.0, &y, &x);
6241 gmt_xyz_to_xy (GMT, GMT->current.proj.r * (1.0 + x), GMT->current.proj.r * (1.0 + y), gmt_z_to_zz(GMT, GMT->common.R.wesn[ZLO]), &x, &y);
6242 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6243 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6244 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6245 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6246 gmt_xyz_to_xy (GMT, GMT->current.proj.r * (1.0 + x), GMT->current.proj.r * (1.0 + y), gmt_z_to_zz(GMT, GMT->common.R.wesn[ZHI]), &x, &y);
6247 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6248 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6249 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6250 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6251 }
6252 }
6253 else {
6254 GMT->current.proj.z_project.corner_x[0] = GMT->current.proj.z_project.corner_x[3] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI];
6255 GMT->current.proj.z_project.corner_x[1] = GMT->current.proj.z_project.corner_x[2] = (GMT->current.proj.xyz_pos[GMT_X]) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO];
6256 GMT->current.proj.z_project.corner_y[0] = GMT->current.proj.z_project.corner_y[1] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YLO] : GMT->common.R.wesn[YHI];
6257 GMT->current.proj.z_project.corner_y[2] = GMT->current.proj.z_project.corner_y[3] = (GMT->current.proj.xyz_pos[GMT_Y]) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO];
6258 for (i = 0; i < GMT->current.map.n_lon_nodes; i++) { /* S and N */
6259 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], GMT->common.R.wesn[ZLO], &x, &y);
6260 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6261 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6262 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6263 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6264 if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6265 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], GMT->common.R.wesn[ZHI], &x, &y);
6266 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6267 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6268 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6269 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6270 }
6271 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], GMT->common.R.wesn[ZLO], &x, &y);
6272 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6273 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6274 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6275 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6276 if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6277 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], GMT->common.R.wesn[ZHI], &x, &y);
6278 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6279 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6280 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6281 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6282 }
6283 }
6284 for (i = 0; i < GMT->current.map.n_lat_nodes; i++) { /* W and E */
6285 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZLO], &x, &y);
6286 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6287 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6288 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6289 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6290 if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6291 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZHI], &x, &y);
6292 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6293 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6294 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6295 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6296 }
6297 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZLO], &x, &y);
6298 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6299 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6300 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6301 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6302 if (GMT->common.R.wesn[ZHI] != GMT->common.R.wesn[ZLO]) {
6303 gmt_geoz_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, GMT->common.R.wesn[ZHI], &x, &y);
6304 GMT->current.proj.z_project.ymin = MIN (GMT->current.proj.z_project.ymin, y);
6305 GMT->current.proj.z_project.ymax = MAX (GMT->current.proj.z_project.ymax, y);
6306 GMT->current.proj.z_project.xmin = MIN (GMT->current.proj.z_project.xmin, x);
6307 GMT->current.proj.z_project.xmax = MAX (GMT->current.proj.z_project.xmax, x);
6308 }
6309 }
6310 }
6311
6312 GMT->current.proj.z_project.face[0] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 2) ? 0 : 1;
6313 GMT->current.proj.z_project.face[1] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 4) ? 2 : 3;
6314 GMT->current.proj.z_project.face[2] = (GMT->current.proj.z_project.view_elevation >= 0.0) ? 4 : 5;
6315 GMT->current.proj.z_project.draw[0] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 4) ? true : false;
6316 GMT->current.proj.z_project.draw[1] = (GMT->current.proj.z_project.quadrant == 3 || GMT->current.proj.z_project.quadrant == 4) ? true : false;
6317 GMT->current.proj.z_project.draw[2] = (GMT->current.proj.z_project.quadrant == 2 || GMT->current.proj.z_project.quadrant == 3) ? true : false;
6318 GMT->current.proj.z_project.draw[3] = (GMT->current.proj.z_project.quadrant == 1 || GMT->current.proj.z_project.quadrant == 2) ? true : false;
6319 GMT->current.proj.z_project.sign[0] = GMT->current.proj.z_project.sign[3] = -1.0;
6320 GMT->current.proj.z_project.sign[1] = GMT->current.proj.z_project.sign[2] = 1.0;
6321 GMT->current.proj.z_project.z_axis = (GMT->current.proj.z_project.quadrant%2) ? GMT->current.proj.z_project.quadrant : GMT->current.proj.z_project.quadrant - 2;
6322
6323 if (GMT->current.proj.z_project.fixed) {
6324 if (!GMT->current.proj.z_project.world_given) { /* Pick center point of region */
6325 GMT->current.proj.z_project.world_x = (gmt_M_is_geographic (GMT, GMT_IN)) ? GMT->current.proj.central_meridian : 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);
6326 GMT->current.proj.z_project.world_y = 0.5 * (GMT->common.R.wesn[YLO] + GMT->common.R.wesn[YHI]);
6327 GMT->current.proj.z_project.world_z = GMT->current.proj.z_level;
6328 }
6329 gmt_geoz_to_xy (GMT, GMT->current.proj.z_project.world_x, GMT->current.proj.z_project.world_y, GMT->current.proj.z_project.world_z, &x, &y);
6330 if (!GMT->current.proj.z_project.view_given) { /* Pick center of current page */
6331 GMT->current.proj.z_project.view_x = 0.5 * GMT->current.setting.ps_page_size[0] * GMT->session.u2u[GMT_PT][GMT_INCH];
6332 GMT->current.proj.z_project.view_y = 0.5 * GMT->current.setting.ps_page_size[1] * GMT->session.u2u[GMT_PT][GMT_INCH];
6333 }
6334 GMT->current.proj.z_project.x_off = GMT->current.proj.z_project.view_x - x;
6335 GMT->current.proj.z_project.y_off = GMT->current.proj.z_project.view_y - y;
6336 }
6337 else {
6338 GMT->current.proj.z_project.x_off = -GMT->current.proj.z_project.xmin;
6339 GMT->current.proj.z_project.y_off = -GMT->current.proj.z_project.ymin;
6340 }
6341
6342 /* Adjust the xmin/xmax and ymin/ymax because of xoff and yoff */
6343 GMT->current.proj.z_project.xmin += GMT->current.proj.z_project.x_off;
6344 GMT->current.proj.z_project.xmax += GMT->current.proj.z_project.x_off;
6345 GMT->current.proj.z_project.ymin += GMT->current.proj.z_project.y_off;
6346 GMT->current.proj.z_project.ymax += GMT->current.proj.z_project.y_off;
6347
6348 return (GMT_NOERROR);
6349 }
6350
6351 /*! . */
gmtmap_geodesic_dist_cos(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)6352 GMT_LOCAL double gmtmap_geodesic_dist_cos (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
6353 /* Convenience function to get cosine instead */
6354 return (cosd (gmtmap_geodesic_dist_degree (GMT, lonS, latS, lonE, latE)));
6355 }
6356
6357 /*! . */
gmtlib_great_circle_dist_cos(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)6358 double gmtlib_great_circle_dist_cos (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
6359 /* Return cosine of great circle distance */
6360
6361 double sin_half_squared = gmtmap_haversine (GMT, lon1, lat1, lon2, lat2);
6362 return (1.0 - 2.0 * sin_half_squared); /* Convert sin^2 (half-angle) to cos (angle) */
6363 }
6364
6365 /*! . */
gmtmap_cartesian_dist_periodic(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)6366 GMT_LOCAL double gmtmap_cartesian_dist_periodic (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
6367 /* Calculates the good-old straight line distance in users units */
6368 double dx = x1 - x0, dy = y1 - y0;
6369 if (GMT->common.n.periodic[GMT_X] && (dx = fabs (dx)) > GMT->common.n.half_range[GMT_X]) dx = GMT->common.n.range[GMT_X] - dx;
6370 if (GMT->common.n.periodic[GMT_Y] && (dy = fabs (dy)) > GMT->common.n.half_range[GMT_Y]) dy = GMT->common.n.range[GMT_Y] - dy;
6371 return (hypot (dx, dy));
6372 }
6373
6374 /*! . */
gmtmap_set_distaz(struct GMT_CTRL * GMT,unsigned int mode,unsigned int type,char * unit_name)6375 GMT_LOCAL int gmtmap_set_distaz (struct GMT_CTRL *GMT, unsigned int mode, unsigned int type, char *unit_name) {
6376 /* Assigns pointers to the chosen distance and azimuth functions */
6377 char *type_name[3] = {"Map", "Contour", "Contour annotation"};
6378 char *aux[6] = {"no", "authalic", "conformal", "meridional", "geocentric", "parametric"};
6379 char *rad[5] = {"mean (R_1)", "authalic (R_2)", "volumetric (R_3)", "meridional", "quadratic"};
6380 int choice = (GMT->current.setting.proj_aux_latitude == GMT_LATSWAP_NONE) ? 0 : 1 + GMT->current.setting.proj_aux_latitude/2;
6381 GMT->current.map.dist[type].scale = 1.0; /* Default scale */
6382
6383 switch (mode) { /* Set pointers to distance functions */
6384 case GMT_CARTESIAN_DIST_PERIODIC: /* Cartesian 2-D x,y data but with one or two periodic dimensions */
6385 GMT->current.map.dist[type].func = &gmtmap_cartesian_dist_periodic;
6386 GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian;
6387 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian [periodic]\n", type_name[type]);
6388 break;
6389 case GMT_CARTESIAN_DIST: /* Cartesian 2-D x,y data */
6390 GMT->current.map.dist[type].func = &gmtlib_cartesian_dist;
6391 GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian;
6392 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian\n", type_name[type]);
6393 break;
6394 case GMT_CARTESIAN_DIST2: /* Cartesian 2-D x,y data, use r^2 instead of hypot */
6395 GMT->current.map.dist[type].func = &gmtmap_cartesian_dist2;
6396 GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian;
6397 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian\n", type_name[type]);
6398 break;
6399 case GMT_CARTESIAN_DIST_PROJ: /* Cartesian distance after projecting 2-D lon,lat data */
6400 GMT->current.map.dist[type].func = &gmtlib_cartesian_dist_proj;
6401 GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian_proj;
6402 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian after first projecting via -J\n", type_name[type]);
6403 break;
6404 case GMT_CARTESIAN_DIST_PROJ2: /* Cartesian distance after projecting 2-D lon,lat data, use r^2 instead of hypot */
6405 GMT->current.map.dist[type].func = &gmtmap_cartesian_dist_proj2;
6406 GMT->current.map.azimuth_func = &gmtmap_az_backaz_cartesian_proj;
6407 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Cartesian after first projecting via -J\n", type_name[type]);
6408 break;
6409 case GMT_DIST_M+GMT_FLATEARTH: /* 2-D lon, lat data, but scale to Cartesian flat earth in meter */
6410 GMT->current.map.dist[type].func = &gmtmap_flatearth_dist_meter;
6411 GMT->current.map.azimuth_func = &gmtmap_az_backaz_flatearth;
6412 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Flat Earth in %s\n", type_name[type], unit_name);
6413 break;
6414 case GMT_DIST_M+GMT_GREATCIRCLE: /* 2-D lon, lat data, use spherical distances in meter */
6415 GMT->current.map.dist[type].func = &gmt_great_circle_dist_meter;
6416 GMT->current.map.azimuth_func = &gmtmap_az_backaz_sphere;
6417 GMT->current.map.second_point = &gmtmap_translate_point_spherical;
6418 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using great circle approximation with %s auxiliary latitudes and %s radius = %.4f m, in %s.\n",
6419 type_name[type], aux[choice], rad[GMT->current.setting.proj_mean_radius], GMT->current.proj.mean_radius, unit_name);
6420 break;
6421 case GMT_DIST_M+GMT_GEODESIC: /* 2-D lon, lat data, use geodesic distances in meter */
6422 GMT->current.map.dist[type].func = GMT->current.map.geodesic_meter;
6423 GMT->current.map.azimuth_func = GMT->current.map.geodesic_az_backaz;
6424 GMT->current.map.second_point = &gmtmap_translate_point_geodesic;
6425 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using %s geodesics in %s\n", type_name[type], GEOD_TEXT[GMT->current.setting.proj_geodesic], unit_name);
6426 break;
6427 case GMT_DIST_DEG+GMT_FLATEARTH: /* 2-D lon, lat data, use Flat Earth distances in degrees */
6428 GMT->current.map.dist[type].func = gmtmap_flatearth_dist_degree;
6429 GMT->current.map.azimuth_func = &gmtmap_az_backaz_flatearth;
6430 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be Flat Earth in %s\n", type_name[type], unit_name);
6431 break;
6432 case GMT_DIST_DEG+GMT_GREATCIRCLE: /* 2-D lon, lat data, use spherical distances in degrees */
6433 GMT->current.map.dist[type].func = &gmtlib_great_circle_dist_degree;
6434 GMT->current.map.azimuth_func = &gmtmap_az_backaz_sphere;
6435 GMT->current.map.second_point = &gmtmap_translate_point_spherical;
6436 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using great circle approximation with %s auxiliary latitudes and return lengths in %s.\n", unit_name,
6437 type_name[type], aux[choice]);
6438 break;
6439 case GMT_DIST_DEG+GMT_GEODESIC: /* 2-D lon, lat data, use geodesic distances in degrees */
6440 GMT->current.map.dist[type].func = &gmtmap_geodesic_dist_degree;
6441 GMT->current.map.azimuth_func = GMT->current.map.geodesic_az_backaz;
6442 GMT->current.map.second_point = &gmtmap_translate_point_geodesic;
6443 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using geodesics in %s\n", type_name[type], unit_name);
6444 break;
6445 case GMT_DIST_COS+GMT_GREATCIRCLE: /* 2-D lon, lat data, and Green's function needs cosine of spherical distance */
6446 GMT->current.map.dist[type].func = &gmtlib_great_circle_dist_cos;
6447 GMT->current.map.azimuth_func = &gmtmap_az_backaz_sphere;
6448 GMT->current.map.second_point = &gmtmap_translate_point_spherical;
6449 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using great circle approximation with %s auxiliary latitudes and return cosine of spherical angles.\n",
6450 type_name[type], aux[choice]);
6451 break;
6452 case GMT_DIST_COS+GMT_GEODESIC: /* 2-D lon, lat data, and Green's function needs cosine of geodesic distance */
6453 GMT->current.map.dist[type].func = &gmtmap_geodesic_dist_cos;
6454 GMT->current.map.azimuth_func = GMT->current.map.geodesic_az_backaz;
6455 GMT->current.map.second_point = &gmtmap_translate_point_geodesic;
6456 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be using cosine of geodesic angle\n", type_name[type]);
6457 break;
6458 case GMT_DIST_M+GMT_LOXODROME: /* 2-D lon, lat data, but measure distance along rhumblines in meter */
6459 GMT->current.map.dist[type].func = &gmtmap_loxodrome_dist_meter;
6460 GMT->current.map.azimuth_func = &gmtmap_az_backaz_loxodrome;
6461 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be along loxodromes in meters\n", type_name[type]);
6462 break;
6463 case GMT_DIST_DEG+GMT_LOXODROME: /* 2-D lon, lat data, but measure distance along rhumblines in degrees */
6464 GMT->current.map.dist[type].func = &gmtmap_loxodrome_dist_degree;
6465 GMT->current.map.azimuth_func = &gmtmap_az_backaz_loxodrome;
6466 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s distance calculation will be along loxodromes with %s auxiliary latitudes and return lengths in degrees.\n",
6467 type_name[type], aux[choice]);
6468 break;
6469 default: /* Cannot happen unless we make a bug */
6470 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Mode (=%d) for distance function is unknown. Must be bug.\n", mode);
6471 return (GMT_PROJECTION_ERROR);
6472 break;
6473 }
6474 if (type > 0) return (GMT_NOERROR); /* Contour-related assignments end here */
6475
6476 /* Mapping only */
6477 if (mode == GMT_CARTESIAN_DIST || mode == GMT_CARTESIAN_DIST2) { /* Cartesian data */
6478 GMT->current.map.near_lines_func = &gmtmap_near_lines_cartesian;
6479 GMT->current.map.near_a_line_func = &gmtmap_near_a_line_cartesian;
6480 GMT->current.map.near_point_func = &gmtmap_near_a_point_cartesian;
6481 }
6482 else { /* Geographic data */
6483 GMT->current.map.near_lines_func = &gmtmap_near_lines_spherical;
6484 GMT->current.map.near_a_line_func = &gmtmap_near_a_line_spherical;
6485 GMT->current.map.near_point_func = &gmtmap_near_a_point_spherical;
6486 }
6487 return (GMT_NOERROR);
6488 }
6489
6490
6491 #if 0
6492 void GMT_set_geocentric (struct GMT_CTRL *GMT, bool notify)
6493 { /* The idea is to call this from sample1d/grdtrack */
6494 /* Set up ellipsoid parameters for spherical approximation with geocentric parameters */
6495
6496 GMT->current.setting.proj_aux_latitude = GMT_LATSWAP_G2O; /* Geocentric/Geodetic conversion */
6497 GMT->current.setting.proj_mean_radius = GMT_RADIUS_MERIDIONAL;
6498 gmtlib_init_ellipsoid (GMT);
6499 }
6500 #endif
6501
6502 /* GMT_dateline_clip simply clips a polygon against the dateline and results in two polygons in L */
6503
6504 /*! . */
gmtlib_set_oblique_pole_and_origin(struct GMT_CTRL * GMT,double plon,double plat,double olon,double olat)6505 void gmtlib_set_oblique_pole_and_origin (struct GMT_CTRL *GMT, double plon, double plat, double olon, double olat) {
6506 /* The set quantities are used in gmtproj_obl and gmtlib_iobl */
6507 /* Get forward pole and origin vectors FP, FC */
6508 double P[3];
6509 gmt_geo_to_cart (GMT, plat, plon, GMT->current.proj.o_FP, true); /* Set forward Cartesian pole o_FP */
6510 gmt_geo_to_cart (GMT, olat, olon, P, true); /* P points to the origin */
6511 gmt_cross3v (GMT, GMT->current.proj.o_FP, P, GMT->current.proj.o_FC); /* Set forward Cartesian center o_FC */
6512 gmt_normalize3v (GMT, GMT->current.proj.o_FC);
6513
6514 /* Get inverse pole and origin vectors FP, FC */
6515 gmtproj_obl (GMT, 0.0, M_PI_2, &plon, &plat);
6516 gmt_geo_to_cart (GMT, plat, plon, GMT->current.proj.o_IP, false); /* Set inverse Cartesian pole o_IP */
6517 gmtproj_obl (GMT, 0.0, 0.0, &olon, &olat);
6518 gmt_geo_to_cart (GMT, olat, olon, P, false); /* P points to origin */
6519 gmt_cross3v (GMT, GMT->current.proj.o_IP, P, GMT->current.proj.o_IC); /* Set inverse Cartesian center o_FC */
6520 gmt_normalize3v (GMT, GMT->current.proj.o_IC);
6521 }
6522
6523 /*! . */
gmt_cart_outside(struct GMT_CTRL * GMT,double x,double y)6524 bool gmt_cart_outside (struct GMT_CTRL *GMT, double x, double y) {
6525 /* Expects x,y to be projected and comparing with rectangular projected domain */
6526 /* Note PW: 8-20-2014: The on_border_is_outside tests had GMT_CONV4_LIMIT instead of GMT_CONV8_LIMIT. Trying the latter */
6527 if (GMT->current.map.on_border_is_outside && fabs (x - GMT->current.proj.rect[XLO]) < GMT_CONV8_LIMIT)
6528 GMT->current.map.this_x_status = -1;
6529 else if (GMT->current.map.on_border_is_outside && fabs (x - GMT->current.proj.rect[XHI]) < GMT_CONV8_LIMIT)
6530 GMT->current.map.this_x_status = 1;
6531 else if (x < GMT->current.proj.rect[XLO] - GMT_CONV8_LIMIT)
6532 GMT->current.map.this_x_status = -2;
6533 else if (x > GMT->current.proj.rect[XHI] + GMT_CONV8_LIMIT)
6534 GMT->current.map.this_x_status = 2;
6535 else
6536 GMT->current.map.this_x_status = 0;
6537
6538 if (GMT->current.map.on_border_is_outside && fabs (y -GMT->current.proj.rect[YLO]) < GMT_CONV8_LIMIT)
6539 GMT->current.map.this_y_status = -1;
6540 else if (GMT->current.map.on_border_is_outside && fabs (y - GMT->current.proj.rect[YHI]) < GMT_CONV8_LIMIT)
6541 GMT->current.map.this_y_status = 1;
6542 else if (y < GMT->current.proj.rect[YLO] - GMT_CONV8_LIMIT)
6543 GMT->current.map.this_y_status = -2;
6544 else if (y > GMT->current.proj.rect[YHI] + GMT_CONV8_LIMIT)
6545 GMT->current.map.this_y_status = 2;
6546 else
6547 GMT->current.map.this_y_status = 0;
6548
6549 return ((GMT->current.map.this_x_status != 0 || GMT->current.map.this_y_status != 0) ? true : false);
6550 }
6551
6552 /*! . */
gmt_clip_to_map(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t np,double ** x,double ** y)6553 uint64_t gmt_clip_to_map (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t np, double **x, double **y) {
6554 /* This routine makes sure that all points are either inside or on the map boundary
6555 * and returns the number of points to be used for plotting (in x,y units) */
6556
6557 uint64_t i, out, n;
6558 uint64_t total_nx = 0;
6559 int64_t out_x, out_y, np2;
6560 bool polygon;
6561 double *xx = NULL, *yy = NULL;
6562
6563 /* First check for trivial cases: All points outside or all points inside */
6564
6565 for (i = out = out_x = out_y = 0; i < np; i++) {
6566 (void) gmt_map_outside (GMT, lon[i], lat[i]);
6567 out_x += GMT->current.map.this_x_status; /* Completely left of west gives -2 * np, right of east gives + 2 * np */
6568 out_y += GMT->current.map.this_y_status; /* Completely below south gives -2 * np, above north gives + 2 * np */
6569 out += (abs (GMT->current.map.this_x_status) == 2 || abs (GMT->current.map.this_y_status) == 2);
6570 }
6571 if (out == 0) { /* All points are inside map boundary; no clipping required */
6572 gmt_M_malloc2 (GMT, xx, yy, np, NULL, double);
6573 for (i = 0; i < np; i++) gmt_geo_to_xy (GMT, lon[i], lat[i], &xx[i], &yy[i]);
6574 *x = xx; *y = yy; n = np;
6575 }
6576 else if (out == np) { /* All points are outside map boundary */
6577 np2 = 2 * np;
6578 if (int64_abs (out_x) == np2 || int64_abs (out_y) == np2) /* All points safely outside the region, no part of polygon survives */
6579 n = 0;
6580 else { /* All points are outside, but they are not just to one side so lines _may_ intersect the region */
6581 n = (*GMT->current.map.clip) (GMT, lon, lat, np, x, y, &total_nx);
6582 polygon = (np > 2 && !gmt_polygon_is_open (GMT, lon, lat, np)); /* The following can only be used on closed polygons */
6583 /* Polygons that completely contains the -R region will not generate crossings, just duplicate -R box */
6584 if (polygon && n > 0 && total_nx == 0) { /* No crossings and all points outside means one of two things: */
6585 /* Either the polygon contains portions of the -R region including corners or it does not. We pick the corners and check for insidedness: */
6586 bool ok = false;
6587 if (gmt_non_zero_winding (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], lon, lat, np)) ok = true; /* true if inside */
6588 if (!ok && gmt_non_zero_winding (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], lon, lat, np)) ok = true; /* true if inside */
6589 if (!ok && gmt_non_zero_winding (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YHI], lon, lat, np)) ok = true; /* true if inside */
6590 if (!ok && gmt_non_zero_winding (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], lon, lat, np)) ok = true; /* true if inside */
6591 if (!ok) {
6592 /* Polygon does NOT contain the region and we delete it */
6593 n = 0;
6594 gmt_M_free (GMT, *x);
6595 gmt_M_free (GMT, *y);
6596 }
6597 /* Otherwise the polygon completely contains -R and we pass it along */
6598 }
6599 else if (GMT->common.R.oblique && GMT->current.proj.projection_GMT == GMT_AZ_EQDIST && n <= 5 && !strncmp (GMT->init.module_name, "pscoast", 7U)) {
6600 /* Special check for -JE where a coastline block is completely outside yet fully surrounds the rectangular -R -JE...r region.
6601 This results in a rectangular closed polygon after the clipping. */
6602 n = 0;
6603 gmt_M_free (GMT, *x);
6604 gmt_M_free (GMT, *y);
6605 }
6606 }
6607 }
6608 else /* Mixed case so we must clip the polygon */
6609 n = (*GMT->current.map.clip) (GMT, lon, lat, np, x, y, &total_nx);
6610
6611 return (n);
6612 }
6613
6614 /*! . */
gmt_split_poly_at_dateline(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S,struct GMT_DATASEGMENT *** Lout)6615 unsigned int gmt_split_poly_at_dateline (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S, struct GMT_DATASEGMENT ***Lout) {
6616 int side, j, np, cross = 0;
6617 unsigned int smode = (S->text) ? GMT_WITH_STRINGS : GMT_NO_STRINGS;
6618 uint64_t row, m;
6619 size_t n_alloc = 0;
6620 char label[GMT_LEN256] = {""}, *part = "EW";
6621 double xx[2], yy[2];
6622 struct GMT_DATASEGMENT **L = NULL;
6623 struct GMT_DATASEGMENT_HIDDEN *SH = gmt_get_DS_hidden (S);
6624 bool (*inside[2]) (double, double);
6625 bool (*outside[2]) (double, double);
6626
6627
6628 inside[0] = gmtmap_inside_upper_boundary; outside[0] = gmtmap_outside_upper_boundary;
6629 inside[1] = gmtmap_inside_lower_boundary; outside[1] = gmtmap_outside_lower_boundary;
6630 L = gmt_M_memory (GMT, NULL, 2, struct GMT_DATASEGMENT *); /* The two polygons */
6631
6632 for (row = 0; row < S->n_rows; row++) gmt_lon_range_adjust (GMT_IS_0_TO_P360_RANGE, &S->data[GMT_X][row]); /* First enforce 0 <= lon < 360 so we don't have to check again */
6633
6634 for (side = 0; side < 2; side++) { /* Do it twice to get two truncated polygons */
6635 if (S->n_rows == 0) continue; /* Nothing! */
6636 n_alloc = lrint (1.05*S->n_rows + 5); /* Anticipate just a few crossings (5%)+5, allocate more later if needed */
6637 L[side] = GMT_Alloc_Segment (GMT->parent, smode, n_alloc, S->n_columns, NULL, NULL);
6638 m = 0; /* Start with nuthin' */
6639
6640 /* Must ensure we copy the very first point if it is left of the Dateline */
6641 if (S->data[GMT_X][0] < 180.0) { L[side]->data[GMT_X][0] = S->data[GMT_X][0]; L[side]->data[GMT_Y][0] = S->data[GMT_Y][0]; } /* First point is inside; add it */
6642 for (row = 1; row < S->n_rows; row++) { /* For each line segment */
6643 np = gmtmap_clip_we (S->data[GMT_X][row-1], S->data[GMT_Y][row-1], S->data[GMT_X][row], S->data[GMT_Y][row], xx, yy, 180.0, inside[side], outside[side], &cross); /* Returns 0, 1, or 2 points */
6644 for (j = 0; j < np; j++) { /* Add the np returned points to the new clipped polygon path */
6645 if (m == n_alloc) L[side] = GMT_Alloc_Segment (GMT->parent, smode, n_alloc << 2, S->n_columns, NULL, L[side]);
6646 L[side]->data[GMT_X][m] = xx[j]; L[side]->data[GMT_Y][m] = yy[j]; m++;
6647 }
6648 }
6649 if (m > 2 && gmt_polygon_is_open (GMT, L[side]->data[GMT_X], L[side]->data[GMT_Y], m)) { /* Do we need to explicitly close this clipped polygon? */
6650 if (m == n_alloc) L[side] = GMT_Alloc_Segment (GMT->parent, smode, n_alloc << 2, S->n_columns, NULL, L[side]);
6651 L[side]->data[GMT_X][m] = L[side]->data[GMT_X][0]; L[side]->data[GMT_Y][m] = L[side]->data[GMT_Y][0]; m++; /* Yes. */
6652 }
6653 if (m != n_alloc) L[side] = GMT_Alloc_Segment (GMT->parent, smode, m, S->n_columns, NULL, L[side]);
6654 L[side]->n_rows = m;
6655 if (S->label) {
6656 snprintf (label, GMT_LEN256, "%s part %c", S->label, part[side]);
6657 L[side]->label = strdup (label);
6658 }
6659 if (S->header) L[side]->header = strdup (S->header);
6660 if (SH->ogr) gmt_duplicate_ogr_seg (GMT, L[side], S);
6661 }
6662 SH = gmt_get_DS_hidden (L[0]);
6663 SH->range = GMT_IS_0_TO_P360;
6664 SH = gmt_get_DS_hidden (L[1]);
6665 SH->range = GMT_IS_M360_TO_0_RANGE;
6666 *Lout = L;
6667 return (2);
6668 }
6669
6670 /*! . */
gmtlib_get_point_from_r_az(struct GMT_CTRL * GMT,double lon0,double lat0,double r,double azim,double * lon1,double * lat1)6671 void gmtlib_get_point_from_r_az (struct GMT_CTRL *GMT, double lon0, double lat0, double r, double azim, double *lon1, double *lat1) {
6672 /* Given point (lon0, lat0), find coordinates of a point r degrees away in the azim direction */
6673
6674 double sinr, cosr, sinaz, cosaz, siny, cosy;
6675 gmt_M_unused(GMT);
6676
6677 sincosd (azim, &sinaz, &cosaz);
6678 sincosd (r, &sinr, &cosr);
6679 sincosd (lat0, &siny, &cosy);
6680
6681 *lon1 = lon0 + atan2d (sinr * sinaz, (cosy * cosr - siny * sinr * cosaz));
6682 *lat1 = d_asind (siny * cosr + cosy * sinr * cosaz);
6683 }
6684
6685 /*! . */
gmt_az_backaz(struct GMT_CTRL * GMT,double lonE,double latE,double lonS,double latS,bool baz)6686 double gmt_az_backaz (struct GMT_CTRL *GMT, double lonE, double latE, double lonS, double latS, bool baz) {
6687 return (GMT->current.map.azimuth_func (GMT, lonE, latE, lonS, latS, baz));
6688 }
6689
gmtmap_auto_time_increment(double inc,char * unit)6690 GMT_LOCAL double gmtmap_auto_time_increment (double inc, char *unit) {
6691 /* Given the first guess interval inc, determine closest time unit and return
6692 * the number of seconds in that unit and its code */
6693 int k, kk = GMT_NOTSET;
6694 double f;
6695 static char units[6] = {'S', 'M', 'H', 'D', 'O', 'Y'};
6696 static double incs[6] = {1.0, GMT_MIN2SEC_F, GMT_HR2SEC_F, GMT_DAY2SEC_F, GMT_MON2SEC_F, GMT_YR2SEC_F};
6697 for (k = 5; kk == GMT_NOTSET && k >= 0; k--) { /* Find the largest time unit that is smaller than guess interval */
6698 f = inc / incs[k];
6699 if (irint (f) >= 1)
6700 kk = k;
6701 }
6702 if (kk == GMT_NOTSET) kk = 0; /* Safety valve */
6703 *unit = units[kk];
6704 return (incs[kk]);
6705 }
6706
gmtmap_polar_az(struct GMT_CTRL * GMT)6707 GMT_LOCAL bool gmtmap_polar_az (struct GMT_CTRL *GMT) {
6708 return (gmt_M_is_azimuthal (GMT) && GMT->current.proj.polar && !GMT->common.R.oblique);
6709 }
6710
6711 /*! . */
gmt_auto_frame_interval(struct GMT_CTRL * GMT,unsigned int axis,unsigned int item)6712 void gmt_auto_frame_interval (struct GMT_CTRL *GMT, unsigned int axis, unsigned int item) {
6713 /* Determine the annotation and frame tick interval when they are not set (interval = 0) */
6714 int i = 0, n = 6;
6715 char unit = 's', sunit[2] = {""}, tmp[GMT_LEN16] = {""}, string[GMT_LEN64] = {""}, par[GMT_LEN128] = {""}, ax_code[4] = "xyz";
6716 bool set_a = false, interval = false, is_time = gmt_M_axis_is_time (GMT, axis);
6717 double defmaj[7] = {2.0, 5.0, 10.0, 15.0, 30.0, 60.0, 90.0}, defsub[7] = {1.0, 1.0, 2.0, 5.0, 10.0, 15.0, 30.0};
6718 double Hmaj[4] = {2.0, 3.0, 6.0, 12.0}, Hsub[4] = {1.0, 1.0, 3.0, 3.0};
6719 double Omaj[4] = {3.0, 6.0}, Osub[4] = {1.0, 3.0};
6720 double Dmaj[4] = {2.0, 3.0, 7.0, 14.0}, Dsub[4] = {1.0, 1.0, 1.0, 7.0};
6721 double d, f, p, sxy = 1.0, sz = 1.0, *maj = defmaj, *sub = defsub;
6722 struct GMT_PLOT_AXIS *A = &GMT->current.map.frame.axis[axis];
6723 struct GMT_PLOT_AXIS_ITEM *T;
6724
6725 if (A->special == GMT_CUSTOM) return; /* Got custom annotation/tick information via user file */
6726 if (A->type == GMT_LOG10 || A->type == GMT_POW) return; /* These axes still needs to have automatic selections implemented */
6727 if (GMT->current.io.cycle_operator) is_time = false; /* This axis is time, but now wrapped so treated differently */
6728 if (!(A->item[item].active && A->item[item].interval == 0.0) &&
6729 !(A->item[item+2].active && A->item[item+2].interval == 0.0) &&
6730 !(A->item[item+4].active && A->item[item+4].interval == 0.0)) return;
6731
6732 if (GMT->current.proj.three_D) { /* Make an approximate adjustment to the "sizes" given a perspective view */
6733 sxy = 1.0 - cosd (GMT->current.proj.z_project.view_elevation);
6734 sz = 1.0 - fabs (sind (GMT->current.proj.z_project.view_elevation));
6735 }
6736 /* f = frame width/height (inch); d = domain width/height (world coordinates) */
6737 if (axis == GMT_X) {
6738 f = sxy * fabs (GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]);
6739 d = fabs (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]);
6740 if (gmtmap_polar_az (GMT) && d > 120) f *= M_PI_2; /* Circle perimeter is the right comparison to circular longitudes */
6741 }
6742 else if (axis == GMT_Y) {
6743 f = sxy * fabs (GMT->current.proj.rect[YHI] - GMT->current.proj.rect[YLO]);
6744 d = fabs (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]);
6745 if (gmtmap_polar_az (GMT)) f /= 4; /* Latitude range is only covering half the plot */
6746 }
6747 else {
6748 f = sz * fabs (GMT->current.proj.zmax - GMT->current.proj.zmin);
6749 d = fabs (GMT->common.R.wesn[ZHI] - GMT->common.R.wesn[ZLO]);
6750 }
6751 f *= GMT->session.u2u[GMT_INCH][GMT_PT]; /* Change to points */
6752
6753 #ifndef NO_THEMES
6754 if (GMT->current.setting.run_mode == GMT_MODERN && gmt_M_axis_is_geo (GMT, axis)) { /* Need more space for degree symbol and WESN letters not considered in the algorithm */
6755 if (strchr (GMT->current.setting.format_geo_map, 'F')) /* Need more space for degree symbol and letter */
6756 d *= 1.2;
6757 else /* Just more space for degree symbol */
6758 d *= 1.1;
6759 }
6760 #endif
6761
6762 /* First guess of interval */
6763 if (gmtmap_polar_az (GMT))
6764 d *= MAX (0.05, MIN (5.0 * GMT->current.setting.font_annot[item].size / f, 0.3));
6765 else
6766 d *= MAX (0.05, MIN (5.0 * GMT->current.setting.font_annot[item].size / f, 0.2));
6767
6768 /* Now determine 'round' major and minor tick intervals */
6769 if (gmt_M_axis_is_geo (GMT, axis)) /* Geographical coordinate */
6770 p = (d < GMT_MIN2DEG) ? GMT_SEC2DEG : (d < 1.0) ? GMT_MIN2DEG : 1.0;
6771 else if (GMT->current.io.cycle_col == axis && GMT->current.io.cycle_operator != GMT_CYCLE_CUSTOM) {
6772 switch (GMT->current.io.cycle_operator) {
6773 case GMT_CYCLE_MIN: case GMT_CYCLE_HOUR: /* With a range of 60 it behaves like geo */
6774 p = (d < GMT_MIN2DEG) ? GMT_SEC2DEG : (d < 1.0) ? GMT_MIN2DEG : 1.0;
6775 break;
6776 case GMT_CYCLE_DAY: /* With a range of 24 */
6777 p = (d < 1.0) ? GMT_HR2DAY : 1.0;
6778 maj = Hmaj; sub = Hsub; n = 3;
6779 break;
6780 case GMT_CYCLE_ANNUAL: /* 12 months */
6781 p = 1.0;
6782 maj = Omaj; sub = Osub; n = 1;
6783 break;
6784 case GMT_CYCLE_WEEK: /* Need individual days here */
6785 d = p = f = 1.0; n = 0;
6786 break;
6787 default: /* This is the normalized ones which we treat like general linear axis */
6788 p = pow (10.0, floor (log10 (d)));
6789 break;
6790 }
6791 }
6792 else if (is_time) /* Time axis coordinate, get p in seconds and the unit it represents */
6793 p = gmtmap_auto_time_increment (d, &unit);
6794 else /* General (linear) axis */
6795 p = pow (10.0, floor (log10 (d)));
6796 d /= p; /* d is now in degrees, minutes or seconds (or time seconds), or in the range [1;10) */
6797 if (is_time) { /* p was in seconds but we will use unit, so reset p = 1 */
6798 p = 1.0;
6799 switch (unit) { /* Select other steps more suitable for month, day, hour. */
6800 case 'O': maj = Omaj; sub = Osub; n = 1; break;
6801 case 'D': maj = Dmaj; sub = Dsub; n = 3; break;
6802 case 'H': maj = Hmaj; sub = Hsub; n = 3;
6803 break;
6804 }
6805 if ((unit == 'H' || unit == 'M') && !strcmp (GMT->current.setting.format_clock_map, "hh:mm:ss")) {
6806 /* Strip off the seconds from the formatting if default setting is used */
6807 strcpy (GMT->current.setting.format_clock_map, "hh:mm");
6808 gmtlib_clock_C_format (GMT, GMT->current.setting.format_clock_map, &GMT->current.plot.calclock.clock, 2);
6809 sprintf (par, " --FORMAT_CLOCK_MAP=hh:mm");
6810 }
6811 else if (unit == 'D' && !strcmp (GMT->current.setting.format_date_map, "yyyy-mm-dd")) {
6812 /* Use month name and day */
6813 strcpy (GMT->current.setting.format_date_map, "o dd");
6814 gmtlib_date_C_format (GMT, GMT->current.setting.format_date_map, &GMT->current.plot.calclock.date, 2);
6815 sprintf (par, " --FORMAT_DATE_MAP=\"o dd\"");
6816 }
6817 else if (unit == 'O' && !strcmp (GMT->current.setting.format_date_map, "yyyy-mm-dd")) {
6818 /* Use year and month name for month intervals */
6819 strcpy (GMT->current.setting.format_date_map, "o yyyy");
6820 gmtlib_date_C_format (GMT, GMT->current.setting.format_date_map, &GMT->current.plot.calclock.date, 2);
6821 sprintf (par, " --FORMAT_DATE_MAP=\"o yyyy\"");
6822 }
6823
6824 interval = (unit == 'Y' || unit == 'O' || unit == 'D');
6825 }
6826 if (n) {
6827 while (i < n && maj[i] < d) i++; /* Wind up to largest reasonable interval */
6828 if ((GMT->current.proj.projection == GMT_MOLLWEIDE || GMT->current.proj.projection == GMT_HAMMER || \
6829 GMT->current.proj.projection == GMT_ECKERT4) && i == 6) /* Rounded misc. projections */
6830 d = maj[i-1] * p; /* Annotation interval needs to be less than 90 */
6831 else d = maj[i] * p; /* Scale up intervals in multiple of unit */
6832 f = sub[i] * p;
6833 }
6834 if (is_time) { /* Last check to change a 12 month unit to 1 year and 24 hours to 1 day */
6835 if (unit == 'O' && d == 12.0) d = 1.0, f /= 12.0, unit = 'Y';
6836 if (unit == 'H' && d == 24.0) d = 1.0, f /= 24.0, unit = 'D';
6837 sunit[0] = unit; /* Since we need a string in strcat */
6838 A->type = GMT_TIME;
6839 }
6840
6841 /* Set annotation/major tick interval */
6842 T = &A->item[item];
6843 if (T->active && T->interval == 0.0) {
6844 T->interval = d, T->generated = set_a = true;
6845 snprintf (tmp, GMT_LEN16, "a%g", T->interval); strcat (string, tmp);
6846 if (is_time) T->unit = unit, strcat (string, sunit);
6847 if (interval) T->type = 'i', T->flavor = 1;
6848 }
6849
6850 /* Set minor ticks as well (if copied from annotation, set to major interval) */
6851 T = &A->item[item+2];
6852 if (T->active && T->interval == 0.0) {
6853 T->interval = (T->type == 'f' || T->type == 'F') ? f : d, T->generated = true;
6854 snprintf (tmp, GMT_LEN16, "f%g", T->interval); strcat (string, tmp);
6855 if (is_time) T->unit = unit, strcat (string, sunit);
6856 }
6857
6858 /* Finally set grid interval (if annotation set as well, use major, otherwise minor interval) */
6859 T = &A->item[item+4];
6860 if (T->active && T->interval == 0.0) {
6861 T->interval = set_a ? d : f, T->generated = true; /* Commented out because f is too fine for gridline spacing, even if no annotations */
6862 // T->interval = d, T->generated = true;
6863 snprintf (tmp, GMT_LEN16, "g%g", T->interval); strcat (string, tmp);
6864 if (is_time) T->unit = unit, strcat (string, sunit);
6865 }
6866 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Auto-frame interval for axis %d item %d: d = %g f = %g\n", axis, item, d, f);
6867 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Auto-frame interval for %c-axis (item %d): %s%s\n", ax_code[axis], item, string, par);
6868 }
6869
6870 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
6871 *
6872 * S E C T I O N 1 : M A P - T R A N S F O R M A T I O N S
6873 *
6874 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
6875
6876 /*
6877 * gmt_map_setup sets up the transformations for the chosen map projection.
6878 * The user must pass:
6879 * w,e,s,n,parameters[0] - parameters[np-1] (np = number of parameters), and project:
6880 * w,e,s,n defines the area in degrees.
6881 * project == GMT_LINEAR, GMT_POLAR, GMT_MERCATOR, GMT_STEREO, GMT_LAMBERT, GMT_OBLIQUE_MERC, GMT_UTM,
6882 * GMT_TM, GMT_ORTHO, GMT_AZ_EQDIST, GMT_LAMB_AZ_EQ, GMT_WINKEL, GMT_ROBINSON, GMT_CASSINI, GMT_ALBERS, GMT_ECONIC,
6883 * GMT_ECKERT4, GMT_ECKERT6, GMT_CYL_EQ, GMT_CYL_EQDIST, GMT_CYL_STEREO, GMT_MILLER, GMT_VANGRINTEN
6884 * For GMT_LINEAR, we may have GMT_LINEAR, GMT_LOG10, or GMT_POW
6885 *
6886 * parameters[0] through parameters[np-1] mean different things to the various
6887 * projections, as explained below. (np also varies, of course)
6888 *
6889 * LINEAR projection:
6890 * parameters[0] is inch (or cm)/x_user_unit.
6891 * parameters[1] is inch (or cm)/y_user_unit. If 0, then yscale = xscale.
6892 * parameters[2] is pow for x^pow (if GMT_POW is on).
6893 * parameters[3] is pow for y^pow (if GMT_POW is on).
6894 *
6895 * POLAR (r,theta) projection:
6896 * parameters[0] is inch (or cm)/x_user_unit (radius)
6897 *
6898 * MERCATOR projection:
6899 * parameters[0] is central meridian
6900 * parameters[1] is standard parallel
6901 * parameters[2] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6902 *
6903 * STEREOGRAPHIC projection:
6904 * parameters[0] is longitude of pole
6905 * parameters[1] is latitude of pole
6906 * parameters[2] is radius in inches (or cm) from pole to the latitude specified
6907 * by parameters[3] OR 1:xxxxx OR map-width.
6908 *
6909 * LAMBERT projection (Conic):
6910 * parameters[0] is first standard parallel
6911 * parameters[1] is second standard parallel
6912 * parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
6913 *
6914 * OBLIQUE MERCATOR projection:
6915 * parameters[0] is longitude of origin
6916 * parameters[1] is latitude of origin
6917 * Definition a:
6918 * parameters[2] is longitude of second point along oblique equator
6919 * parameters[3] is latitude of second point along oblique equator
6920 * parameters[4] is scale in inch (or cm)/degree along oblique equator OR 1:xxxxx OR map-width
6921 * Definition b:
6922 * parameters[2] is azimuth along oblique equator at origin
6923 * parameters[3] is scale in inch (or cm)/degree along oblique equator OR 1:xxxxx OR map-width
6924 * Definition c:
6925 * parameters[2] is longitude of pole of projection
6926 * parameters[3] is latitude of pole of projection
6927 * parameters[4] is scale in inch (cm)/degree along oblique equator OR 1:xxxxx OR map-width
6928 *
6929 * TRANSVERSE MERCATOR (TM) projection
6930 * parameters[0] is central meridian
6931 * parameters[1] is central parallel
6932 * parameters[2] is scale in inch (cm)/degree along this meridian OR 1:xxxxx OR map-width
6933 *
6934 * UNIVERSAL TRANSVERSE MERCATOR (UTM) projection
6935 * parameters[0] is UTM zone (0-60, use negative for S hemisphere)
6936 * parameters[1] is scale in inch (cm)/degree along this meridian OR 1:xxxxx OR map-width
6937 *
6938 * LAMBERT AZIMUTHAL EQUAL AREA projection:
6939 * parameters[0] is longitude of origin
6940 * parameters[1] is latitude of origin
6941 * parameters[2] is radius in inches (or cm) from pole to the latitude specified
6942 * by parameters[3] OR 1:xxxxx OR map-width.
6943 *
6944 * ORTHOGRAPHIC AZIMUTHAL projection:
6945 * parameters[0] is longitude of origin
6946 * parameters[1] is latitude of origin
6947 * parameters[2] is radius in inches (or cm) from pole to the latitude specified
6948 * by parameters[3] OR 1:xxxxx OR map-width.
6949 *
6950 * GENERAL PERSPECTIVE projection:
6951 * parameters[0] is longitude of origin
6952 * parameters[1] is latitude of origin
6953 * parameters[2] is radius in inches (or cm) from pole to the latitude specified
6954 * by parameters[3] OR 1:xxxxx OR map-width.
6955 * parameters[4] is the altitude of the view point in kilometers
6956 * if altitude is < 10 then it is the distance from the center of the Earth
6957 * divided by the radius of the Earth
6958 * parameters[5] is the azimuth east for North of the viewpoint
6959 * parameters[6] is the tilt upward of the plane of projection
6960 * parameters[7] is the width of the viewpoint in degrees
6961 * if = 0, no viewpoint clipping
6962 * parameters[8] is the height of the viewpoint in degrees
6963 * if = 0, no viewpoint clipping
6964 *
6965 * AZIMUTHAL EQUIDISTANCE projection:
6966 * parameters[0] is longitude of origin
6967 * parameters[1] is latitude of origin
6968 * parameters[2] is radius in inches (or cm) from pole to the latitude specified
6969 * by parameters[3] OR 1:xxxxx OR map-width.
6970 *
6971 * MOLLWEIDE EQUAL-AREA projection
6972 * parameters[0] is longitude of origin
6973 * parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6974 *
6975 * HAMMER-AITOFF EQUAL-AREA projection
6976 * parameters[0] is longitude of origin
6977 * parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6978 *
6979 * SINUSOIDAL EQUAL-AREA projection
6980 * parameters[0] is longitude of origin
6981 * parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6982 *
6983 * WINKEL TRIPEL MODIFIED AZIMUTHAL projection
6984 * parameters[0] is longitude of origin
6985 * parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6986 *
6987 * ROBINSON PSEUDOCYLINDRICAL projection
6988 * parameters[0] is longitude of origin
6989 * parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6990 *
6991 * VAN DER VANGRINTEN projection
6992 * parameters[0] is longitude of origin
6993 * parameters[1] is in inch (or cm)/degree_longitude @ equator OR 1:xxxxx OR map-width
6994 *
6995 * CASSINI projection
6996 * parameters[0] is longitude of origin
6997 * parameters[1] is latitude of origin
6998 * parameters[2] is scale in inch (cm)/degree along this meridian OR 1:xxxxx OR map-width
6999 *
7000 * ALBERS projection (Conic):
7001 * parameters[0] is first standard parallel
7002 * parameters[1] is second standard parallel
7003 * parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7004 *
7005 * CONIC EQUIDISTANT projection:
7006 * parameters[0] is first standard parallel
7007 * parameters[1] is second standard parallel
7008 * parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7009 *
7010 * ECKERT6 IV projection:
7011 * parameters[0] is longitude of origin
7012 * parameters[1] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7013 *
7014 * ECKERT6 IV projection:
7015 * parameters[0] is longitude of origin
7016 * parameters[1] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7017 *
7018 * CYLINDRICAL EQUAL-AREA projections (Behrmann, Gall):
7019 * parameters[0] is longitude of origin
7020 * parameters[1] is the standard parallel
7021 * parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7022 *
7023 * CYLINDRICAL STEREOGRAPHIC projections (Braun, Gall, B.S.A.M.):
7024 * parameters[0] is longitude of origin
7025 * parameters[1] is the standard parallel
7026 * parameters[2] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7027 *
7028 * MILLER CYLINDRICAL projection:
7029 * parameters[0] is longitude of origin
7030 * parameters[1] is scale in inch (or cm)/degree along parallels OR 1:xxxxx OR map-width
7031 *
7032 * Pointers to the correct map transformation functions will be set up so that
7033 * there are no if tests to determine which routine to call. These pointers
7034 * are forward and inverse, and are called from gmt_geo_to_xy and gmt_xy_to_geo.
7035 *
7036 */
7037
7038 /*
7039 * GENERIC TRANSFORMATION ROUTINES FOR THE LINEAR PROJECTION
7040 */
7041
7042 /*! . */
gmt_x_to_xx(struct GMT_CTRL * GMT,double x)7043 double gmt_x_to_xx (struct GMT_CTRL *GMT, double x) {
7044 /* Converts x to xx using the current linear projection */
7045 double xx = 0.0;
7046 (*GMT->current.proj.fwd_x) (GMT, x, &xx);
7047 return (xx * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
7048 }
7049
7050 /*! . */
gmt_y_to_yy(struct GMT_CTRL * GMT,double y)7051 double gmt_y_to_yy (struct GMT_CTRL *GMT, double y) {
7052 /* Converts y to yy using the current linear projection */
7053 double yy = 0.0;
7054 (*GMT->current.proj.fwd_y) (GMT, y, &yy);
7055 return (yy * GMT->current.proj.scale[GMT_Y] + GMT->current.proj.origin[GMT_Y]);
7056 }
7057
7058 /*! . */
gmt_z_to_zz(struct GMT_CTRL * GMT,double z)7059 double gmt_z_to_zz (struct GMT_CTRL *GMT, double z) {
7060 /* Converts z to zz using the current linear projection */
7061 double zz = 0.0;
7062 (*GMT->current.proj.fwd_z) (GMT, z, &zz);
7063 return (zz * GMT->current.proj.scale[GMT_Z] + GMT->current.proj.origin[GMT_Z]);
7064 }
7065
7066 /*! . */
gmt_geo_to_xy(struct GMT_CTRL * GMT,double lon,double lat,double * x,double * y)7067 bool gmt_geo_to_xy (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
7068 /* Converts lon/lat to x/y using the current projection */
7069 if (gmt_M_is_dnan (lon) || gmt_M_is_dnan (lat)) {(*x) = (*y) = GMT->session.d_NaN; return true;} /* Quick and safe way to ensure NaN-input results in NaNs */
7070 (*GMT->current.proj.fwd) (GMT, lon, lat, x, y);
7071 (*x) = (*x) * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X];
7072 (*y) = (*y) * GMT->current.proj.scale[GMT_Y] + GMT->current.proj.origin[GMT_Y];
7073 return false;
7074 }
7075
7076 /*! . */
gmt_geo_to_xy_noshift(struct GMT_CTRL * GMT,double lon,double lat,double * x,double * y)7077 bool gmt_geo_to_xy_noshift (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
7078 /* Converts lon/lat to x/y using the current projection but applies no shift */
7079 if (gmt_M_is_dnan (lon) || gmt_M_is_dnan (lat)) {(*x) = (*y) = GMT->session.d_NaN; return true;} /* Quick and safe way to ensure NaN-input results in NaNs */
7080 (*GMT->current.proj.fwd) (GMT, lon, lat, x, y);
7081 (*x) = (*x) * GMT->current.proj.scale[GMT_X];
7082 (*y) = (*y) * GMT->current.proj.scale[GMT_Y];
7083 return false;
7084 }
7085
7086 /*! . */
gmt_geo_to_xy_noshiftscale(struct GMT_CTRL * GMT,double lon,double lat,double * x,double * y)7087 bool gmt_geo_to_xy_noshiftscale (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
7088 /* Converts lon/lat to x/y using the current projection but applies no shift */
7089 if (gmt_M_is_dnan (lon) || gmt_M_is_dnan (lat)) {(*x) = (*y) = GMT->session.d_NaN; return true;} /* Quick and safe way to ensure NaN-input results in NaNs */
7090 (*GMT->current.proj.fwd) (GMT, lon, lat, x, y);
7091 return false;
7092 }
7093
7094 /*! . */
gmt_xy_to_geo(struct GMT_CTRL * GMT,double * lon,double * lat,double x,double y)7095 void gmt_xy_to_geo (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
7096 /* Converts x/y to lon/lat using the current projection */
7097
7098 if (gmt_M_is_dnan (x) || gmt_M_is_dnan (y)) {(*lon) = (*lat) = GMT->session.d_NaN; return;} /* Quick and safe way to ensure NaN-input results in NaNs */
7099 x = (x - GMT->current.proj.origin[GMT_X]) * GMT->current.proj.i_scale[GMT_X];
7100 y = (y - GMT->current.proj.origin[GMT_Y]) * GMT->current.proj.i_scale[GMT_Y];
7101
7102 (*GMT->current.proj.inv) (GMT, lon, lat, x, y);
7103 }
7104
7105 /*! . */
gmt_xy_to_geo_noshiftscale(struct GMT_CTRL * GMT,double * lon,double * lat,double x,double y)7106 void gmt_xy_to_geo_noshiftscale (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
7107 /* Converts x/y to lon/lat using the current projection but applies no shift */
7108
7109 if (gmt_M_is_dnan (x) || gmt_M_is_dnan (y)) {(*lon) = (*lat) = GMT->session.d_NaN; return;} /* Quick and safe way to ensure NaN-input results in NaNs */
7110 (*GMT->current.proj.inv) (GMT, lon, lat, x, y);
7111 }
7112
7113 /*! . */
gmt_xy_to_geo_noshift(struct GMT_CTRL * GMT,double * lon,double * lat,double x,double y)7114 void gmt_xy_to_geo_noshift (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
7115 /* Converts x/y to lon/lat using the current projection but applies no shift */
7116
7117 if (gmt_M_is_dnan (x) || gmt_M_is_dnan (y)) {(*lon) = (*lat) = GMT->session.d_NaN; return;} /* Quick and safe way to ensure NaN-input results in NaNs */
7118 (*GMT->current.proj.inv) (GMT, lon, lat, x, y);
7119 }
7120
7121 /*! . */
gmt_geoz_to_xy(struct GMT_CTRL * GMT,double x,double y,double z,double * x_out,double * y_out)7122 void gmt_geoz_to_xy (struct GMT_CTRL *GMT, double x, double y, double z, double *x_out, double *y_out) {
7123 /* Map-projects xy first, the projects xyz onto xy plane */
7124 double x0, y0;
7125 gmt_geo_to_xy (GMT, x, y, &x0, &y0);
7126 gmt_xyz_to_xy (GMT, x0, y0, gmt_z_to_zz (GMT, z), x_out, y_out);
7127 }
7128
7129 /*! . */
gmt_xyz_to_xy(struct GMT_CTRL * GMT,double x,double y,double z,double * x_out,double * y_out)7130 void gmt_xyz_to_xy (struct GMT_CTRL *GMT, double x, double y, double z, double *x_out, double *y_out) {
7131 /* projects xyz (inches) onto perspective xy plane (inches) */
7132 *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;
7133 *y_out = - (x * GMT->current.proj.z_project.sin_az + y * GMT->current.proj.z_project.cos_az) * GMT->current.proj.z_project.sin_el + z * GMT->current.proj.z_project.cos_el + GMT->current.proj.z_project.y_off;
7134 }
7135
7136
7137 /*! Setting w/e/s/n for a fully qualified UTM zone */
7138
gmt_UTMzone_to_wesn(struct GMT_CTRL * GMT,unsigned int zone_x,char zone_y,int hemi,double wesn[])7139 bool gmt_UTMzone_to_wesn (struct GMT_CTRL *GMT, unsigned int zone_x, char zone_y, int hemi, double wesn[]) {
7140 /* Given the full UTM zone specification, return w/e/s/n */
7141
7142 bool error = false;
7143 gmt_M_unused(GMT);
7144
7145 wesn[XHI] = -180.0 + 6.0 * zone_x; wesn[XLO] = wesn[XHI] - 6.0;
7146
7147 if (zone_y == 0) { /* Latitude zone is not specified */
7148 if (hemi == -1) {
7149 wesn[YLO] = -80.0; wesn[YHI] = 0.0;
7150 }
7151 else if (hemi == +1) {
7152 wesn[YLO] = 0.0; wesn[YHI] = 84.0;
7153 }
7154 else
7155 error = true;
7156 return (error);
7157 }
7158 else if (zone_y < 'A' || zone_y > 'Z')
7159 error = true;
7160 else if (zone_y <= 'B') {
7161 wesn[YLO] = -90.0; wesn[YHI] = -80.0;
7162 wesn[XHI] = 180.0 * (zone_y - 'A');
7163 wesn[XLO] = wesn[XHI] - 180.0;
7164 }
7165 else if (zone_y <= 'I') { /* I will behave as J */
7166 wesn[YLO] = -80.0 + 8.0 * (zone_y - 'C'); wesn[YHI] = wesn[YLO] + 8.0;
7167 }
7168 else if (zone_y <= 'O') { /* O will behave as P */
7169 wesn[YLO] = -80.0 + 8.0 * (zone_y - 'D'); wesn[YHI] = wesn[YLO] + 8.0;
7170 }
7171 else if (zone_y <= 'W') {
7172 wesn[YLO] = -80.0 + 8.0 * (zone_y - 'E'); wesn[YHI] = wesn[YLO] + 8.0;
7173 if (zone_y == 'V' && zone_x == 31) wesn[XHI] = 3.0;
7174 if (zone_y == 'V' && zone_x == 32) wesn[XLO] = 3.0;
7175 }
7176 else if (zone_y == 'X') {
7177 wesn[YLO] = 72.0; wesn[YHI] = 84.0;
7178 if (zone_x == 31) wesn[XHI] = 9.0;
7179 if (zone_x == 33) {wesn[XLO] = 9.0; wesn[XHI] = 21.0;}
7180 if (zone_x == 35) {wesn[XLO] = 21.0; wesn[XHI] = 33.0;}
7181 if (zone_x == 37) wesn[XLO] = 33.0;
7182 if (zone_x == 32 || zone_x == 34 || zone_x == 36) error = true;
7183 }
7184 else { /* Y or Z */
7185 wesn[YLO] = 84.0; wesn[YHI] = 90.0;
7186 wesn[XHI] = 180.0 * (zone_y - 'Y');
7187 wesn[XLO] = wesn[XHI] - 180.0;
7188 }
7189
7190 return (error);
7191 }
7192
7193 /*! . */
gmtlib_genper_reset(struct GMT_CTRL * GMT,bool reset)7194 bool gmtlib_genper_reset (struct GMT_CTRL *GMT, bool reset) {
7195 /* Switch between the windowed functions for lines etc and the rect ones for annotations.
7196 * Because of issue # 667 it became clear that we had problems with gridlines when the
7197 * projection became windowed, i.e., view is a clipped circle with both straight and
7198 * curved boundaries. I added several new functions to deal with overlaps, crossings,
7199 * left and right boundary x-value, etc (all called gmtproj_genper_*) but they yield way too
7200 * many annotations (probably due to some confusion) so until that might be looked into I
7201 * have kept the older, simpler functions and switch from those to the new ones when we
7202 * do gridlines. P. Wessel, Feb 10, 2015.
7203 */
7204 if (GMT->current.proj.projection_GMT == GMT_GENPER && GMT->current.proj.windowed) {
7205 if (reset) {
7206 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Revert to old genper crossing/overlap functions\n");
7207 GMT->current.map.crossing = &gmtmap_rect_crossing;
7208 GMT->current.map.overlap = &gmtmap_rect_overlap;
7209 }
7210 else {
7211 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Switch to new genper crossing/overlap functions\n");
7212 GMT->current.map.crossing = &gmtmap_genper_crossing;
7213 GMT->current.map.overlap = &gmtmap_genperw_overlap;
7214 }
7215 return true;
7216 }
7217 return false;
7218 }
7219
7220 /*! . */
gmt_map_outside(struct GMT_CTRL * GMT,double lon,double lat)7221 bool gmt_map_outside (struct GMT_CTRL *GMT, double lon, double lat) {
7222 /* Save current status in previous status and update current in/out status */
7223 GMT->current.map.prev_x_status = GMT->current.map.this_x_status;
7224 GMT->current.map.prev_y_status = GMT->current.map.this_y_status;
7225 if (GMT->current.map.outside == NULL) {
7226 GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_map_outside: FATAL ERROR - the pointer to the projection function is NULL.\n");
7227 return GMT_NOTSET;
7228 }
7229 return ((*GMT->current.map.outside) (GMT, lon, lat));
7230 }
7231
7232 /*! . */
gmt_half_map_width(struct GMT_CTRL * GMT,double y)7233 double gmt_half_map_width (struct GMT_CTRL *GMT, double y) {
7234 /* Returns 1/2-width of map in inches given y value */
7235 double half_width;
7236
7237 switch (GMT->current.proj.projection_GMT) {
7238
7239 case GMT_STEREO: /* Must compute width of circular map based on y value (ASSUMES FULL CIRCLE!!!) */
7240 case GMT_LAMB_AZ_EQ:
7241 case GMT_GNOMONIC:
7242 case GMT_AZ_EQDIST:
7243 case GMT_VANGRINTEN:
7244 if (!GMT->common.R.oblique && GMT->current.map.is_world) {
7245 y -= GMT->current.proj.r;
7246 half_width = d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y);
7247 }
7248 else
7249 half_width = GMT->current.map.half_width;
7250 break;
7251
7252 case GMT_ORTHO:
7253 case GMT_GENPER:
7254 if (!GMT->common.R.oblique && gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) {
7255 y -= GMT->current.proj.r;
7256 half_width = d_sqrt (GMT->current.proj.r * GMT->current.proj.r - y * y);
7257 }
7258 else
7259 half_width = GMT->current.map.half_width;
7260 break;
7261 case GMT_MOLLWEIDE: /* Must compute width of Mollweide map based on y value */
7262 case GMT_HAMMER:
7263 case GMT_WINKEL:
7264 case GMT_SINUSOIDAL:
7265 case GMT_ROBINSON:
7266 case GMT_ECKERT4:
7267 case GMT_ECKERT6:
7268 if (!GMT->common.R.oblique && GMT->current.map.is_world)
7269 half_width = gmtlib_right_boundary (GMT, y) - GMT->current.map.half_width;
7270 else
7271 half_width = GMT->current.map.half_width;
7272 break;
7273
7274 default: /* Rectangular maps are easy */
7275 half_width = GMT->current.map.half_width;
7276 break;
7277 }
7278 return (half_width);
7279 }
7280
7281 /*! . */
gmt_map_truncate(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,uint64_t start,int side)7282 uint64_t gmt_map_truncate (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, uint64_t start, int side) {
7283 /* Truncates a wrapping polygon against left or right edge.
7284 (bottom or top edge when projection is TM)
7285 x, y : arrays of plot coordinates
7286 n : length of the arrays
7287 start: first point of array to consider
7288 side : -1 = left (bottom); +1 = right (top)
7289 */
7290
7291 if (GMT->current.proj.projection_GMT == GMT_TM)
7292 return (gmtmap_truncate_tm (GMT, x, y, n, start, side));
7293 else
7294 return (gmtmap_truncate_x (GMT, x, y, n, start, side));
7295 }
7296
7297 /* GMT generic distance calculations between a pair of points in 2-D */
7298
7299 /*! . */
gmtlib_distance_type(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE,unsigned int id)7300 double gmtlib_distance_type (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE, unsigned int id) {
7301 /* Generic function available to programs for contour/label distance calculations */
7302 return (GMT->current.map.dist[id].scale * GMT->current.map.dist[id].func (GMT, lonS, latS, lonE, latE));
7303 }
7304
7305 /*! . */
gmt_distance(struct GMT_CTRL * GMT,double lonS,double latS,double lonE,double latE)7306 double gmt_distance (struct GMT_CTRL *GMT, double lonS, double latS, double lonE, double latE) {
7307 /* Generic function available to programs */
7308 return (gmtlib_distance_type (GMT, lonS, latS, lonE, latE, 0));
7309 }
7310
7311 /*! . */
gmt_near_a_point(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,double dist)7312 bool gmt_near_a_point (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, double dist) {
7313 /* Compute distance to nearest point in T from (lon, lat) */
7314 return (GMT->current.map.near_point_func (GMT, lon, lat, T, dist));
7315 }
7316
7317 /*! . */
gmt_near_lines(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)7318 bool gmt_near_lines (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
7319 /* Compute distance to nearest line in T from (lon,lat) */
7320 return (GMT->current.map.near_lines_func (GMT, lon, lat, T, return_mindist, dist_min, x_near, y_near));
7321 }
7322
7323 /*! . */
gmt_near_a_line(struct GMT_CTRL * GMT,double lon,double lat,uint64_t seg,struct GMT_DATASEGMENT * S,unsigned int return_mindist,double * dist_min,double * x_near,double * y_near)7324 bool gmt_near_a_line (struct GMT_CTRL *GMT, double lon, double lat, uint64_t seg, struct GMT_DATASEGMENT *S, unsigned int return_mindist, double *dist_min, double *x_near, double *y_near) {
7325 /* Compute distance to the line S from (lon,lat) */
7326 return (GMT->current.map.near_a_line_func (GMT, lon, lat, seg, S, return_mindist, dist_min, x_near, y_near));
7327 }
7328
7329 /* Specific functions that are accessed via pointer only */
7330
7331 /*! . */
gmtlib_cartesian_dist(struct GMT_CTRL * GMT,double x0,double y0,double x1,double y1)7332 double gmtlib_cartesian_dist (struct GMT_CTRL *GMT, double x0, double y0, double x1, double y1) {
7333 /* Calculates the good-old straight line distance in users units */
7334 gmt_M_unused(GMT);
7335 return (hypot ( (x1 - x0), (y1 - y0)));
7336 }
7337
7338 /*! . */
gmtlib_cartesian_dist_proj(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)7339 double gmtlib_cartesian_dist_proj (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
7340 /* Calculates the good-old straight line distance after first projecting the data */
7341 double x0, y0, x1, y1;
7342 gmt_geo_to_xy (GMT, lon1, lat1, &x0, &y0);
7343 gmt_geo_to_xy (GMT, lon2, lat2, &x1, &y1);
7344 return (hypot ( (x1 - x0), (y1 - y0)));
7345 }
7346
7347 /*! . */
gmtlib_great_circle_dist_degree(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)7348 double gmtlib_great_circle_dist_degree (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
7349 /* Great circle distance on a sphere in degrees */
7350
7351 double sin_half_squared = gmtmap_haversine (GMT, lon1, lat1, lon2, lat2);
7352 return (2.0 * d_asind (d_sqrt (sin_half_squared)));
7353 }
7354
7355 /*! . */
gmt_great_circle_dist_meter(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2)7356 double gmt_great_circle_dist_meter (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2) {
7357 /* Calculates the great circle distance in meter */
7358 return (gmtlib_great_circle_dist_degree (GMT, lon1, lat1, lon2, lat2) * GMT->current.proj.DIST_M_PR_DEG);
7359 }
7360
7361 /* Functions dealing with distance between points */
7362
7363 /*! . */
gmt_mindist_to_point(struct GMT_CTRL * GMT,double lon,double lat,struct GMT_DATATABLE * T,uint64_t * id)7364 double gmt_mindist_to_point (struct GMT_CTRL *GMT, double lon, double lat, struct GMT_DATATABLE *T, uint64_t *id) {
7365 uint64_t row, seg;
7366 double d, d_min;
7367
7368 d_min = DBL_MAX;
7369 for (seg = 0; seg < T->n_segments; seg++) {
7370 for (row = 0; row < T->segment[seg]->n_rows; row++) {
7371 d = gmt_distance (GMT, lon, lat, T->segment[seg]->data[GMT_X][row], T->segment[seg]->data[GMT_Y][row]);
7372 if (d < d_min) { /* Update the shortest distance and the point responsible */
7373 d_min = d; id[0] = seg; id[1] = row;
7374 }
7375 }
7376 }
7377 return (d_min);
7378 }
7379
7380 #if 0
7381 int gmtlib_small_circle_intersection (struct GMT_CTRL *GMT, double Ax, double Ay, double Ar, double Bx, double By, double Br, double Xx[2], double Yy[2]) {
7382 /* Let (Ax,Ay) and (Bx,By) be the poles of two small circles, each with radius Ar and Br, respectively, in degrees.
7383 * Pass out the 0-2 intersections via Xx, Yx and return the number of intersections 0-2.
7384 * Modified from http://gis.stackexchange.com/questions/48937/how-to-calculate-the-intersection-of-2-circles
7385 */
7386 unsigned int k;
7387 double A[3], B[3], X[3], N[3], P[3], t, a, b, L_X2, L_N2, cos_AB, AB, cos_Ar, cos_Br, cos_AB2;
7388
7389 gmt_geo_to_cart (GMT, Ay, Ax, A, true); /* Get pole vector A */
7390 gmt_geo_to_cart (GMT, By, Bx, B, true); /* Get pole vector B */
7391 cos_AB = gmt_dot3v (GMT, A, B); /* Cos of spherical distance between A and B */
7392 AB = d_acosd (cos_AB); /* Distance between the two poles in degrees */
7393 if ((Ar + Br) < AB) return 0; /* No intersection possible */
7394 if (doubleAlmostEqual (Ar + Br, AB)) { /* Tangent point lines along great circle form A to B */
7395 t = Ar / AB; /* Fractional distance of X from A */
7396 for (k = 0; k < 3; k++) X[k] = A[k] * (1.0 - t) + B[k] * t;
7397 gmt_normalize3v (GMT, X); /* Make sure X has unit length */
7398 gmt_cart_to_geo (GMT, &Yy[0], &Xx[0], X, true); /* Recover lon,lat of tangent point */
7399 return 1;
7400 }
7401 /* Here we have two intersections */
7402 cos_AB2 = cos_AB * cos_AB;
7403 cos_Ar = cosd (Ar); cos_Br = cosd (Br);
7404 a = (cos_Ar - cos_Br * cos_AB) / (1.0 - cos_AB2);
7405 b = (cos_Br - cos_Ar * cos_AB) / (1.0 - cos_AB2);
7406 for (k = 0; k < 3; k++) X[k] = a * A[k] + b * B[k];
7407 L_X2 = gmt_dot3v (GMT, X, X); /* Length^2 of X */
7408 gmt_cross3v (GMT, A, B, N); /* Get pole position of plane through A and B (and origin O) */
7409 L_N2 = gmt_dot3v (GMT, N, N); /* Length^2 of N */
7410 t = sqrt ((1.0 - L_X2) / L_N2);
7411 for (k = 0; k < 3; k++) P[k] = X[k] + t * N[k];
7412 gmt_normalize3v (GMT, P); /* Make sure P has unit length */
7413 gmt_cart_to_geo (GMT, &Yy[0], &Xx[0], P, true); /* Recover lon,lat of 1st intersection point */
7414 for (k = 0; k < 3; k++) P[k] = X[k] - t * N[k];
7415 gmt_normalize3v (GMT, P); /* Make sure P has unit length */
7416 gmt_cart_to_geo (GMT, &Yy[1], &Xx[1], P, true); /* Recover lon,lat of 2nd intersection point */
7417 return 2;
7418 }
7419 #endif
7420
7421 /*! . */
gmtlib_great_circle_intersection(struct GMT_CTRL * GMT,double A[],double B[],double C[],double X[],double * CX_dist)7422 int gmtlib_great_circle_intersection (struct GMT_CTRL *GMT, double A[], double B[], double C[], double X[], double *CX_dist) {
7423 /* A, B, C are 3-D Cartesian unit vectors, i.e., points on the sphere.
7424 * Let points A and B define a great circle, and consider a
7425 * third point C. A second great circle goes through C and
7426 * is orthogonal to the first great circle. Their intersection
7427 * X is the point on (A,B) closest to C. We must test if X is
7428 * between A,B or outside.
7429 */
7430 unsigned int i;
7431 double P[3], E[3], M[3], Xneg[3], cos_AB, cos_MX1, cos_MX2, cos_test;
7432
7433 gmt_cross3v (GMT, A, B, P); /* Get pole position of plane through A and B (and origin O) */
7434 gmt_normalize3v (GMT, P); /* Make sure P has unit length */
7435 gmt_cross3v (GMT, C, P, E); /* Get pole E to plane through C (and origin) but normal to A,B (hence going through P) */
7436 gmt_normalize3v (GMT, E); /* Make sure E has unit length */
7437 gmt_cross3v (GMT, P, E, X); /* Intersection between the two planes is oriented line*/
7438 gmt_normalize3v (GMT, X); /* Make sure X has unit length */
7439 /* The X we want could be +x or -X; must determine which might be closest to A-B midpoint M */
7440 for (i = 0; i < 3; i++) {
7441 M[i] = A[i] + B[i];
7442 Xneg[i] = -X[i];
7443 }
7444 gmt_normalize3v (GMT, M); /* Make sure M has unit length */
7445 /* Must first check if X is along the (A,B) segment and not on its extension */
7446
7447 cos_MX1 = gmt_dot3v (GMT, M, X); /* Cos of spherical distance between M and +X */
7448 cos_MX2 = gmt_dot3v (GMT, M, Xneg); /* Cos of spherical distance between M and -X */
7449 if (cos_MX2 > cos_MX1) gmt_M_memcpy (X, Xneg, 3, double); /* -X is closest to A-B midpoint */
7450 cos_AB = fabs (gmt_dot3v (GMT, A, B)); /* Cos of spherical distance between A,B */
7451 cos_test = fabs (gmt_dot3v (GMT, A, X)); /* Cos of spherical distance between A and X */
7452 if (cos_test < cos_AB) return 1; /* X must be on the A-B extension if its distance to A exceeds the A-B length */
7453 cos_test = fabs (gmt_dot3v (GMT, B, X)); /* Cos of spherical distance between B and X */
7454 if (cos_test < cos_AB) return 1; /* X must be on the A-B extension if its distance to B exceeds the A-B length */
7455
7456 /* X is between A and B. Now calculate distance between C and X */
7457
7458 *CX_dist = gmt_dot3v (GMT, C, X); /* Cos of spherical distance between C and X */
7459 return (0); /* Return zero if intersection is between A and B */
7460 }
7461
7462 /*! . */
gmtlib_split_line(struct GMT_CTRL * GMT,double ** xx,double ** yy,uint64_t * nn,bool add_crossings)7463 uint64_t *gmtlib_split_line (struct GMT_CTRL *GMT, double **xx, double **yy, uint64_t *nn, bool add_crossings) {
7464 /* Accepts x/y array for a line in projected inches and looks for
7465 * map jumps. If found it will insert the boundary crossing points and
7466 * build a split integer array with the nodes of the first point
7467 * for each segment. The number of segments is returned. If
7468 * no jumps are found then NULL is returned. This function is needed
7469 * so that the new PS contouring machinery only sees lines that do no
7470 * jump across the map.
7471 * add_crossings is true if we need to find the crossings; false means
7472 * they are already part of the line. */
7473
7474 uint64_t i, j, jc, k, n, n_seg, *split = NULL, *pos = NULL;
7475 size_t n_alloc = 0;
7476 int l_or_r;
7477 char *way = NULL;
7478 double *x = NULL, *y = NULL, *xin = NULL, *yin = NULL, xc[2], yc[2];
7479
7480 /* First quick scan to see how many jumps there are */
7481
7482 xin = *xx; yin = *yy;
7483 gmt_set_meminc (GMT, GMT_SMALL_CHUNK);
7484 for (n_seg = 0, i = 1; i < *nn; i++) {
7485 if ((l_or_r = gmtmap_jump_xy (GMT, xin[i], yin[i], xin[i-1], yin[i-1]))) {
7486 if (n_seg == n_alloc) {
7487 pos = gmt_M_malloc (GMT, pos, n_seg, &n_alloc, uint64_t);
7488 n_alloc = n_seg;
7489 way = gmt_M_malloc (GMT, way, n_seg, &n_alloc, char);
7490 }
7491 pos[n_seg] = i; /* 2nd of the two points that generate the jump */
7492 way[n_seg] = (char)l_or_r; /* Which way we jump : +1 is right to left, -1 is left to right, +2 is top to bottom, -2 is bottom to top */
7493 n_seg++;
7494 }
7495 }
7496 gmt_reset_meminc (GMT);
7497
7498 if (n_seg == 0) return (NULL); /* No jumps, just return NULL */
7499
7500 /* Here we have one or more jumps so we need to split the line */
7501
7502 n = *nn; /* Original line count */
7503 if (add_crossings) n += 2 * n_seg; /* Must add 2 crossing points per jump */
7504 n_alloc = 0;
7505 gmt_M_malloc2 (GMT, x, y, n, &n_alloc, double);
7506 split = gmt_M_memory (GMT, NULL, n_seg+2, uint64_t);
7507 split[0] = n_seg; /* Number of segments we will need */
7508
7509 x[0] = xin[0]; y[0] = yin[0];
7510 for (i = j = 1, k = 0; i < *nn; i++, j++) {
7511 if (k < n_seg && i == pos[k]) { /* At jump point */
7512 if (add_crossings) { /* Find and insert the crossings */
7513 if (way[k] == -2 || way[k] == +2) { /* Jump in y for TM */
7514 gmtmap_get_crossings_y (GMT, xc, yc, xin[i], yin[i], xin[i-1], yin[i-1]);
7515 if (way[k] == -2) { /* Add top border point first */
7516 gmt_M_double_swap (xc[0], xc[1]);
7517 gmt_M_double_swap (yc[0], yc[1]);
7518 }
7519 }
7520 else { /* Jump in x */
7521 gmtmap_get_crossings_x (GMT, xc, yc, xin[i], yin[i], xin[i-1], yin[i-1]);
7522 if (way[k] == 1) { /* Add right border point first */
7523 gmt_M_double_swap (xc[0], xc[1]);
7524 gmt_M_double_swap (yc[0], yc[1]);
7525 }
7526 }
7527 x[j] = xc[0]; y[j++] = yc[0]; /* End of one segment */
7528 jc = j; /* Node of first point in next segment */
7529 x[j] = xc[1]; y[j++] = yc[1]; /* Start of another */
7530 }
7531 else
7532 jc = j;
7533 split[++k] = jc; /* Node of first point in new segment */
7534 }
7535 /* Then copy the regular points */
7536 x[j] = xin[i]; y[j] = yin[i];
7537 }
7538 split[++k] = j; /* End of last segment */
7539
7540 /* Time to return the pointers to new data */
7541
7542 gmt_M_free (GMT, pos);
7543 gmt_M_free (GMT, way);
7544 gmt_M_free (GMT, xin);
7545 gmt_M_free (GMT, yin);
7546 *xx = x;
7547 *yy = y;
7548 *nn = j;
7549
7550 return (split);
7551 }
7552
7553 /* Routines to add pieces of parallels or meridians */
7554
7555 /*! . */
gmt_graticule_path(struct GMT_CTRL * GMT,double ** x,double ** y,int dir,bool check,double w,double e,double s,double n)7556 uint64_t gmt_graticule_path (struct GMT_CTRL *GMT, double **x, double **y, int dir, bool check, double w, double e, double s, double n) {
7557 /* Returns the path of a graticule (box of meridians and parallels).
7558 * dir determines if we go counter-clockwise or clockwise.
7559 * check, if true, forces a straddle check at the end for geographic data.
7560 */
7561 size_t n_alloc = 0;
7562 uint64_t np = 0;
7563 double *xx = NULL, *yy = NULL;
7564 double px0, px1, px2, px3;
7565
7566 if (dir == 1) { /* Forward sense */
7567 px0 = px3 = w; px1 = px2 = e;
7568 }
7569 else { /* Reverse sense */
7570 px0 = px3 = e; px1 = px2 = w;
7571 }
7572
7573 /* Close graticule from point 0 through point 4 */
7574
7575 if (gmt_M_is_rect_graticule(GMT)) { /* Simple rectangle in this projection */
7576 gmt_M_malloc2 (GMT, xx, yy, 5U, NULL, double);
7577 xx[0] = xx[4] = px0; xx[1] = px1; xx[2] = px2; xx[3] = px3;
7578 yy[0] = yy[1] = yy[4] = s; yy[2] = yy[3] = n;
7579 np = 5U;
7580 }
7581 else { /* Must assemble path from meridians and parallel pieces */
7582 double *xtmp = NULL, *ytmp = NULL;
7583 size_t add;
7584 uint64_t k;
7585
7586 /* SOUTH BORDER */
7587
7588 if (gmt_M_is_geographic (GMT, GMT_IN) && s == -90.0 && gmt_M_pole_is_point(GMT)) { /* No path, just a point */
7589 gmt_M_malloc2 (GMT, xx, yy, 1U, &n_alloc, double);
7590 xx[0] = px1; yy[0] = -90.0;
7591 }
7592 else
7593 n_alloc = gmtlib_latpath (GMT, s, px0, px1, &xx, &yy); /* South */
7594 np = n_alloc;
7595
7596 /* EAST (OR WEST) BORDER */
7597
7598 add = gmtlib_lonpath (GMT, px1, s, n, &xtmp, &ytmp); /* east (or west if dir == -1) */
7599 k = n_alloc + add;
7600 gmt_M_malloc2 (GMT, xx, yy, k, &n_alloc, double);
7601 gmt_M_memcpy (&xx[np], xtmp, add, double);
7602 gmt_M_memcpy (&yy[np], ytmp, add, double);
7603 np += add;
7604 gmt_M_free (GMT, xtmp); gmt_M_free (GMT, ytmp);
7605
7606 /* NORTH BORDER */
7607
7608 if (gmt_M_is_geographic (GMT, GMT_IN) && n == 90.0 && gmt_M_pole_is_point(GMT)) { /* No path, just a point */
7609 add = 0;
7610 gmt_M_malloc2 (GMT, xtmp, ytmp, 1U, &add, double);
7611 xtmp[0] = px3; ytmp[0] = +90.0;
7612 }
7613 else
7614 add = gmtlib_latpath (GMT, n, px2, px3, &xtmp, &ytmp); /* North */
7615
7616 k = n_alloc + add;
7617 gmt_M_malloc2 (GMT, xx, yy, k, &n_alloc, double);
7618 gmt_M_memcpy (&xx[np], xtmp, add, double);
7619 gmt_M_memcpy (&yy[np], ytmp, add, double);
7620 np += add;
7621 gmt_M_free (GMT, xtmp); gmt_M_free (GMT, ytmp);
7622
7623 /* WEST (OR EAST) BORDER */
7624
7625 add = gmtlib_lonpath (GMT, px3, n, s, &xtmp, &ytmp); /* west */
7626 k = n_alloc + add;
7627 gmt_M_malloc2 (GMT, xx, yy, k, &n_alloc, double);
7628 gmt_M_memcpy (&xx[np], xtmp, add, double);
7629 gmt_M_memcpy (&yy[np], ytmp, add, double);
7630 np += add;
7631 n_alloc = np;
7632 gmt_M_free (GMT, xtmp); gmt_M_free (GMT, ytmp);
7633 gmt_M_malloc2 (GMT, xx, yy, 0, &n_alloc, double);
7634 }
7635
7636 if (check && gmt_M_x_is_lon (GMT, GMT_IN)) {
7637 bool straddle;
7638 uint64_t i;
7639 straddle = (GMT->common.R.wesn[XLO] < 0.0 && GMT->common.R.wesn[XHI] > 0.0);
7640 for (i = 0; straddle && i < np; i++) {
7641 while (xx[i] < 0.0) xx[i] += 360.0;
7642 if (straddle && xx[i] > 180.0) xx[i] -= 360.0;
7643 }
7644 }
7645
7646 *x = xx;
7647 *y = yy;
7648 return (np);
7649 }
7650
7651 /*! . */
gmtlib_map_path(struct GMT_CTRL * GMT,double lon1,double lat1,double lon2,double lat2,double ** x,double ** y)7652 uint64_t gmtlib_map_path (struct GMT_CTRL *GMT, double lon1, double lat1, double lon2, double lat2, double **x, double **y) {
7653 if (doubleAlmostEqualZero (lat1, lat2))
7654 return (gmtlib_latpath (GMT, lat1, lon1, lon2, x, y));
7655 else
7656 return (gmtlib_lonpath (GMT, lon1, lat1, lat2, x, y));
7657 }
7658
7659 /*! . */
gmtlib_lonpath(struct GMT_CTRL * GMT,double lon,double lat1,double lat2,double ** x,double ** y)7660 uint64_t gmtlib_lonpath (struct GMT_CTRL *GMT, double lon, double lat1, double lat2, double **x, double **y) {
7661 size_t n_alloc = 0;
7662 uint64_t n, k;
7663 int n_try, pos;
7664 bool keep_trying;
7665 double dlat, dlat0, *tlon = NULL, *tlat = NULL, x0, x1, y0, y1, d, min_gap;
7666
7667 if (GMT->current.map.meridian_straight == 2) { /* Special non-sampling for gmtselect/grdlandmask */
7668 gmt_M_malloc2 (GMT, tlon, tlat, 2U, NULL, double);
7669 tlon[0] = tlon[1] = lon;
7670 tlat[0] = lat1; tlat[1] = lat2;
7671 *x = tlon;
7672 *y = tlat;
7673 return (2ULL);
7674 }
7675
7676 if (GMT->current.map.meridian_straight) { /* Easy, just a straight line connect via quarter-points */
7677 gmt_M_malloc2 (GMT, tlon, tlat, 5, &n_alloc, double);
7678 tlon[0] = tlon[1] = tlon[2] = tlon[3] = tlon[4] = lon;
7679 dlat = lat2 - lat1;
7680 tlat[0] = lat1; tlat[1] = lat1 + 0.25 * dlat; tlat[2] = lat1 + 0.5 * dlat;
7681 tlat[3] = lat1 + 0.75 * dlat; tlat[4] = lat2;
7682 *x = tlon;
7683 *y = tlat;
7684 return (n = n_alloc);
7685 }
7686
7687 /* Must do general case */
7688 n = 0;
7689 min_gap = 0.1 * GMT->current.setting.map_line_step;
7690 if ((n_alloc = lrint (ceil (fabs (lat2 - lat1) / GMT->current.map.dlat))) == 0) return (0);
7691
7692 n_alloc++; /* So n_alloc is at least 2 */
7693 dlat0 = (lat2 - lat1) / n_alloc;
7694 pos = (dlat0 > 0.0);
7695
7696 k = n_alloc; n_alloc = 0;
7697 gmt_M_malloc2 (GMT, tlon, tlat, k, &n_alloc, double);
7698
7699 tlon[0] = lon;
7700 tlat[0] = lat1;
7701 gmt_geo_to_xy (GMT, tlon[0], tlat[0], &x0, &y0);
7702 while ((pos && (tlat[n] < lat2)) || (!pos && (tlat[n] > lat2))) {
7703 n++;
7704 if (n == n_alloc-1) {
7705 n_alloc += GMT_SMALL_CHUNK;
7706 tlon = gmt_M_memory (GMT, tlon, n_alloc, double);
7707 tlat = gmt_M_memory (GMT, tlat, n_alloc, double);
7708 }
7709 n_try = 0;
7710 keep_trying = true;
7711 dlat = dlat0;
7712 tlon[n] = lon;
7713 do {
7714 n_try++;
7715 tlat[n] = tlat[n-1] + dlat;
7716 if (gmt_M_y_is_lat (GMT, GMT_IN) && fabs (tlat[n]) > 90.0) tlat[n] = copysign (90.0, tlat[n]);
7717 gmt_geo_to_xy (GMT, tlon[n], tlat[n], &x1, &y1);
7718 if ((*GMT->current.map.jump) (GMT, x0, y0, x1, y1) || (y0 < GMT->current.proj.rect[YLO] || y0 > GMT->current.proj.rect[YHI]))
7719 keep_trying = false;
7720 else {
7721 d = hypot (x1 - x0, y1 - y0);
7722 if (d > GMT->current.setting.map_line_step)
7723 dlat *= 0.5;
7724 else if (d < min_gap)
7725 dlat *= 2.0;
7726 else
7727 keep_trying = false;
7728 }
7729 } while (keep_trying && n_try < 10);
7730 x0 = x1; y0 = y1;
7731 }
7732 tlon[n] = lon;
7733 tlat[n] = lat2;
7734 n++;
7735
7736 if (n != n_alloc) {
7737 tlon = gmt_M_memory (GMT, tlon, n, double);
7738 tlat = gmt_M_memory (GMT, tlat, n, double);
7739 }
7740
7741 *x = tlon; *y = tlat;
7742 return (n);
7743 }
7744
7745 /*! . */
gmtlib_latpath(struct GMT_CTRL * GMT,double lat,double lon1,double lon2,double ** x,double ** y)7746 uint64_t gmtlib_latpath (struct GMT_CTRL *GMT, double lat, double lon1, double lon2, double **x, double **y) {
7747 size_t n_alloc = 0;
7748 uint64_t k, n;
7749 int n_try, pos;
7750 bool keep_trying;
7751 double dlon, dlon0, *tlon = NULL, *tlat = NULL, x0, x1, y0, y1, d, min_gap;
7752
7753 if (GMT->current.map.parallel_straight == 2) { /* Special non-sampling for gmtselect/grdlandmask */
7754 gmt_M_malloc2 (GMT, tlon, tlat, 2U, NULL, double);
7755 tlat[0] = tlat[1] = lat;
7756 tlon[0] = lon1; tlon[1] = lon2;
7757 *x = tlon; *y = tlat;
7758 return (2ULL);
7759 }
7760 if (GMT->current.map.parallel_straight) { /* Easy, just a straight line connection via quarter points */
7761 gmt_M_malloc2 (GMT, tlon, tlat, 5U, NULL, double);
7762 tlat[0] = tlat[1] = tlat[2] = tlat[3] = tlat[4] = lat;
7763 dlon = lon2 - lon1;
7764 tlon[0] = lon1; tlon[1] = lon1 + 0.25 * dlon; tlon[2] = lon1 + 0.5 * dlon;
7765 tlon[3] = lon1 + 0.75 * dlon; tlon[4] = lon2;
7766 *x = tlon; *y = tlat;
7767 return (5ULL);
7768 }
7769 /* Here we try to walk along lat for small increment in longitude to make sure our steps are smaller than the line_step */
7770 min_gap = 0.1 * GMT->current.setting.map_line_step;
7771 if ((n_alloc = lrint (ceil (fabs (lon2 - lon1) / GMT->current.map.dlon))) == 0) return (0); /* Initial guess to path length */
7772
7773 n_alloc++; /* n_alloc is at least 2 */
7774 dlon0 = (lon2 - lon1) / n_alloc;
7775 pos = (dlon0 > 0.0);
7776
7777 k = n_alloc; n_alloc = 0;
7778 gmt_M_malloc2 (GMT, tlon, tlat, k, &n_alloc, double);
7779 tlon[0] = lon1; tlat[0] = lat;
7780 gmt_geo_to_xy (GMT, tlon[0], tlat[0], &x0, &y0);
7781 n = 0;
7782 while ((pos && (tlon[n] < lon2)) || (!pos && (tlon[n] > lon2))) {
7783 n++;
7784 if (n == n_alloc) gmt_M_malloc2 (GMT, tlon, tlat, n, &n_alloc, double);
7785 n_try = 0;
7786 keep_trying = true;
7787 dlon = dlon0;
7788 tlat[n] = lat;
7789 do {
7790 n_try++;
7791 tlon[n] = tlon[n-1] + dlon;
7792 gmt_geo_to_xy (GMT, tlon[n], tlat[n], &x1, &y1);
7793 if ((*GMT->current.map.jump) (GMT, x0, y0, x1, y1) || (y0 < GMT->current.proj.rect[YLO] || y0 > GMT->current.proj.rect[YHI]))
7794 keep_trying = false;
7795 else {
7796 d = hypot (x1 - x0, y1 - y0);
7797 if (d > GMT->current.setting.map_line_step)
7798 dlon *= 0.5;
7799 else if (d < min_gap)
7800 dlon *= 2.0;
7801 else
7802 keep_trying = false;
7803 }
7804 } while (keep_trying && n_try < 10);
7805 x0 = x1; y0 = y1;
7806 }
7807 tlon[n] = lon2;
7808 tlat[n] = lat;
7809 n++;
7810 n_alloc = n;
7811 gmt_M_malloc2 (GMT, tlon, tlat, 0, &n_alloc, double);
7812
7813 *x = tlon; *y = tlat;
7814 return (n);
7815 }
7816
gmtmap_accept_the_jump(struct GMT_CTRL * GMT,double lon1,double lon0,double xx[],bool cartesian)7817 GMT_LOCAL bool gmtmap_accept_the_jump (struct GMT_CTRL *GMT, double lon1, double lon0, double xx[], bool cartesian) {
7818 /* Carefully examine if we really want to draw line from left to right boundary.
7819 * We want to avoid E-W wrapping lines for near-global areas where points simply move
7820 * from being > 180 degrees from the map area to < -180 even though the points do not
7821 * really reflect motion across the area */
7822 double dlon0, dlon1, dlon;
7823 int xm, ym;
7824 gmt_M_unused(xx);
7825 if (cartesian) return true; /* No wrap issues if Cartesian x,y */
7826 xm = GMT->current.map.this_x_status - GMT->current.map.prev_x_status;
7827 ym = GMT->current.map.this_y_status - GMT->current.map.prev_y_status;
7828 gmt_M_set_delta_lon (lon0, GMT->current.proj.central_meridian, dlon0);
7829 gmt_M_set_delta_lon (lon1, GMT->current.proj.central_meridian, dlon1);
7830 dlon = dlon1 - dlon0;
7831 /* When the line is so far away from the regino that it crosses 180 from the central meridian
7832 * we get false jumps. However, we find those by looking at the two dlons and that the change
7833 * in x status is 4 (going from left of region to right of region of the opposite). We then
7834 * reject the jump, otherwise we accept it. */
7835 //fprintf (stderr, "dlon0 = %g dlon1 = %g dlon = %g xx0 = %g xx1 = %g xm = %d ym = %d\n", dlon0, dlon1, dlon, xx[0], xx[1], xm, ym);
7836 if (!(fabs (dlon) > 180.0 && abs (xm) == 4 && ym == 0)) return true; /* Very unlikely to be a real wrap/jump */
7837 return false;
7838 }
7839
7840 /*! . */
gmt_cart_to_xy_line(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n)7841 uint64_t gmt_cart_to_xy_line (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n) {
7842 /* Cartesian data has no wrapping but may go outside in a benign way */
7843 uint64_t k;
7844
7845 while (n > GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7846
7847 for (k = 0; k < n; k++) {
7848 gmt_geo_to_xy (GMT, x[k], y[k], &GMT->current.plot.x[k], &GMT->current.plot.y[k]);
7849 GMT->current.plot.pen[k] = PSL_DRAW;
7850 }
7851 GMT->current.plot.pen[0] = PSL_MOVE;
7852 return (n);
7853 }
7854
7855 /*! . */
gmt_geo_to_xy_line(struct GMT_CTRL * GMT,double * lon,double * lat,uint64_t n)7856 uint64_t gmt_geo_to_xy_line (struct GMT_CTRL *GMT, double *lon, double *lat, uint64_t n) {
7857 /* Traces the lon/lat array and returns x,y plus appropriate pen moves
7858 * Pen moves are caused by breakthroughs of the map boundary or when
7859 * a point has lon = NaN or lat = NaN (this means "pick up pen") */
7860 uint64_t j, k, np, n_sections;
7861 bool this_inside, jump, cartesian = gmt_M_is_cartesian (GMT, GMT_IN);
7862 /* bool last_inside = false; */
7863 unsigned int sides[4];
7864 unsigned int nx;
7865 double xlon[4], xlat[4], xx[4], yy[4];
7866 double this_x, this_y, last_x, last_y, dummy[4];
7867
7868 if (n == 0) return 0; /* Absolutely nothing to do */
7869 while (n > GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7870
7871 /* Here we know n is at least 1 */
7872 np = 0;
7873 gmt_geo_to_xy (GMT, lon[0], lat[0], &last_x, &last_y);
7874 if (!gmt_map_outside (GMT, lon[0], lat[0])) { /* First point is inside the region */
7875 GMT->current.plot.x[0] = last_x; GMT->current.plot.y[0] = last_y;
7876 GMT->current.plot.pen[np++] = PSL_MOVE;
7877 /* last_inside = true; */
7878 }
7879 for (j = 1; j < n; j++) {
7880 gmt_geo_to_xy (GMT, lon[j], lat[j], &this_x, &this_y);
7881 this_inside = !gmt_map_outside (GMT, lon[j], lat[j]);
7882 if (gmt_M_is_dnan (lon[j]) || gmt_M_is_dnan (lat[j])) continue; /* Skip NaN point now */
7883 if (gmt_M_is_dnan (lon[j-1]) || gmt_M_is_dnan (lat[j-1])) { /* Point after NaN needs a move */
7884 GMT->current.plot.x[np] = this_x; GMT->current.plot.y[np] = this_y;
7885 GMT->current.plot.pen[np++] = PSL_MOVE;
7886 if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7887 last_x = this_x; last_y = this_y; /* last_inside = this_inside; */
7888 continue;
7889 }
7890 if ((nx = gmtmap_crossing (GMT, lon[j-1], lat[j-1], lon[j], lat[j], xlon, xlat, xx, yy, sides))) { /* Do nothing if we get crossings*/ }
7891 else if (GMT->current.map.is_world) /* Check global wrapping if 360 range */
7892 nx = (*GMT->current.map.wrap_around_check) (GMT, dummy, last_x, last_y, this_x, this_y, xx, yy, sides);
7893 if (nx == 1) { /* inside-outside or outside-inside; set move&clip vs draw flags */
7894 GMT->current.plot.x[np] = xx[0]; GMT->current.plot.y[np] = yy[0];
7895 /* If next point is inside then we want to move to the crossing, otherwise we want to draw to the crossing */
7896 GMT->current.plot.pen[np++] = (this_inside) ? PSL_MOVE|PSL_CLIP : PSL_DRAW|PSL_CLIP;
7897 if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7898 }
7899 else if (nx == 2) { /* outside-inside-outside or (with wrapping) inside-outside-inside */
7900 /* PW: I will be working on things here to solve the polygon wrap problem reported by Nicky */
7901 jump = gmtmap_accept_the_jump (GMT, lon[j], lon[j-1], xx, cartesian);
7902 if (jump) {
7903 //if ((this_inside && last_inside) || cartesian || dy > 0.1) { /* outside-inside-outside or (with wrapping) inside-outside-inside */
7904 GMT->current.plot.x[np] = xx[0]; GMT->current.plot.y[np] = yy[0];
7905 GMT->current.plot.pen[np++] = (this_inside) ? PSL_DRAW|PSL_CLIP : PSL_MOVE|PSL_CLIP;
7906 if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7907 GMT->current.plot.x[np] = xx[1]; GMT->current.plot.y[np] = yy[1];
7908 GMT->current.plot.pen[np++] = (this_inside) ? PSL_MOVE|PSL_CLIP : PSL_DRAW|PSL_CLIP;
7909 if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7910 }
7911 }
7912 if (this_inside) {
7913 if ( np >= GMT->current.plot.n_alloc ) {
7914 GMT_Report (GMT->parent, GMT_MSG_ERROR, "bad access: cannot access current.plot.x[%" PRIu64 "], np=%" PRIu64 ", GMT->current.plot.n=%" PRIu64 "\n", np, np, GMT->current.plot.n);
7915 }
7916 else {
7917 GMT->current.plot.x[np] = this_x; GMT->current.plot.y[np] = this_y;
7918 GMT->current.plot.pen[np++] = PSL_DRAW;
7919 }
7920 if (np == GMT->current.plot.n_alloc) gmt_get_plot_array (GMT);
7921 }
7922 last_x = this_x; last_y = this_y; /* last_inside = this_inside; */
7923 }
7924 if (np) GMT->current.plot.pen[0] |= PSL_MOVE; /* Sanity override: Gotta start off with new start point */
7925
7926 /* When a line that starts and ends inside the domain exits and reenters, we end up with two pieces.
7927 * When these are plotted separately the sections are not joined properly and ugly gaps can appear, especially if
7928 * the pen width is large. Here we try to fix this case since it happens fairly frequently */
7929
7930 for (j = n_sections = k = 0; j < np; j++) if (GMT->current.plot.pen[j] & PSL_MOVE) {
7931 n_sections++;
7932 if (n_sections == 2) k = j; /* Start of 2nd section */
7933 }
7934 if (n_sections == 2 && doubleAlmostEqualZero (GMT->current.plot.x[0], GMT->current.plot.x[np-1]) && doubleAlmostEqualZero (GMT->current.plot.y[0], GMT->current.plot.y[np-1])) {
7935 double *tmp = gmt_M_memory (GMT, NULL, np, double);
7936 /* Shuffle x-array safely */
7937 gmt_M_memcpy (tmp, &GMT->current.plot.x[k], np-k, double);
7938 gmt_M_memcpy (&tmp[np-k], GMT->current.plot.x, k, double);
7939 gmt_M_memcpy (GMT->current.plot.x, tmp, np, double);
7940 /* Shuffle y-array safely */
7941 gmt_M_memcpy (tmp, &GMT->current.plot.y[k], np-k, double);
7942 gmt_M_memcpy (&tmp[np-k], GMT->current.plot.y, k, double);
7943 gmt_M_memcpy (GMT->current.plot.y, tmp, np, double);
7944 /* Change PSL_MOVE to PSL_DRAW at start of 2nd section */
7945 GMT->current.plot.pen[k] = PSL_DRAW;
7946 if (k && GMT->current.plot.pen[k-1] & PSL_CLIP) GMT->current.plot.pen[k-1] = PSL_DRAW;
7947 if (k < (np-1) && GMT->current.plot.pen[k+1] & PSL_CLIP) GMT->current.plot.pen[k+1] = PSL_DRAW;
7948 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_geo_to_xy_line: Clipping in two separate abutting lines that were joined into a single line\n");
7949 gmt_M_free (GMT, tmp);
7950 }
7951
7952 return (np);
7953 }
7954
7955 /*! . */
gmt_compact_line(struct GMT_CTRL * GMT,double * x,double * y,uint64_t n,int pen_flag,int * pen)7956 uint64_t gmt_compact_line (struct GMT_CTRL *GMT, double *x, double *y, uint64_t n, int pen_flag, int *pen) {
7957 /* true if pen movements is present */
7958 /* gmt_compact_line will remove unnecessary points in paths, but does not reallocate to the shorter size */
7959 uint64_t i, j;
7960 double old_slope, new_slope, dx;
7961 char *flag = NULL;
7962
7963 if (n < 3) return (n); /* Nothing to do */
7964 flag = gmt_M_memory (GMT, NULL, n, char);
7965
7966 dx = x[1] - x[0];
7967 old_slope = (doubleAlmostEqualZero (x[1], x[0])) ? copysign (HALF_DBL_MAX, y[1] - y[0]) : (y[1] - y[0]) / dx;
7968
7969 for (i = 1; i < n-1; ++i) {
7970 dx = x[i+1] - x[i];
7971 new_slope = (doubleAlmostEqualZero (x[i+1], x[i])) ? copysign (HALF_DBL_MAX, y[i+1] - y[i]) : (y[i+1] - y[i]) / dx;
7972 if (doubleAlmostEqualZero (new_slope, old_slope) && !(pen_flag && (pen[i]+pen[i+1]) > 4)) /* 4 is 2+2 which is draw line; a 3 will produce > 4 */
7973 flag[i] = 1;
7974 else
7975 old_slope = new_slope;
7976 }
7977
7978 for (i = j = 1; i < n; i++) { /* i = 1 since first point must be included */
7979 if (flag[i] == 0) {
7980 x[j] = x[i];
7981 y[j] = y[i];
7982 if (pen_flag) pen[j] = pen[i];
7983 j++;
7984 }
7985 }
7986 gmt_M_free (GMT, flag);
7987
7988 return (j);
7989 }
7990
7991 /* Routines to transform grdfiles to/from map projections */
7992
7993 /*! . */
gmt_project_init(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,double * inc,unsigned int n_columns,unsigned int n_rows,unsigned int dpi,unsigned int offset)7994 int gmt_project_init (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, double *inc, unsigned int n_columns, unsigned int n_rows, unsigned int dpi, unsigned int offset) {
7995 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
7996 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_project_init: IN: Inc [%.12g/%.12g] n_columns/n_rows [%u/%u] dpi = %u offset = %u\n",
7997 inc[0], inc[1], n_columns, n_rows, dpi, offset);
7998
7999 if (inc[GMT_X] > 0.0 && inc[GMT_Y] > 0.0) {
8000 if (GMT->current.io.inc_code[GMT_X] || GMT->current.io.inc_code[GMT_Y]) { /* Must convert from distance units to degrees */
8001 gmt_M_memcpy (header->inc, inc, 2, double); /* Set these temporarily as the grids incs */
8002 gmt_RI_prepare (GMT, header); /* Converts the proper xinc/yinc units */
8003 gmt_M_memcpy (inc, header->inc, 2, double); /* Restore the inc array for use below */
8004 GMT->current.io.inc_code[GMT_X] = GMT->current.io.inc_code[GMT_Y] = 0;
8005 }
8006 #if 0
8007 if (<to ensure x-ymax is adjusted so the increments are exact>) {
8008 header->wesn[XHI] = ceil (header->wesn[XHI] / inc[GMT_X]) * inc[GMT_X];
8009 header->wesn[YHI] = ceil (header->wesn[YHI] / inc[GMT_Y]) * inc[GMT_Y];
8010 }
8011 #endif
8012 header->n_columns = gmt_M_get_n (GMT, header->wesn[XLO], header->wesn[XHI], inc[GMT_X], offset);
8013 header->n_rows = gmt_M_get_n (GMT, header->wesn[YLO], header->wesn[YHI], inc[GMT_Y], offset);
8014 header->inc[GMT_X] = gmt_M_get_inc (GMT, header->wesn[XLO], header->wesn[XHI], header->n_columns, offset);
8015 header->inc[GMT_Y] = gmt_M_get_inc (GMT, header->wesn[YLO], header->wesn[YHI], header->n_rows, offset);
8016 }
8017 else if (n_columns > 0 && n_rows > 0) {
8018 header->n_columns = n_columns; header->n_rows = n_rows;
8019 header->inc[GMT_X] = gmt_M_get_inc (GMT, header->wesn[XLO], header->wesn[XHI], header->n_columns, offset);
8020 header->inc[GMT_Y] = gmt_M_get_inc (GMT, header->wesn[YLO], header->wesn[YHI], header->n_rows, offset);
8021 }
8022 else if (dpi > 0) {
8023 header->n_columns = urint ((header->wesn[XHI] - header->wesn[XLO]) * dpi) + 1 - offset;
8024 header->n_rows = urint ((header->wesn[YHI] - header->wesn[YLO]) * dpi) + 1 - offset;
8025 header->inc[GMT_X] = gmt_M_get_inc (GMT, header->wesn[XLO], header->wesn[XHI], header->n_columns, offset);
8026 header->inc[GMT_Y] = gmt_M_get_inc (GMT, header->wesn[YLO], header->wesn[YHI], header->n_rows, offset);
8027 }
8028 else {
8029 GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_project_init: Necessary arguments not set\n");
8030 return GMT_PROJECTION_ERROR;
8031 }
8032 header->registration = offset;
8033 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_project_init: OUT: Inc [%.12g/%.12g] n_columns/n_rows [%u/%u] dpi = %u offset = %u\n",
8034 inc[0], inc[1], n_columns, n_rows, dpi, offset);
8035
8036 gmt_RI_prepare (GMT, header); /* Ensure -R -I consistency and set n_columns, n_rows */
8037 gmt_M_err_pass (GMT, gmt_grd_RI_verify (GMT, header, 1), "");
8038 if (!HH->reset_pad) gmt_M_grd_setpad (GMT, header, GMT->current.io.pad); /* Assign default pad */
8039 gmt_set_grddim (GMT, header); /* Set all dimensions before returning */
8040
8041 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid projection from size %dx%d to %dx%d\n", n_columns, n_rows, header->n_columns, header->n_rows);
8042 return (GMT_NOERROR);
8043 }
8044
8045 /*! . */
gmt_grd_project(struct GMT_CTRL * GMT,struct GMT_GRID * I,struct GMT_GRID * O,bool inverse)8046 int gmt_grd_project (struct GMT_CTRL *GMT, struct GMT_GRID *I, struct GMT_GRID *O, bool inverse) {
8047 /* Generalized grid projection that deals with both interpolation and averaging effects.
8048 * It requires the input grid to have 2 boundary rows/cols so that the bcr
8049 * functions can be used. The I struct represents the input grid which is either in original
8050 * (i.e., lon/lat) coordinates or projected x/y (if inverse = true).
8051 *
8052 * I: Grid and header with input grid on a padded grid with 2 extra rows/columns
8053 * O: Grid and header for output grid, no padding needed (but allowed)
8054 * inverse: true if input is x/y and we want to invert for a lon/lat grid
8055 *
8056 * In addition, these settings (via -n) control interpolation:
8057 * antialias: true if we need to do the antialiasing STEP 1 (below)
8058 * interpolant: 0 = nearest neighbor, 1 = bilinear, 2 = B-spline, 3 = bicubic
8059 * threshold: minimum weight to be used. If weight < threshold interpolation yields NaN.
8060 * We initialize the O->data array to NaN.
8061 *
8062 * Changed 10-Sep-07 to include the argument "antialias" and "threshold" and
8063 * made "interpolant" an integer (was int bilinear).
8064 */
8065
8066 bool in = false, out = false;
8067 int col_in, row_in, col_out, row_out;
8068 uint64_t ij_in, ij_out;
8069 short int *nz = NULL;
8070 double x_proj = 0.0, y_proj = 0.0, z_int, inv_nz;
8071 double *x_in = NULL, *x_out = NULL, *x_in_proj = NULL, *x_out_proj = NULL;
8072 double *y_in = NULL, *y_out = NULL, *y_in_proj = NULL, *y_out_proj = NULL;
8073 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
8074 struct GMT_GRID *I2 = NULL;
8075
8076 /* Only input grid MUST have at least 2 rows/cols padding - otherwise we must allocate a temp grid */
8077 if (I->header->pad[XLO] < 2 || I->header->pad[XHI] < 2 || I->header->pad[YLO] < 2 || I->header->pad[YHI] < 2) {
8078 unsigned int def_pad[4], pad2[4] = {2, 2, 2, 2};
8079 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: Input grid has insufficient padding - create and work on a duplicate with r row/col pad\n");
8080 gmt_M_memcpy (def_pad, GMT->current.io.pad, 4, unsigned int); /* Save default pad */
8081 gmt_M_memcpy (GMT->current.io.pad, pad2, 4, unsigned int); /* Change default pad */
8082 if ((I2 = GMT_Duplicate_Data (GMT->parent, GMT_IS_GRID, GMT_DUPLICATE_DATA | GMT_DUPLICATE_RESET, I)) == NULL) {
8083 GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_grd_project: Unable to duplicate grid with padding\n");
8084 return GMT_RUNTIME_ERROR;
8085 }
8086 gmt_BC_init (GMT, I2->header); /* Initialize grid interpolation and boundary condition parameters */
8087 gmt_grd_BC_set (GMT, I2, GMT_IN); /* Set boundary conditions */
8088 I = I2; /* Use this input grid instead */
8089 gmt_M_memcpy (GMT->current.io.pad, def_pad, 4, unsigned int); /* Restore default pad */
8090 }
8091
8092 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_grd_project: In [%.12g/%.12g/%.12g/%.12g] and out [%.12g/%.12g/%.12g/%.12g]\n",
8093 I->header->wesn[XLO], I->header->wesn[XHI], I->header->wesn[YLO], I->header->wesn[YHI],
8094 O->header->wesn[XLO], O->header->wesn[XHI], O->header->wesn[YLO], O->header->wesn[YHI]);
8095
8096 /* Precalculate grid coordinates */
8097
8098 if (I->x && HH->reset_pad == 0) { /* Reuse existing arrays if present, but not if grid has had its pad adjusted to make subsets of read-only memory grids */
8099 x_in = I->x; y_in = I->y; in = true;
8100 }
8101 else { /* Must allocate here */
8102 x_in = gmt_grd_coord (GMT, I->header, GMT_X);
8103 y_in = gmt_grd_coord (GMT, I->header, GMT_Y);
8104 }
8105 if (O->x) { /* Reuse existing arrays */
8106 x_out = O->x; y_out = O->y; out = true;
8107 }
8108 else { /* Must allocate here */
8109 x_out = gmt_grd_coord (GMT, O->header, GMT_X);
8110 y_out = gmt_grd_coord (GMT, O->header, GMT_Y);
8111 }
8112
8113 if (gmt_M_is_rect_graticule (GMT)) { /* Since lon/lat parallels x/y it pays to precalculate projected grid coordinates up front */
8114 x_in_proj = gmt_M_memory (GMT, NULL, I->header->n_columns, double);
8115 y_in_proj = gmt_M_memory (GMT, NULL, I->header->n_rows, double);
8116 x_out_proj = gmt_M_memory (GMT, NULL, O->header->n_columns, double);
8117 y_out_proj = gmt_M_memory (GMT, NULL, O->header->n_rows, double);
8118 if (inverse) {
8119 gmt_M_row_loop (GMT, I, row_in) gmt_xy_to_geo (GMT, &x_proj, &y_in_proj[row_in], I->header->wesn[XLO], y_in[row_in]);
8120 gmt_M_col_loop2 (GMT, I, col_in) gmt_xy_to_geo (GMT, &x_in_proj[col_in], &y_proj, x_in[col_in], I->header->wesn[YLO]);
8121 gmt_M_row_loop (GMT, O, row_out) gmt_geo_to_xy (GMT, I->header->wesn[YLO], y_out[row_out], &x_proj, &y_out_proj[row_out]);
8122 gmt_M_col_loop2 (GMT, O, col_out) gmt_geo_to_xy (GMT, x_out[col_out], I->header->wesn[YLO], &x_out_proj[col_out], &y_proj);
8123 }
8124 else {
8125 gmt_M_row_loop (GMT, I, row_in) gmt_geo_to_xy (GMT, I->header->wesn[XLO], y_in[row_in], &x_proj, &y_in_proj[row_in]);
8126 gmt_M_col_loop2 (GMT, I, col_in) gmt_geo_to_xy (GMT, x_in[col_in], I->header->wesn[YLO], &x_in_proj[col_in], &y_proj);
8127 gmt_M_row_loop (GMT, O, row_out) gmt_xy_to_geo (GMT, &x_proj, &y_out_proj[row_out], I->header->wesn[YLO], y_out[row_out]);
8128 gmt_M_col_loop2 (GMT, O, col_out) { /* Here we must also align longitudes properly */
8129 gmt_xy_to_geo (GMT, &x_out_proj[col_out], &y_proj, x_out[col_out], I->header->wesn[YLO]);
8130 if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_out_proj[col_out])) {
8131 while (x_out_proj[col_out] < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_out_proj[col_out] += 360.0;
8132 while (x_out_proj[col_out] > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_out_proj[col_out] -= 360.0;
8133 }
8134 }
8135 }
8136 }
8137
8138 gmt_M_grd_loop (GMT, O, row_out, col_out, ij_out) O->data[ij_out] = GMT->session.f_NaN; /* So that nodes outside will retain a NaN value */
8139
8140 /* PART 1: Project input grid points and do a blockmean operation */
8141
8142 O->header->z_min = FLT_MAX; O->header->z_max = -FLT_MAX; /* Min/max for out */
8143 if (GMT->common.n.antialias) { /* Blockaverage repeat pixels, at least the first ~32767 of them... */
8144 bool skip_repeat = false, duplicate_east = false;
8145 int n_columns = O->header->n_columns, n_rows = O->header->n_rows, kase, duplicate_col;
8146 nz = gmt_M_memory (GMT, NULL, O->header->size, short int);
8147 /* Cannot do OPENMP yet here since it would require a reduction into an output array (nz) */
8148 kase = gmt_whole_earth (GMT, I->header->wesn, GMT->common.R.wesn);
8149 if (kase == 1 && I->header->registration == GMT_GRID_NODE_REG) {
8150 /* Need to avoid giving the repeated east and west meridian values double weight when they plot inside the image.
8151 * This is only likely to happen when external global grids are passed in via GMT_IS_REFERENCE. */
8152 skip_repeat = true; /* Since both -180 and +180 fall inside the image, we only want to use one of then (-180) */
8153 duplicate_east = gmt_M_is_periodic (GMT); /* Since the meridian corresponding to map west only appears once we may need to duplicate on east */
8154 if (duplicate_east) /* Find the column in I->data that corresponds to the longitude of the left boundary of the map */
8155 duplicate_col = gmt_M_grd_x_to_col (GMT, GMT->common.R.wesn[XLO], I->header);
8156 }
8157
8158 gmt_M_row_loop (GMT, I, row_in) { /* Loop over the input grid row coordinates */
8159 if (gmt_M_is_rect_graticule (GMT)) y_proj = y_in_proj[row_in];
8160 gmt_M_col_loop (GMT, I, row_in, col_in, ij_in) { /* Loop over the input grid col coordinates */
8161 if (skip_repeat && col_in == 0) continue;
8162 if (gmt_M_is_rect_graticule (GMT))
8163 x_proj = x_in_proj[col_in];
8164 else if (inverse)
8165 gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_in[col_in], y_in[row_in]);
8166 else {
8167 if (GMT->current.map.outside (GMT, x_in[col_in], y_in[row_in])) continue; /* Quite possible we are beyond the horizon */
8168 gmt_geo_to_xy (GMT, x_in[col_in], y_in[row_in], &x_proj, &y_proj);
8169 }
8170
8171 /* Here, (x_proj, y_proj) is the projected grid point. Now find nearest node on the output grid */
8172
8173 row_out = gmt_M_grd_y_to_row (GMT, y_proj, O->header);
8174 if (row_out < 0 || row_out >= n_rows) continue; /* Outside our grid region */
8175 col_out = gmt_M_grd_x_to_col (GMT, x_proj, O->header);
8176 if (col_out < 0 || col_out >= n_columns) continue; /* Outside our grid region */
8177
8178 /* OK, this projected point falls inside the projected grid's rectangular domain */
8179
8180 ij_out = gmt_M_ijp (O->header, row_out, col_out); /* The output node */
8181 if (nz[ij_out] == 0) O->data[ij_out] = 0.0f; /* First time, override the initial value */
8182 if (nz[ij_out] < SHRT_MAX) { /* Avoid overflow */
8183 O->data[ij_out] += I->data[ij_in]; /* Add up the z-sum inside this rect... */
8184 nz[ij_out]++; /* ..and how many points there were */
8185 }
8186 if (gmt_M_is_fnan (O->data[ij_out])) continue;
8187 if (O->data[ij_out] < O->header->z_min) O->header->z_min = O->data[ij_out];
8188 else if (O->data[ij_out] > O->header->z_max) O->header->z_max = O->data[ij_out];
8189 }
8190 if (duplicate_east) {
8191 ij_in = ij_in - I->header->n_columns + duplicate_col; /* Rewind to the col to be duplicated */
8192 col_in = duplicate_col;
8193 if (gmt_M_is_rect_graticule (GMT))
8194 x_proj = GMT->current.proj.rect[XHI];
8195 else if (inverse)
8196 gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_in[col_in], y_in[row_in]);
8197 else {
8198 if (GMT->current.map.outside (GMT, x_in[col_in], y_in[row_in])) continue; /* Quite possible we are beyond the horizon */
8199 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], y_in[row_in], &x_proj, &y_proj);
8200 }
8201
8202 /* Here, (x_proj, y_proj) is the projected grid point. Now find nearest node on the output grid */
8203
8204 row_out = gmt_M_grd_y_to_row (GMT, y_proj, O->header);
8205 if (row_out < 0 || row_out >= n_rows) continue; /* Outside our grid region */
8206 col_out = gmt_M_grd_x_to_col (GMT, x_proj, O->header);
8207 if (col_out < 0 || col_out >= n_columns) continue; /* Outside our grid region */
8208
8209 /* OK, this projected point falls inside the projected grid's rectangular domain */
8210
8211 ij_out = gmt_M_ijp (O->header, row_out, col_out); /* The output node */
8212 if (nz[ij_out] == 0) O->data[ij_out] = 0.0f; /* First time, override the initial value */
8213 if (nz[ij_out] < SHRT_MAX) { /* Avoid overflow */
8214 O->data[ij_out] += I->data[ij_in]; /* Add up the z-sum inside this rect... */
8215 nz[ij_out]++; /* ..and how many points there were */
8216 }
8217 if (gmt_M_is_fnan (O->data[ij_out])) continue;
8218 if (O->data[ij_out] < O->header->z_min) O->header->z_min = O->data[ij_out];
8219 else if (O->data[ij_out] > O->header->z_max) O->header->z_max = O->data[ij_out];
8220
8221 }
8222 }
8223 #ifdef DEBUG
8224 if (GMT->common.n.save_debug) { /* Write nz as a debug grid to file */
8225 uint64_t k;
8226 struct GMT_GRID *G = NULL;
8227 if ((G = GMT_Create_Data (GMT->parent, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, O->header->wesn, O->header->inc, \
8228 O->header->registration, GMT_PAD_DEFAULT, NULL)) == NULL) goto bail_grd_dbg;
8229 for (k = 0; k < G->header->size; k++) G->data[k] = (float)nz[k];
8230 (void) GMT_Write_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, "nz_grd_counter.grd", G);
8231 GMT_Report (GMT->parent, GMT_MSG_NOTICE, "gmt_grd_project: Antialias counter nz written to grid file nz_grd_counter.grd\n");
8232 GMT_Destroy_Data (GMT->parent, &G);
8233 }
8234 bail_grd_dbg:
8235 ij_out = 0;
8236 #endif
8237 }
8238
8239 /* PART 2: Create weighted average of interpolated and observed points */
8240
8241 /* Open MP does not work yet */
8242
8243 /* The OpenMP loop below fails and yields nodes still set to NaN. I cannot see any errors but obviously
8244 * there is something that is not quite correct. */
8245
8246 //#ifdef _OPENMP
8247 //#pragma omp parallel for private(row_out,y_proj,col_out,ij_out,x_proj,z_int,inv_nz) shared(O,GMT,y_out_proj,x_out_proj,inverse,x_out,y_out,I,nz)
8248 //#endif
8249 for (row_out = 0; row_out < (int)O->header->n_rows; row_out++) { /* Loop over the output grid row coordinates */
8250 if (gmt_M_is_rect_graticule (GMT)) y_proj = y_out_proj[row_out];
8251 gmt_M_col_loop (GMT, O, row_out, col_out, ij_out) { /* Loop over the output grid col coordinates */
8252 if (gmt_M_is_rect_graticule (GMT))
8253 x_proj = x_out_proj[col_out];
8254 else if (inverse)
8255 gmt_geo_to_xy (GMT, x_out[col_out], y_out[row_out], &x_proj, &y_proj);
8256 else {
8257 gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_out[col_out], y_out[row_out]);
8258 if (GMT->current.proj.projection_GMT == GMT_GENPER && GMT->current.proj.g_outside) continue; /* We are beyond the horizon */
8259
8260 /* On 17-Sep-2007 the slack of GMT_CONV4_LIMIT was added to allow for round-off
8261 errors in the grid limits. */
8262 if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_proj)) {
8263 while (x_proj < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_proj += 360.0;
8264 while (x_proj > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_proj -= 360.0;
8265 }
8266 }
8267
8268 /* Here, (x_proj, y_proj) is the inversely projected grid point. Now the interpret the input grid at that projected output point */
8269
8270 z_int = gmt_bcr_get_z (GMT, I, x_proj, y_proj);
8271
8272 if (!GMT->common.n.antialias || nz[ij_out] < 2) /* Just use the interpolated value */
8273 O->data[ij_out] = (gmt_grdfloat)z_int;
8274 else if (gmt_M_is_dnan (z_int)) { /* Take the average of what we accumulated */
8275 if (nz[ij_out]) O->data[ij_out] /= nz[ij_out]; /* Plain average */
8276 }
8277 else { /* Weighted average between blockmean'ed and interpolated values */
8278 inv_nz = 1.0 / nz[ij_out];
8279 O->data[ij_out] = (gmt_grdfloat) ((O->data[ij_out] + z_int * inv_nz) / (nz[ij_out] + inv_nz));
8280 }
8281 if (O->data[ij_out] < O->header->z_min) O->header->z_min = O->data[ij_out];
8282 if (O->data[ij_out] > O->header->z_max) O->header->z_max = O->data[ij_out];
8283 }
8284 }
8285
8286 if (O->header->z_min < I->header->z_min || O->header->z_max > I->header->z_max) { /* Truncate output to input extrema */
8287 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: Output grid extrema [%g/%g] exceed extrema of input grid [%g/%g] due to resampling\n",
8288 O->header->z_min, O->header->z_max, I->header->z_min, I->header->z_max);
8289 if (GMT->common.n.truncate) {
8290 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: Output grid clipped to input grid extrema\n");
8291 gmt_M_grd_loop (GMT, O, row_out, col_out, ij_out) {
8292 if (O->data[ij_out] < I->header->z_min) O->data[ij_out] = (gmt_grdfloat)I->header->z_min;
8293 else if (O->data[ij_out] > I->header->z_max) O->data[ij_out] = (gmt_grdfloat)I->header->z_max;
8294 }
8295 O->header->z_min = I->header->z_min; O->header->z_max = I->header->z_max;
8296 }
8297 else
8298 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_grd_project: See option -n+c to clip resampled output range to given input range\n");
8299 }
8300
8301 /* Time to clean up our mess */
8302
8303 if (!in) {
8304 gmt_M_free (GMT, x_in);
8305 gmt_M_free (GMT, y_in);
8306 }
8307 if (!out) {
8308 gmt_M_free (GMT, x_out);
8309 gmt_M_free (GMT, y_out);
8310 }
8311 if (gmt_M_is_rect_graticule(GMT)) {
8312 gmt_M_free (GMT, x_in_proj);
8313 gmt_M_free (GMT, y_in_proj);
8314 gmt_M_free (GMT, x_out_proj);
8315 gmt_M_free (GMT, y_out_proj);
8316 }
8317 if (GMT->common.n.antialias) gmt_M_free (GMT, nz);
8318
8319 if (I2 && GMT_Destroy_Data (GMT->parent, &I2)) {
8320 GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_grd_project: Unable to free padded grid\n");
8321 return GMT_RUNTIME_ERROR;
8322 }
8323 return (GMT_NOERROR);
8324 }
8325
8326 /*! . */
gmt_img_project(struct GMT_CTRL * GMT,struct GMT_IMAGE * I,struct GMT_IMAGE * O,bool inverse)8327 int gmt_img_project (struct GMT_CTRL *GMT, struct GMT_IMAGE *I, struct GMT_IMAGE *O, bool inverse) {
8328 /* Generalized image projection that deals with both interpolation and averaging effects.
8329 * It requires the input image to have 2 boundary rows/cols so that the bcr
8330 * functions can be used. The I struct represents the input image which is either in original
8331 * (i.e., lon/lat) coordinates or projected x/y (if inverse = true).
8332 *
8333 * I: Image and header with input image on a padded image with 2 extra rows/columns
8334 * O: Image and header for output image, no padding needed (but allowed)
8335 * edgeinfo: Structure with information about boundary conditions on input image
8336 * inverse: true if input is x/y and we want to invert for a lon/lat image
8337 *
8338 * In addition, these settings (via -n) control interpolation:
8339 * antialias: true if we need to do the antialiasing STEP 1 (below)
8340 * interpolant: 0 = nearest neighbor, 1 = bilinear, 2 = B-spline, 3 = bicubic
8341 * threshold: minimum weight to be used. If weight < threshold interpolation yields NaN.
8342 *
8343 * We initialize the O->data array to the NaN color.
8344 *
8345 * Changed 10-Sep-07 to include the argument "antialias" and "threshold" and
8346 * made "interpolant" an integer (was int bilinear).
8347 */
8348
8349 bool in = false, out = false;
8350 int col_in, row_in, col_out, row_out, b, nb = I->header->n_bands;
8351 uint64_t ij_in, ij_out;
8352 short int *nz = NULL;
8353 double x_proj = 0.0, y_proj = 0.0, inv_nz, rgb[4];
8354 double *x_in = NULL, *x_out = NULL, *x_in_proj = NULL, *x_out_proj = NULL;
8355 double *y_in = NULL, *y_out = NULL, *y_in_proj = NULL, *y_out_proj = NULL;
8356 unsigned char z_int[4], z_int_bg[4];
8357 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
8358
8359 /* Only input image MUST have at least 2 rows/cols padding */
8360 if (I->header->pad[XLO] < 2 || I->header->pad[XHI] < 2 || I->header->pad[YLO] < 2 || I->header->pad[YHI] < 2) {
8361 GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_img_project: Input image does not have sufficient (2) padding\n");
8362 return GMT_RUNTIME_ERROR;
8363 }
8364
8365 /* Precalculate grid coordinates */
8366
8367 if (I->x && HH->reset_pad == 0) { /* Reuse existing arrays if present, but not if grid has had its pad adjusted to make subsets of read-only memory grids */
8368 x_in = I->x; y_in = I->y; in = true;
8369 }
8370 else { /* Must allocate here */
8371 x_in = gmt_grd_coord (GMT, I->header, GMT_X);
8372 y_in = gmt_grd_coord (GMT, I->header, GMT_Y);
8373 }
8374 if (O->x) { /* Reuse existing arrays */
8375 x_out = O->x; y_out = O->y; out = true;
8376 }
8377 else { /* Must allocate here */
8378 x_out = gmt_grd_coord (GMT, O->header, GMT_X);
8379 y_out = gmt_grd_coord (GMT, O->header, GMT_Y);
8380 }
8381
8382 if (gmt_M_is_rect_graticule (GMT)) { /* Since lon/lat parallels x/y it pays to precalculate projected grid coordinates up front */
8383 x_in_proj = gmt_M_memory (GMT, NULL, I->header->n_columns, double);
8384 y_in_proj = gmt_M_memory (GMT, NULL, I->header->n_rows, double);
8385 x_out_proj = gmt_M_memory (GMT, NULL, O->header->n_columns, double);
8386 y_out_proj = gmt_M_memory (GMT, NULL, O->header->n_rows, double);
8387 if (inverse) {
8388 gmt_M_row_loop (GMT, I, row_in) gmt_xy_to_geo (GMT, &x_proj, &y_in_proj[row_in], I->header->wesn[XLO], y_in[row_in]);
8389 gmt_M_col_loop2 (GMT, I, col_in) gmt_xy_to_geo (GMT, &x_in_proj[col_in], &y_proj, x_in[col_in], I->header->wesn[YLO]);
8390 gmt_M_row_loop (GMT, O, row_out) gmt_geo_to_xy (GMT, I->header->wesn[YLO], y_out[row_out], &x_proj, &y_out_proj[row_out]);
8391 gmt_M_col_loop2 (GMT, O, col_out) gmt_geo_to_xy (GMT, x_out[col_out], I->header->wesn[YLO], &x_out_proj[col_out], &y_proj);
8392 }
8393 else {
8394 gmt_M_row_loop (GMT, I, row_in) gmt_geo_to_xy (GMT, I->header->wesn[XLO], y_in[row_in], &x_proj, &y_in_proj[row_in]);
8395 gmt_M_col_loop2 (GMT, I, col_in) gmt_geo_to_xy (GMT, x_in[col_in], I->header->wesn[YLO], &x_in_proj[col_in], &y_proj);
8396 gmt_M_row_loop (GMT, O, row_out) gmt_xy_to_geo (GMT, &x_proj, &y_out_proj[row_out], I->header->wesn[YLO], y_out[row_out]);
8397 gmt_M_col_loop2 (GMT, O, col_out) { /* Here we must also align longitudes properly */
8398 gmt_xy_to_geo (GMT, &x_out_proj[col_out], &y_proj, x_out[col_out], I->header->wesn[YLO]);
8399 if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_out_proj[col_out])) {
8400 while (x_out_proj[col_out] < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_out_proj[col_out] += 360.0;
8401 while (x_out_proj[col_out] > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_out_proj[col_out] -= 360.0;
8402 }
8403 }
8404 }
8405 }
8406
8407 #if 0
8408 gmt_M_grd_loop (GMT, O, row_out, col_out, ij_out) /* So that nodes outside will have the NaN color */
8409 for (b = 0; b < nb; b++) O->data[nb*ij_out+b] = gmt_M_u255 (GMT->current.setting.color_patch[GMT_NAN][b]);
8410 #endif
8411 for (b = 0; b < 4; b++) z_int_bg[b] = gmt_M_u255 (GMT->current.setting.color_patch[GMT_NAN][b]);
8412
8413 /* PART 1: Project input image points and do a blockmean operation */
8414
8415 if (GMT->common.n.antialias) { /* Blockaverage repeat pixels, at least the first ~32767 of them... */
8416 int n_columns = O->header->n_columns, n_rows = O->header->n_rows;
8417 nz = gmt_M_memory (GMT, NULL, O->header->size, short int);
8418 /* Cannot do OPENMP yet here since it would require a reduction into an output array (nz) */
8419
8420 gmt_M_row_loop (GMT, I, row_in) { /* Loop over the input grid row coordinates */
8421 if (gmt_M_is_rect_graticule (GMT)) y_proj = y_in_proj[row_in];
8422 gmt_M_col_loop (GMT, I, row_in, col_in, ij_in) { /* Loop over the input grid col coordinates */
8423 if (gmt_M_is_rect_graticule (GMT))
8424 x_proj = x_in_proj[col_in];
8425 else if (inverse)
8426 gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_in[col_in], y_in[row_in]);
8427 else {
8428 if (GMT->current.map.outside (GMT, x_in[col_in], y_in[row_in])) continue; /* Quite possible we are beyond the horizon */
8429 gmt_geo_to_xy (GMT, x_in[col_in], y_in[row_in], &x_proj, &y_proj);
8430 }
8431
8432 /* Here, (x_proj, y_proj) is the projected grid point. Now find nearest node on the output grid */
8433
8434 row_out = (int)gmt_M_grd_y_to_row (GMT, y_proj, O->header);
8435 if (row_out < 0 || row_out >= n_rows) continue; /* Outside our grid region */
8436 col_out = (int)gmt_M_grd_x_to_col (GMT, x_proj, O->header);
8437 if (col_out < 0 || col_out >= n_columns) continue; /* Outside our grid region */
8438
8439 /* OK, this projected point falls inside the projected grid's rectangular domain */
8440
8441 ij_out = gmt_M_ijp (O->header, row_out, col_out); /* The output node */
8442 if (nz[ij_out] == 0) for (b = 0; b < nb; b++) O->data[nb*ij_out+b] = 0; /* First time, override the initial value */
8443 if (nz[ij_out] < SHRT_MAX) { /* Avoid overflow */
8444 for (b = 0; b < nb; b++) {
8445 rgb[b] = ((double)nz[ij_out] * O->data[nb*ij_out+b] + I->data[nb*ij_in+b])/(nz[ij_out] + 1.0); /* Update the mean pix values inside this rect... */
8446 O->data[nb*ij_out+b] = (unsigned char) lrint (gmt_M_0_255_truncate (rgb[b]));
8447 }
8448 nz[ij_out]++; /* ..and how many points there were */
8449 }
8450 }
8451 }
8452 #ifdef DEBUG
8453 if (GMT->common.n.save_debug) { /* Write nz as a debug grid to file */
8454 uint64_t k;
8455 struct GMT_GRID *G = NULL;
8456 if ((G = GMT_Create_Data (GMT->parent, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, O->header->wesn, O->header->inc, \
8457 O->header->registration, GMT_PAD_DEFAULT, NULL)) == NULL) goto bail_img_dbg;
8458 for (k = 0; k < G->header->size; k++) G->data[k] = (float)nz[k];
8459 (void) GMT_Write_Data (GMT->parent, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, NULL, "nz_img_counter.grd", G);
8460 GMT_Report (GMT->parent, GMT_MSG_NOTICE, "gmt_grd_project: Antialias counter nz written to grid file nz_img_counter.grd\n");
8461 GMT_Destroy_Data (GMT->parent, &G);
8462 }
8463 bail_img_dbg:
8464 ij_out = 0;
8465 #endif
8466 }
8467 /* PART 2: Create weighted average of interpolated and observed points */
8468
8469 //#ifdef _OPENMP
8470 //#pragma omp parallel for private(row_out,y_proj,col_out,ij_out,x_proj,z_int,inv_nz,b) shared(O,GMT,y_out_proj,x_out_proj,inverse,x_out,y_out,I,nz,z_int_bg,nb)
8471 //#endif
8472 for (row_out = 0; row_out < (int)O->header->n_rows; row_out++) { /* Loop over the output grid row coordinates */
8473 if (gmt_M_is_rect_graticule (GMT)) y_proj = y_out_proj[row_out];
8474 gmt_M_col_loop (GMT, O, row_out, col_out, ij_out) { /* Loop over the output grid col coordinates */
8475 if (gmt_M_is_rect_graticule (GMT))
8476 x_proj = x_out_proj[col_out];
8477 else if (inverse)
8478 gmt_geo_to_xy (GMT, x_out[col_out], y_out[row_out], &x_proj, &y_proj);
8479 else {
8480 gmt_xy_to_geo (GMT, &x_proj, &y_proj, x_out[col_out], y_out[row_out]);
8481 if (GMT->current.proj.projection_GMT == GMT_GENPER && GMT->current.proj.g_outside) continue; /* We are beyond the horizon */
8482
8483 /* On 17-Sep-2007 the slack of GMT_CONV4_LIMIT was added to allow for round-off
8484 errors in the grid limits. */
8485 if (gmt_M_x_is_lon (GMT, GMT_IN) && !gmt_M_is_dnan (x_proj)) {
8486 while (x_proj < I->header->wesn[XLO] - GMT_CONV4_LIMIT) x_proj += 360.0;
8487 while (x_proj > I->header->wesn[XHI] + GMT_CONV4_LIMIT) x_proj -= 360.0;
8488 }
8489 }
8490
8491 /* Here, (x_proj, y_proj) is the inversely projected grid point. Now find nearest node on the input grid */
8492
8493 if (gmtlib_bcr_get_img (GMT, I, x_proj, y_proj, z_int)) /* So that nodes outside will have the NaN color */
8494 for (b = 0; b < 4; b++) z_int[b] = z_int_bg[b];
8495
8496 if (!GMT->common.n.antialias || nz[ij_out] < 2) /* Just use the interpolated value */
8497 for (b = 0; b < nb; b++) O->data[nb*ij_out+b] = z_int[b];
8498 else { /* Weighted average between blockmean'ed and interpolated values */
8499 inv_nz = 1.0 / nz[ij_out];
8500 for (b = 0; b < nb; b++) {
8501 rgb[b] = ((double)nz[ij_out] * O->data[nb*ij_out+b] + z_int[b] * inv_nz) / (nz[ij_out] + inv_nz);
8502 O->data[nb*ij_out+b] = (unsigned char) lrint (gmt_M_0_255_truncate (rgb[b]));
8503 }
8504 }
8505 }
8506 }
8507
8508 /* Time to clean up our mess */
8509
8510 if (!in) {
8511 gmt_M_free (GMT, x_in);
8512 gmt_M_free (GMT, y_in);
8513 }
8514 if (!out) {
8515 gmt_M_free (GMT, x_out);
8516 gmt_M_free (GMT, y_out);
8517 }
8518 if (gmt_M_is_rect_graticule(GMT)) {
8519 gmt_M_free (GMT, x_in_proj);
8520 gmt_M_free (GMT, y_in_proj);
8521 gmt_M_free (GMT, x_out_proj);
8522 gmt_M_free (GMT, y_out_proj);
8523 }
8524 if (GMT->common.n.antialias) gmt_M_free (GMT, nz);
8525
8526 return (GMT_NOERROR);
8527 }
8528
8529 /*! . */
gmt_azim_to_angle(struct GMT_CTRL * GMT,double lon,double lat,double c,double azim)8530 double gmt_azim_to_angle (struct GMT_CTRL *GMT, double lon, double lat, double c, double azim) {
8531 /* All variables in degrees */
8532
8533 double lon1, lat1, x0, x1, y0, y1, dx, width, sinaz, cosaz, angle;
8534
8535 if (gmt_M_is_linear (GMT)) { /* Trivial case */
8536 angle = 90.0 - azim;
8537 if (GMT->current.proj.scale[GMT_X] != GMT->current.proj.scale[GMT_Y]) { /* But allow for different x,y scaling */
8538 sincosd (angle, &sinaz, &cosaz);
8539 angle = d_atan2d (sinaz * GMT->current.proj.scale[GMT_Y], cosaz * GMT->current.proj.scale[GMT_X]);
8540 }
8541 return (angle);
8542 }
8543 else if (GMT->current.proj.projection_GMT == GMT_POLAR) { /* r/theta */
8544 return (azim); /* Place holder - not correct yet but don't want to go into the below if r/theta */
8545 }
8546
8547 /* Find second point c spherical degrees away in the azim direction */
8548
8549 gmtlib_get_point_from_r_az (GMT, lon, lat, c, azim, &lon1, &lat1);
8550
8551 /* Convert both points to x,y and get angle */
8552
8553 gmt_geo_to_xy (GMT, lon, lat, &x0, &y0);
8554 gmt_geo_to_xy (GMT, lon1, lat1, &x1, &y1);
8555
8556 /* Check for wrap-around */
8557
8558 dx = x1 - x0;
8559 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]) && fabs (dx) > (width = gmt_half_map_width (GMT, y0))) {
8560 width *= 2.0;
8561 if (x1 < width)
8562 x0 -= width;
8563 else
8564 x0 += width;
8565 }
8566 angle = d_atan2d (y1 - y0, x1 - x0);
8567 return (angle);
8568 }
8569
8570 /*! . */
gmt_map_clip_path(struct GMT_CTRL * GMT,double ** x,double ** y,bool * donut)8571 uint64_t gmt_map_clip_path (struct GMT_CTRL *GMT, double **x, double **y, bool *donut) {
8572 /* This function returns a clip path corresponding to the
8573 * extent of the map.
8574 */
8575
8576 uint64_t i, j, np;
8577 bool do_circle = false;
8578 double *work_x = NULL, *work_y = NULL, da, r0, s, c, lon, lat;
8579
8580 *donut = false;
8581
8582 if (GMT->common.R.oblique) /* Rectangular map boundary */
8583 np = 4;
8584 else {
8585 switch (GMT->current.proj.projection_GMT) {
8586 case GMT_LINEAR:
8587 case GMT_MERCATOR:
8588 case GMT_CYL_EQ:
8589 case GMT_CYL_EQDIST:
8590 case GMT_CYL_STEREO:
8591 case GMT_MILLER:
8592 case GMT_OBLIQUE_MERC:
8593 np = 4;
8594 break;
8595 case GMT_POLAR:
8596 if (GMT->current.proj.flip)
8597 *donut = (GMT->common.R.wesn[YHI] < GMT->current.proj.flip_radius && GMT->current.map.is_world);
8598 else
8599 *donut = (GMT->common.R.wesn[YLO] > 0.0 && GMT->current.map.is_world);
8600 np = GMT->current.map.n_lon_nodes + 1;
8601 if ((GMT->current.proj.flip && GMT->common.R.wesn[YHI] < GMT->current.proj.flip_radius) || (!GMT->current.proj.flip && GMT->common.R.wesn[YLO] > 0.0)) /* Need inside circle segment */
8602 np *= 2;
8603 else if (!GMT->current.map.is_world) /* Need to include origin */
8604 np++;
8605 break;
8606 case GMT_GENPER:
8607 case GMT_STEREO:
8608 case GMT_LAMBERT:
8609 case GMT_LAMB_AZ_EQ:
8610 case GMT_ORTHO:
8611 case GMT_GNOMONIC:
8612 case GMT_AZ_EQDIST:
8613 case GMT_ALBERS:
8614 case GMT_ECONIC:
8615 case GMT_VANGRINTEN:
8616 np = (GMT->current.proj.polar && (GMT->common.R.wesn[YLO] <= -90.0 || GMT->common.R.wesn[YHI] >= 90.0)) ? GMT->current.map.n_lon_nodes + 2: 2 * (GMT->current.map.n_lon_nodes + 1);
8617 break;
8618 case GMT_MOLLWEIDE:
8619 case GMT_SINUSOIDAL:
8620 case GMT_ROBINSON:
8621 np = 2 * GMT->current.map.n_lat_nodes + 2;
8622 break;
8623 case GMT_WINKEL:
8624 case GMT_HAMMER:
8625 case GMT_ECKERT4:
8626 case GMT_ECKERT6:
8627 np = 2 * GMT->current.map.n_lat_nodes + 2;
8628 if (GMT->common.R.wesn[YLO] != -90.0) np += GMT->current.map.n_lon_nodes - 1;
8629 if (GMT->common.R.wesn[YHI] != 90.0) np += GMT->current.map.n_lon_nodes - 1;
8630 break;
8631 case GMT_TM:
8632 case GMT_UTM:
8633 case GMT_CASSINI:
8634 case GMT_POLYCONIC:
8635 np = 2 * (GMT->current.map.n_lon_nodes + GMT->current.map.n_lat_nodes);
8636 break;
8637 default:
8638 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Bad case in gmt_map_clip_path (%d)\n", GMT->current.proj.projection_GMT);
8639 np = 0;
8640 break;
8641 }
8642 }
8643
8644 work_x = gmt_M_memory (GMT, NULL, np+1, double); /* Add one for manual closure */
8645 work_y = gmt_M_memory (GMT, NULL, np+1, double);
8646
8647 if (GMT->common.R.oblique) {
8648 work_x[0] = work_x[3] = GMT->current.proj.rect[XLO]; work_y[0] = work_y[1] = GMT->current.proj.rect[YLO];
8649 work_x[1] = work_x[2] = GMT->current.proj.rect[XHI]; work_y[2] = work_y[3] = GMT->current.proj.rect[YHI];
8650 }
8651 else {
8652 if (gmt_M_is_azimuthal (GMT)) do_circle = GMT->current.map.is_world;
8653 switch (GMT->current.proj.projection_GMT) { /* Fill in clip path */
8654 case GMT_LINEAR:
8655 case GMT_MERCATOR:
8656 case GMT_CYL_EQ:
8657 case GMT_CYL_EQDIST:
8658 case GMT_CYL_STEREO:
8659 case GMT_MILLER:
8660 case GMT_OBLIQUE_MERC:
8661 work_x[0] = work_x[3] = GMT->current.proj.rect[XLO]; work_y[0] = work_y[1] = GMT->current.proj.rect[YLO];
8662 work_x[1] = work_x[2] = GMT->current.proj.rect[XHI]; work_y[2] = work_y[3] = GMT->current.proj.rect[YHI];
8663 break;
8664 case GMT_LAMBERT:
8665 case GMT_ALBERS:
8666 case GMT_ECONIC:
8667 for (i = j = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8668 lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon;
8669 gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8670 }
8671 for (i = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8672 lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon;
8673 gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8674 }
8675 break;
8676 case GMT_TM:
8677 case GMT_UTM:
8678 case GMT_CASSINI:
8679 case GMT_POLYCONIC:
8680 for (i = j = 0; i < GMT->current.map.n_lon_nodes; i++, j++) /* South */
8681 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8682 for (i = 0; i < GMT->current.map.n_lat_nodes; i++, j++) /* East */
8683 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat, &work_x[j], &work_y[j]);
8684 for (i = 0; i < GMT->current.map.n_lon_nodes; i++, j++) /* North */
8685 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8686 for (i = 0; i < GMT->current.map.n_lat_nodes; i++, j++) /* West */
8687 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI] - i * GMT->current.map.dlat, &work_x[j], &work_y[j]);
8688 break;
8689 case GMT_POLAR:
8690 r0 = GMT->current.proj.r * GMT->common.R.wesn[YLO] / GMT->common.R.wesn[YHI];
8691 if (*donut) {
8692 np /= 2;
8693 da = TWO_PI / np;
8694 for (i = 0, j = 2 * np - 1; i < np; i++, j--) { /* Draw outer clippath */
8695 sincos (i * da, &s, &c);
8696 work_x[i] = GMT->current.proj.r * (1.0 + c);
8697 work_y[i] = GMT->current.proj.r * (1.0 + s);
8698 /* Do inner clippath and put it at end of array */
8699 work_x[j] = GMT->current.proj.r + r0 * c;
8700 work_y[j] = GMT->current.proj.r + r0 * s;
8701 }
8702 }
8703 else {
8704 da = fabs (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / GMT->current.map.n_lon_nodes;
8705 if (GMT->current.proj.flip) {
8706 for (i = j = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) /* Draw outer clippath */
8707 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * da, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8708 if (GMT->common.R.wesn[YHI] < GMT->current.proj.flip_radius) { /* Must do the inner path as well */
8709 for (i = GMT->current.map.n_lon_nodes + 1; i > 0; i--, j++) /* Draw inner clippath */
8710 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + (i-1) * da, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8711 }
8712 if (doubleAlmostEqual (GMT->common.R.wesn[YHI], 90.0) && !GMT->current.map.is_world) /* Add origin */
8713 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8714 }
8715 else {
8716 for (i = j = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) /* Draw outer clippath */
8717 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * da, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8718 if (GMT->common.R.wesn[YLO] > 0.0) { /* Must do the inner path as well */
8719 for (i = GMT->current.map.n_lon_nodes + 1; i > 0; i--, j++) /* Draw inner clippath */
8720 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + (i-1) * da, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8721 }
8722 if (gmt_M_is_zero (GMT->common.R.wesn[YLO]) && !GMT->current.map.is_world) /* Add origin */
8723 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8724 }
8725 }
8726 break;
8727 case GMT_GENPER:
8728 gmtlib_genper_map_clip_path (GMT, np, work_x, work_y);
8729 break;
8730 case GMT_VANGRINTEN:
8731 do_circle = GMT->current.map.is_world;
8732 /* Intentionally fall through */
8733 case GMT_LAMB_AZ_EQ:
8734 case GMT_AZ_EQDIST:
8735 case GMT_ORTHO:
8736 case GMT_GNOMONIC:
8737 case GMT_STEREO:
8738 if (GMT->current.proj.polar && !do_circle) {
8739 j = 0;
8740 if (GMT->common.R.wesn[YLO] > -90.0) {
8741 for (i = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8742 lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XHI] : GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon;
8743 gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8744 }
8745 }
8746 else { /* Just add S pole */
8747 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], -90.0, &work_x[j], &work_y[j]);
8748 j++;
8749 }
8750 if (GMT->common.R.wesn[YHI] < 90.0) {
8751 for (i = 0; i <= GMT->current.map.n_lon_nodes; i++, j++) {
8752 lon = (i == GMT->current.map.n_lon_nodes) ? GMT->common.R.wesn[XLO] : GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon;
8753 gmt_geo_to_xy (GMT, lon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8754 }
8755 }
8756 else { /* Just add N pole */
8757 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], 90.0, &work_x[j], &work_y[j]);
8758 j++;
8759 }
8760 }
8761 else { /* Just need to create a circular closed path */
8762 da = TWO_PI / np;
8763 for (i = 0; i < np; i++) {
8764 sincos (i * da, &s, &c);
8765 work_x[i] = GMT->current.proj.r * (1.0 + c);
8766 work_y[i] = GMT->current.proj.r * (1.0 + s);
8767 }
8768 }
8769 break;
8770 case GMT_MOLLWEIDE:
8771 case GMT_SINUSOIDAL:
8772 case GMT_ROBINSON:
8773 for (i = j = 0; i <= GMT->current.map.n_lat_nodes; i++, j++) { /* Right */
8774 lat = (i == GMT->current.map.n_lat_nodes) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat;
8775 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], lat, &work_x[j], &work_y[j]);
8776 }
8777 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]); j++;
8778 for (i = GMT->current.map.n_lat_nodes; i > 0; j++, i--) { /* Left */
8779 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + (i-1) * GMT->current.map.dlat, &work_x[j], &work_y[j]);
8780 }
8781 break;
8782 case GMT_HAMMER:
8783 case GMT_WINKEL:
8784 case GMT_ECKERT4:
8785 case GMT_ECKERT6:
8786 for (i = j = 0; i <= GMT->current.map.n_lat_nodes; i++, j++) { /* Right */
8787 lat = (i == GMT->current.map.n_lat_nodes) ? GMT->common.R.wesn[YHI] : GMT->common.R.wesn[YLO] + i * GMT->current.map.dlat;
8788 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI], lat, &work_x[j], &work_y[j]);
8789 }
8790 for (i = 1; GMT->common.R.wesn[YHI] != 90.0 && i < GMT->current.map.n_lon_nodes; i++, j++)
8791 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XHI] - i * GMT->current.map.dlon, GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]);
8792 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YHI], &work_x[j], &work_y[j]); j++;
8793 for (i = GMT->current.map.n_lat_nodes; i > 0; j++, i--) { /* Left */
8794 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO], GMT->common.R.wesn[YLO] + (i-1)* GMT->current.map.dlat, &work_x[j], &work_y[j]);
8795 }
8796 for (i = 1; GMT->common.R.wesn[YLO] != -90.0 && i < GMT->current.map.n_lon_nodes; i++, j++)
8797 gmt_geo_to_xy (GMT, GMT->common.R.wesn[XLO] + i * GMT->current.map.dlon, GMT->common.R.wesn[YLO], &work_x[j], &work_y[j]);
8798 break;
8799 }
8800 }
8801
8802 /* CLose the clipping polygon */
8803 work_x[np] = work_x[0];
8804 work_y[np] = work_y[0];
8805 np++;
8806 if (!(*donut)) np = gmt_compact_line (GMT, work_x, work_y, np, false, NULL);
8807
8808 *x = work_x;
8809 *y = work_y;
8810
8811 return (np);
8812 }
8813
8814 /*! . */
gmtmap_lat_swap_quick(struct GMT_CTRL * GMT,double lat,double c[])8815 double gmtmap_lat_swap_quick (struct GMT_CTRL *GMT, double lat, double c[]) {
8816 /* Return latitude, in degrees, given latitude, in degrees, based on coefficients c */
8817
8818 double delta, cos2phi, sin2phi;
8819 gmt_M_unused(GMT);
8820
8821 /* First deal with trivial cases */
8822
8823 if (lat >= 90.0) return ( 90.0);
8824 if (lat <= -90.0) return (-90.0);
8825 if (gmt_M_is_zero (lat)) return (0.0);
8826
8827 sincosd (2.0 * lat, &sin2phi, &cos2phi);
8828
8829 delta = sin2phi * (c[0] + cos2phi * (c[1] + cos2phi * (c[2] + cos2phi * c[3])));
8830
8831 return (lat + R2D * delta);
8832 }
8833
8834 /*! . */
gmt_lat_swap(struct GMT_CTRL * GMT,double lat,int itype)8835 double gmt_lat_swap (struct GMT_CTRL *GMT, double lat, int itype) {
8836 /* Return latitude, in degrees, given latitude, in degrees, based on itype */
8837
8838 double delta, cos2phi, sin2phi;
8839
8840 /* First deal with trivial cases */
8841
8842 if (lat >= 90.0) return ( 90.0);
8843 if (lat <= -90.0) return (-90.0);
8844 if (gmt_M_is_zero (lat)) return (0.0);
8845
8846 if (GMT->current.proj.lat_swap_vals.spherical) return (lat);
8847
8848 if (itype >= GMT_LATSWAP_N) {
8849 /* This should never happen -?- or do we want to allow the
8850 possibility of using itype = -1 to do nothing */
8851 GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_lat_swap(): Invalid choice, programming bug.\n");
8852 return(lat);
8853 }
8854
8855 sincosd (2.0 * lat, &sin2phi, &cos2phi);
8856
8857 delta = sin2phi * (GMT->current.proj.lat_swap_vals.c[itype][0]
8858 + cos2phi * (GMT->current.proj.lat_swap_vals.c[itype][1]
8859 + cos2phi * (GMT->current.proj.lat_swap_vals.c[itype][2]
8860 + cos2phi * GMT->current.proj.lat_swap_vals.c[itype][3])));
8861
8862 return (lat + R2D * delta);
8863 }
8864
8865 /*! . */
gmtlib_scale_eqrad(struct GMT_CTRL * GMT)8866 void gmtlib_scale_eqrad (struct GMT_CTRL *GMT) {
8867 /* Reinitialize GMT->current.proj.EQ_RAD to the appropriate value */
8868
8869 switch (GMT->current.proj.projection_GMT) {
8870
8871 /* Conformal projections */
8872
8873 case GMT_MERCATOR:
8874 case GMT_TM:
8875 case GMT_UTM:
8876 case GMT_OBLIQUE_MERC:
8877 case GMT_LAMBERT:
8878 case GMT_STEREO:
8879
8880 GMT->current.proj.EQ_RAD = GMT->current.proj.lat_swap_vals.rm;
8881 break;
8882
8883 /* Equal Area projections */
8884
8885 case GMT_LAMB_AZ_EQ:
8886 case GMT_ALBERS:
8887 case GMT_ECKERT4:
8888 case GMT_ECKERT6:
8889 case GMT_HAMMER:
8890 case GMT_MOLLWEIDE:
8891 case GMT_SINUSOIDAL:
8892
8893 GMT->current.proj.EQ_RAD = GMT->current.proj.lat_swap_vals.ra;
8894 break;
8895
8896 default: /* Keep EQ_RAD as is */
8897 break;
8898 }
8899
8900 /* Also reset dependencies of EQ_RAD */
8901
8902 GMT->current.proj.i_EQ_RAD = 1.0 / GMT->current.proj.EQ_RAD;
8903 GMT->current.proj.M_PR_DEG = TWO_PI * GMT->current.proj.EQ_RAD / 360.0;
8904 GMT->current.proj.KM_PR_DEG = GMT->current.proj.M_PR_DEG / METERS_IN_A_KM;
8905 }
8906
8907
8908 /*! . */
gmtlib_init_ellipsoid(struct GMT_CTRL * GMT)8909 void gmtlib_init_ellipsoid (struct GMT_CTRL *GMT) {
8910 double f;
8911
8912 /* Set up ellipsoid parameters for the selected ellipsoid since gmt.conf could have changed them */
8913
8914 f = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].flattening;
8915 GMT->current.proj.ECC2 = 2.0 * f - f * f;
8916 GMT->current.proj.ECC4 = GMT->current.proj.ECC2 * GMT->current.proj.ECC2;
8917 GMT->current.proj.ECC6 = GMT->current.proj.ECC2 * GMT->current.proj.ECC4;
8918 GMT->current.proj.one_m_ECC2 = 1.0 - GMT->current.proj.ECC2;
8919 GMT->current.proj.i_one_m_ECC2 = 1.0 / GMT->current.proj.one_m_ECC2;
8920 GMT->current.proj.ECC = d_sqrt (GMT->current.proj.ECC2);
8921 GMT->current.proj.half_ECC = 0.5 * GMT->current.proj.ECC;
8922 if (GMT->current.proj.ECC != 0) { /* avoid division by 0 */
8923 GMT->current.proj.i_half_ECC = 0.5 / GMT->current.proj.ECC; /* Only used in inverse Alberts when e > 0 anyway */
8924 }
8925 GMT->current.proj.EQ_RAD = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius;
8926 GMT->current.proj.i_EQ_RAD = 1.0 / GMT->current.proj.EQ_RAD;
8927
8928 /* Spherical degrees to m or km */
8929 GMT->current.proj.mean_radius = gmtmap_mean_radius (GMT, GMT->current.proj.EQ_RAD, f);
8930 GMT->current.proj.M_PR_DEG = TWO_PI * GMT->current.proj.mean_radius / 360.0;
8931 GMT->current.proj.KM_PR_DEG = GMT->current.proj.M_PR_DEG / METERS_IN_A_KM;
8932 GMT->current.proj.DIST_M_PR_DEG = GMT->current.proj.M_PR_DEG;
8933 GMT->current.proj.DIST_KM_PR_DEG = GMT->current.proj.KM_PR_DEG;
8934
8935 /* Compute coefficients needed for auxiliary latitude conversions */
8936 gmtmap_lat_swap_init (GMT);
8937 }
8938
8939 /*! . */
gmtlib_init_geodesic(struct GMT_CTRL * GMT)8940 void gmtlib_init_geodesic (struct GMT_CTRL *GMT) {
8941 switch (GMT->current.setting.proj_geodesic) {
8942 case GMT_GEODESIC_VINCENTY:
8943 GMT->current.map.geodesic_meter = gmtmap_vincenty_dist_meter;
8944 GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_vincenty;
8945 break;
8946 case GMT_GEODESIC_ANDOYER:
8947 GMT->current.map.geodesic_meter = gmtmap_andoyer_dist_meter;
8948 GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_vincenty; /* This may change later */
8949 break;
8950 case GMT_GEODESIC_RUDOE:
8951 GMT->current.map.geodesic_meter = gmtmap_rudoe_dist_meter;
8952 GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_rudoe;
8953 break;
8954 default:
8955 GMT_Report (GMT->parent, GMT_MSG_WARNING, "The PROJ_GEODESIC is not set! - use Vincenty\n");
8956 GMT->current.setting.proj_geodesic = GMT_GEODESIC_VINCENTY;
8957 GMT->current.map.geodesic_meter = gmtmap_vincenty_dist_meter;
8958 GMT->current.map.geodesic_az_backaz = gmtmap_az_backaz_vincenty;
8959 break;
8960 }
8961 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "The PROJ_GEODESIC set to %s\n", GEOD_TEXT[GMT->current.setting.proj_geodesic]);
8962 }
8963
8964 /* Datum conversion routines */
8965
8966 /*! . */
gmt_datum_init(struct GMT_CTRL * GMT,struct GMT_DATUM * from,struct GMT_DATUM * to,bool heights)8967 void gmt_datum_init (struct GMT_CTRL *GMT, struct GMT_DATUM *from, struct GMT_DATUM *to, bool heights) {
8968 /* Initialize datum conv structures based on the parsed values*/
8969 unsigned int k;
8970
8971 GMT->current.proj.datum.h_given = heights;
8972 gmt_M_memcpy (&GMT->current.proj.datum.from, from, 1, struct GMT_DATUM);
8973 gmt_M_memcpy (&GMT->current.proj.datum.to, to, 1, struct GMT_DATUM);
8974
8975 GMT->current.proj.datum.da = GMT->current.proj.datum.to.a - GMT->current.proj.datum.from.a;
8976 GMT->current.proj.datum.df = GMT->current.proj.datum.to.f - GMT->current.proj.datum.from.f;
8977 for (k = 0; k < 3; k++)
8978 GMT->current.proj.datum.dxyz[k] = -(GMT->current.proj.datum.to.xyz[k] - GMT->current.proj.datum.from.xyz[k]); /* Since the X, Y, Z are Deltas relative to WGS-84 */
8979 GMT->current.proj.datum.one_minus_f = 1.0 - GMT->current.proj.datum.from.f;
8980 }
8981
8982 /*! . */
gmt_ECEF_init(struct GMT_CTRL * GMT,struct GMT_DATUM * D)8983 void gmt_ECEF_init (struct GMT_CTRL *GMT, struct GMT_DATUM *D) {
8984 /* Duplicate the parsed datum to the GMT from datum */
8985 gmt_M_memcpy (&GMT->current.proj.datum.from, D, 1, struct GMT_DATUM);
8986 }
8987
8988 /*! . */
gmt_set_datum(struct GMT_CTRL * GMT,char * text,struct GMT_DATUM * D)8989 int gmt_set_datum (struct GMT_CTRL *GMT, char *text, struct GMT_DATUM *D) {
8990 int i;
8991 double t;
8992
8993 if (text[0] == '\0' || text[0] == '-') { /* Shortcut for WGS-84 */
8994 if ((i = gmt_get_ellipsoid (GMT, "WGS-84")) >= 0) { /* For Coverity's benefit */
8995 gmt_M_memset (D->xyz, 3, double);
8996 D->a = GMT->current.setting.ref_ellipsoid[i].eq_radius;
8997 D->f = GMT->current.setting.ref_ellipsoid[i].flattening;
8998 D->ellipsoid_id = i;
8999 }
9000 }
9001 else if (strchr (text, ':')) { /* Has colons, must get ellipsoid and dr separately */
9002 char ellipsoid[GMT_LEN256] = {""}, dr[GMT_LEN256] = {""};
9003 if (sscanf (text, "%[^:]:%s", ellipsoid, dr) != 2) {
9004 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed <ellipsoid>:<dr> argument!\n");
9005 return (GMT_NOTSET);
9006 }
9007 if (sscanf (dr, "%lf,%lf,%lf", &D->xyz[GMT_X], &D->xyz[GMT_Y], &D->xyz[GMT_Z]) != 3) {
9008 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed <x>,<y>,<z> argument!\n");
9009 return (GMT_NOTSET);
9010 }
9011 if ((i = gmt_get_ellipsoid (GMT, ellipsoid)) >= 0) { /* This includes looking for format <a>,<1/f> */
9012 D->a = GMT->current.setting.ref_ellipsoid[i].eq_radius;
9013 D->f = GMT->current.setting.ref_ellipsoid[i].flattening;
9014 D->ellipsoid_id = i;
9015 }
9016 else {
9017 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Ellipsoid %s not recognized!\n", ellipsoid);
9018 return (GMT_NOTSET);
9019 }
9020 }
9021 else { /* Gave a Datum ID tag [ 0-(GMT_N_DATUMS-1)] */
9022 int k;
9023 if (sscanf (text, "%d", &i) != 1) {
9024 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Malformed or unrecognized <datum> argument (%s)!\n", text);
9025 return (GMT_NOTSET);
9026 }
9027 if (i < 0 || i >= GMT_N_DATUMS) {
9028 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Datum ID (%d) outside valid range (0-%d)!\n", i, GMT_N_DATUMS-1);
9029 return (GMT_NOTSET);
9030 }
9031 if ((k = gmt_get_ellipsoid (GMT, GMT->current.setting.proj_datum[i].ellipsoid)) < 0) { /* This should not happen... */
9032 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Ellipsoid %s not recognized!\n", GMT->current.setting.proj_datum[i].ellipsoid);
9033 return (GMT_NOTSET);
9034 }
9035 D->a = GMT->current.setting.ref_ellipsoid[k].eq_radius;
9036 D->f = GMT->current.setting.ref_ellipsoid[k].flattening;
9037 D->ellipsoid_id = k;
9038 for (k = 0; k < 3; k++) D->xyz[k] = GMT->current.setting.proj_datum[i].xyz[k];
9039 }
9040 D->b = D->a * (1 - D->f);
9041 D->e_squared = 2 * D->f - D->f * D->f;
9042 t = D->a /D->b;
9043 D->ep_squared = t * t - 1.0; /* (a^2 - b^2)/b^2 */
9044 return 0;
9045 }
9046
9047 /*! Compute the Abridged Molodensky transformation (3 parametrs). */
gmt_conv_datum(struct GMT_CTRL * GMT,double in[],double out[])9048 void gmt_conv_datum (struct GMT_CTRL *GMT, double in[], double out[]) {
9049 /* Evaluate J^-1 and B on from ellipsoid */
9050 /* Based on Standard Molodensky Datum Conversion, implemented from
9051 * http://www.colorado.edu/geography/gcraft/notes/datum/gif/molodens.gif */
9052
9053 double sin_lon, cos_lon, sin_lat, cos_lat, sin_lat2, M, N, h, tmp_1, tmp_2, tmp_3;
9054 double delta_lat, delta_lon, delta_h, sc_lat;
9055
9056 h = (GMT->current.proj.datum.h_given) ? in[GMT_Z] : 0.0;
9057 sincosd (in[GMT_X], &sin_lon, &cos_lon);
9058 sincosd (in[GMT_Y], &sin_lat, &cos_lat);
9059 sin_lat2 = sin_lat * sin_lat;
9060 sc_lat = sin_lat * cos_lat;
9061 M = GMT->current.proj.datum.from.a * (1.0 - GMT->current.proj.datum.from.e_squared) / pow (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat2, 1.5);
9062 N = GMT->current.proj.datum.from.a / sqrt (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat2);
9063
9064 tmp_1 = -GMT->current.proj.datum.dxyz[GMT_X] * sin_lat * cos_lon - GMT->current.proj.datum.dxyz[GMT_Y] * sin_lat * sin_lon + GMT->current.proj.datum.dxyz[GMT_Z] * cos_lat;
9065 tmp_2 = GMT->current.proj.datum.da * (N * GMT->current.proj.datum.from.e_squared * sc_lat) / GMT->current.proj.datum.from.a;
9066 tmp_3 = GMT->current.proj.datum.df * (M / GMT->current.proj.datum.one_minus_f + N * GMT->current.proj.datum.one_minus_f) * sc_lat;
9067 delta_lat = (tmp_1 + tmp_2 + tmp_3) / (M + h);
9068
9069 delta_lon = (-GMT->current.proj.datum.dxyz[GMT_X] * sin_lon + GMT->current.proj.datum.dxyz[GMT_Y] * cos_lon) / ((N + h) * cos_lat);
9070
9071 tmp_1 = GMT->current.proj.datum.dxyz[GMT_X] * cos_lat * cos_lon + GMT->current.proj.datum.dxyz[GMT_Y] * cos_lat * sin_lon + GMT->current.proj.datum.dxyz[GMT_Z] * sin_lat;
9072 tmp_2 = -GMT->current.proj.datum.da * GMT->current.proj.datum.from.a / N;
9073 tmp_3 = GMT->current.proj.datum.df * GMT->current.proj.datum.one_minus_f * N * sin_lat2;
9074 delta_h = tmp_1 + tmp_2 + tmp_3;
9075
9076 out[GMT_X] = in[GMT_X] + delta_lon * R2D;
9077 out[GMT_Y] = in[GMT_Y] + delta_lat * R2D;
9078 if (GMT->current.proj.datum.h_given) out[GMT_Z] = in[GMT_Z] + delta_h;
9079 }
9080
9081 /*! . */
gmt_ECEF_forward(struct GMT_CTRL * GMT,double in[],double out[])9082 void gmt_ECEF_forward (struct GMT_CTRL *GMT, double in[], double out[]) {
9083 /* Convert geodetic lon, lat, height to ECEF coordinates given the datum parameters.
9084 * GMT->current.proj.datum.from is always the ellipsoid to use */
9085
9086 double sin_lon, cos_lon, sin_lat, cos_lat, N, tmp;
9087
9088 sincosd (in[GMT_X], &sin_lon, &cos_lon);
9089 sincosd (in[GMT_Y], &sin_lat, &cos_lat);
9090
9091 N = GMT->current.proj.datum.from.a / d_sqrt (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat * sin_lat);
9092 tmp = (N + in[GMT_Z]) * cos_lat;
9093 out[GMT_X] = tmp * cos_lon + GMT->current.proj.datum.from.xyz[GMT_X];
9094 out[GMT_Y] = tmp * sin_lon + GMT->current.proj.datum.from.xyz[GMT_Y];
9095 out[GMT_Z] = (N * (1 - GMT->current.proj.datum.from.e_squared) + in[GMT_Z]) * sin_lat + GMT->current.proj.datum.from.xyz[GMT_Z];
9096 }
9097
9098 /*! . */
gmt_ECEF_inverse(struct GMT_CTRL * GMT,double in[],double out[])9099 void gmt_ECEF_inverse (struct GMT_CTRL *GMT, double in[], double out[]) {
9100 /* Convert ECEF coordinates to geodetic lon, lat, height given the datum parameters.
9101 * GMT->current.proj.datum.from is always the ellipsoid to use */
9102
9103 unsigned int i;
9104 double in_p[3], sin_lat, cos_lat, N, p, theta, sin_theta, cos_theta;
9105
9106 /* First remove the xyz shifts, us in_p to avoid changing in */
9107 for (i = 0; i < 3; i++) in_p[i] = in[i] - GMT->current.proj.datum.from.xyz[i];
9108
9109 p = hypot (in_p[GMT_X], in_p[GMT_Y]);
9110 theta = atan (in_p[GMT_Z] * GMT->current.proj.datum.from.a / (p * GMT->current.proj.datum.from.b));
9111 sincos (theta, &sin_theta, &cos_theta);
9112 out[GMT_X] = d_atan2d (in_p[GMT_Y], in_p[GMT_X]);
9113 out[GMT_Y] = atand ((in_p[GMT_Z] + GMT->current.proj.datum.from.ep_squared * GMT->current.proj.datum.from.b * pow (sin_theta, 3.0)) / (p - GMT->current.proj.datum.from.e_squared * GMT->current.proj.datum.from.a * pow (cos_theta, 3.0)));
9114 sincosd (out[GMT_Y], &sin_lat, &cos_lat);
9115 N = GMT->current.proj.datum.from.a / sqrt (1.0 - GMT->current.proj.datum.from.e_squared * sin_lat * sin_lat);
9116 out[GMT_Z] = (p / cos_lat) - N;
9117 }
9118
9119 #if 0
9120 /*! Convert ECEF coordinates to geodetic lon, lat, height given the 'to' parameters. Used in 7 params Bursa-Wolf transform */
9121 void gmt_ECEF_inverse_dest_datum (struct GMT_CTRL *GMT, double in[], double out[]) {
9122 double sin_lat, cos_lat, N, p, theta, sin_theta, cos_theta;
9123
9124 p = hypot (in[GMT_X], in[GMT_Y]);
9125 theta = atan (in[GMT_Z] * GMT->current.proj.datum.to.a / (p * GMT->current.proj.datum.to.b));
9126 sincos (theta, &sin_theta, &cos_theta);
9127 out[GMT_X] = d_atan2d (in[GMT_Y], in[GMT_X]);
9128 out[GMT_Y] = atand ((in[GMT_Z] + GMT->current.proj.datum.to.ep_squared * GMT->current.proj.datum.to.b * pow (sin_theta, 3.0)) / (p - GMT->current.proj.datum.to.e_squared * GMT->current.proj.datum.to.a * pow (cos_theta, 3.0)));
9129 sincosd (out[GMT_Y], &sin_lat, &cos_lat);
9130 N = GMT->current.proj.datum.to.a / sqrt (1.0 - GMT->current.proj.datum.to.e_squared * sin_lat * sin_lat);
9131 out[GMT_Z] = (p / cos_lat) - N;
9132 }
9133 #endif
9134
9135 /*! . */
gmt_line_length(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,bool project)9136 double gmt_line_length (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, bool project) {
9137 /* Returns distance of line in units set by GMT_distaz. It bypassed points where x and/or y are NaN. */
9138 uint64_t this_p, prev;
9139 bool xy_not_NaN;
9140 double cum_dist = 0.0, xp0 = 0, xp1, yp0 = 0, yp1;
9141
9142 if (n == 0) return 0.0;
9143 if (project) gmt_geo_to_xy (GMT, x[0], y[0], &xp0, &yp0);
9144 for (this_p = 1, prev = 0; this_p < n; this_p++) {
9145 xy_not_NaN = !(gmt_M_is_dnan (x[this_p]) || gmt_M_is_dnan (y[this_p]));
9146 if (xy_not_NaN) { /* safe to calculate inc */
9147 if (project) {
9148 gmt_geo_to_xy (GMT, x[this_p], y[this_p], &xp1, &yp1);
9149 cum_dist += hypot (xp0 - xp1, yp0 - yp1);
9150 xp0 = xp1; yp0 = yp1;
9151 }
9152 else
9153 cum_dist += gmt_distance (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9154 prev = this_p; /* This was a record with OK x,y; make it the previous point for distance calculations */
9155 }
9156 }
9157 if (project) cum_dist *= GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit];
9158
9159 return (cum_dist);
9160 }
9161
9162 /*! . */
gmt_dist_array(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,bool cumulative)9163 double *gmt_dist_array (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, bool cumulative) {
9164 /* Returns distances in units set by GMT_distaz. It bypassed points where x and/or y are NaN.
9165 * If cumulative is false we just return the increments; otherwise we add up distances */
9166 uint64_t this_p, prev;
9167 bool xy_not_NaN;
9168 double *d = NULL, cum_dist = 0.0, inc = 0.0;
9169
9170 if (n == 0) return (NULL);
9171 d = gmt_M_memory (GMT, NULL, n, double);
9172 if (gmt_M_is_dnan (x[0]) || gmt_M_is_dnan (y[0])) d[0] = GMT->session.d_NaN;
9173 for (this_p = 1, prev = 0; this_p < n; this_p++) {
9174 xy_not_NaN = !(gmt_M_is_dnan (x[this_p]) || gmt_M_is_dnan (y[this_p]));
9175 if (xy_not_NaN) { /* safe to calculate inc */
9176 inc = gmt_distance (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9177 if (cumulative) {
9178 cum_dist += inc;
9179 d[this_p] = cum_dist;
9180 }
9181 else
9182 d[this_p] = inc;
9183 }
9184 else
9185 d[this_p] = GMT->session.d_NaN;
9186
9187 if (xy_not_NaN) prev = this_p; /* This was a record with OK x,y; make it the previous point for distance calculations */
9188 }
9189 return (d);
9190 }
9191
9192 /*! . */
gmt_dist_array_2(struct GMT_CTRL * GMT,double x[],double y[],uint64_t n,double scale,int dist_flag)9193 double * gmt_dist_array_2 (struct GMT_CTRL *GMT, double x[], double y[], uint64_t n, double scale, int dist_flag) {
9194 /* Returns distances in meter; use scale to get other units */
9195 uint64_t this_p, prev;
9196 bool cumulative = true, do_scale, xy_not_NaN;
9197 double *d = NULL, cum_dist = 0.0, inc = 0.0;
9198
9199 if (dist_flag < 0) { /* Want increments and not cumulative distances */
9200 dist_flag = abs (dist_flag);
9201 cumulative = false;
9202 }
9203
9204 if (dist_flag < 0 || dist_flag > 3) return (NULL);
9205
9206 do_scale = (scale != 1.0);
9207 d = gmt_M_memory (GMT, NULL, n, double);
9208 if (gmt_M_is_dnan (x[0]) || gmt_M_is_dnan (y[0])) d[0] = GMT->session.d_NaN;
9209 for (this_p = 1, prev = 0; this_p < n; this_p++) {
9210 xy_not_NaN = !(gmt_M_is_dnan (x[this_p]) || gmt_M_is_dnan (y[this_p]));
9211 if (xy_not_NaN) { /* safe to calculate inc */
9212 switch (dist_flag) {
9213
9214 case 0: /* Cartesian distances */
9215
9216 inc = hypot (x[this_p] - x[prev], y[this_p] - y[prev]);
9217 break;
9218
9219 case 1: /* Flat earth distances in meter */
9220
9221 inc = gmtmap_flatearth_dist_meter (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9222 break;
9223
9224 case 2: /* Great circle distances in meter */
9225
9226 inc = gmt_great_circle_dist_meter (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9227 break;
9228
9229 case 3: /* Geodesic distances in meter */
9230
9231 inc = (*GMT->current.map.geodesic_meter) (GMT, x[this_p], y[this_p], x[prev], y[prev]);
9232 break;
9233 }
9234
9235 if (do_scale) inc *= scale;
9236 if (cumulative) cum_dist += inc;
9237 d[this_p] = (cumulative) ? cum_dist : inc;
9238 }
9239 else
9240 d[this_p] = GMT->session.d_NaN;
9241
9242 if (xy_not_NaN) prev = this_p; /* This was a record with OK x,y; make it the previous point for distance calculations */
9243 }
9244 return (d);
9245 }
9246
9247 /*! . */
gmtlib_map_latcross(struct GMT_CTRL * GMT,double lat,double west,double east,struct GMT_XINGS ** xings)9248 unsigned int gmtlib_map_latcross (struct GMT_CTRL *GMT, double lat, double west, double east, struct GMT_XINGS **xings) {
9249 bool go = false;
9250 unsigned int i, nx, nc = 0;
9251 size_t n_alloc = GMT_SMALL_CHUNK;
9252 double lon, lon_old, this_x, this_y, last_x, last_y, xlon[2], xlat[2], gap;
9253 struct GMT_XINGS *X = NULL;
9254
9255 if (gmt_M_is_conical (GMT) && gmt_M_360_range (west, east)) { /* Special case since 360 longitudes do not form a circle but a pacman shape */
9256 X = gmt_M_memory (GMT, NULL, 1U, struct GMT_XINGS);
9257 X[0].nx = 2; /* Will cut both east and west repeated boundaries */
9258 /* Do west boundary */
9259 gmt_geo_to_xy (GMT, west, lat, &X[0].xx[0], &X[0].yy[0]);
9260 X[0].angle[0] = gmtmap_get_angle (GMT, west, GMT->common.R.wesn[YLO], west, GMT->common.R.wesn[YHI]) + 90.0; /* Get angle of west boundary and add 90 */
9261 X[0].sides[0] = W_SIDE;
9262 /* Do east boundary */
9263 gmt_geo_to_xy (GMT, east, lat, &X[0].xx[1], &X[0].yy[1]);
9264 X[0].angle[1] = gmtmap_get_angle (GMT, east, GMT->common.R.wesn[YLO], east, GMT->common.R.wesn[YHI]) - 90.0; /* Get angle of east boundary and subtract 90 */
9265 X[0].sides[1] = E_SIDE;
9266 *xings = X;
9267 return 1; /* Done here, returning array with single GMT_XINGS structure holding two crossings */
9268 }
9269
9270 /* Remaining (general) cases */
9271
9272 X = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_XINGS);
9273
9274 lon_old = west - 2.0 * GMT_CONV4_LIMIT;
9275 gmt_map_outside (GMT, lon_old, lat);
9276 gmt_geo_to_xy (GMT, lon_old, lat, &last_x, &last_y);
9277 for (i = 1; i <= GMT->current.map.n_lon_nodes; i++) {
9278 lon = (i == GMT->current.map.n_lon_nodes) ? east + 2.0 * GMT_CONV4_LIMIT : west + i * GMT->current.map.dlon;
9279 gmt_map_outside (GMT, lon, lat);
9280 gmt_geo_to_xy (GMT, lon, lat, &this_x, &this_y);
9281 if ((nx = gmtmap_crossing (GMT, lon_old, lat, lon, lat, xlon, xlat, X[nc].xx, X[nc].yy, X[nc].sides))) {
9282 X[nc].angle[0] = gmtmap_get_angle (GMT, lon_old, lat, lon, lat); /* Get angle at first crossing */
9283 if (nx == 2) X[nc].angle[1] = X[nc].angle[0] + 180.0; /* If a 2nd crossing it must be really close so just add 180 */
9284 if (GMT->current.map.corner > 0) {
9285 X[nc].sides[0] = (GMT->current.map.corner%4 > 1) ? 1 : 3;
9286 if (GMT->current.proj.got_azimuths) X[nc].sides[0] = (X[nc].sides[0] + 2) % 4;
9287 GMT->current.map.corner = 0;
9288 }
9289 gmt_M_memcpy (X[nc].lon, xlon, 2U, double);
9290 gmt_M_memcpy (X[nc].lat, xlat, 2U, double);
9291 }
9292 else if (GMT->current.map.is_world) /* Deal with possibility of wrapping around 360 */
9293 nx = (*GMT->current.map.wrap_around_check) (GMT, X[nc].angle, last_x, last_y, this_x, this_y, X[nc].xx, X[nc].yy, X[nc].sides);
9294 if (nx == 2 && fabs (fabs (X[nc].xx[1] - X[nc].xx[0]) - GMT->current.map.width) < GMT_CONV4_LIMIT && !GMT->current.map.is_world)
9295 /* I assume this means if we have a crossing and both left and right and not worldmap then skip as something went wrong? */
9296 go = false;
9297 else if (nx == 2 && (gap = fabs (X[nc].yy[1] - X[nc].yy[0])) > GMT_CONV4_LIMIT && fabs (gap - GMT->current.map.height) < GMT_CONV4_LIMIT && !GMT->current.map.is_world_tm)
9298 /* I assume this means if we have a crossing and both top and bottom and it is not a global UTM map then skip as something went wrong? */
9299 go = false;
9300 else if (nx > 0)
9301 go = true;
9302 if (go) {
9303 X[nc].nx = nx;
9304 nc++;
9305 if (nc == n_alloc) {
9306 n_alloc <<= 1;
9307 X = gmt_M_memory (GMT, X, n_alloc, struct GMT_XINGS);
9308 }
9309 go = false;
9310 }
9311 lon_old = lon;
9312 last_x = this_x; last_y = this_y;
9313 }
9314
9315 if (nc > 0) {
9316 X = gmt_M_memory (GMT, X, nc, struct GMT_XINGS);
9317 *xings = X;
9318 }
9319 else
9320 gmt_M_free (GMT, X);
9321
9322 return (nc);
9323 }
9324
9325 /*! . */
gmtlib_map_loncross(struct GMT_CTRL * GMT,double lon,double south,double north,struct GMT_XINGS ** xings)9326 unsigned int gmtlib_map_loncross (struct GMT_CTRL *GMT, double lon, double south, double north, struct GMT_XINGS **xings) {
9327 bool go = false;
9328 unsigned int j, nx, nc = 0;
9329 size_t n_alloc = GMT_SMALL_CHUNK;
9330 double lat, lat_old, this_x, this_y, last_x, last_y, xlon[2], xlat[2], gap;
9331 struct GMT_XINGS *X = NULL;
9332
9333 X = gmt_M_memory (GMT, NULL, n_alloc, struct GMT_XINGS);
9334
9335 lat_old = ((south - (2.0 * GMT_CONV4_LIMIT)) >= -90.0) ? south - 2.0 * GMT_CONV4_LIMIT : south; /* Outside */
9336 if ((north + 2.0 * GMT_CONV4_LIMIT) <= 90.0) north += 2.0 * GMT_CONV4_LIMIT;
9337 gmt_map_outside (GMT, lon, lat_old);
9338 gmt_geo_to_xy (GMT, lon, lat_old, &last_x, &last_y);
9339 for (j = 1; j <= GMT->current.map.n_lat_nodes; j++) {
9340 lat = (j == GMT->current.map.n_lat_nodes) ? north: south + j * GMT->current.map.dlat;
9341 gmt_map_outside (GMT, lon, lat);
9342 gmt_geo_to_xy (GMT, lon, lat, &this_x, &this_y);
9343 if ((nx = gmtmap_crossing (GMT, lon, lat_old, lon, lat, xlon, xlat, X[nc].xx, X[nc].yy, X[nc].sides))) {
9344 X[nc].angle[0] = gmtmap_get_angle (GMT, lon, lat_old, lon, lat); /* Get angle at first crossing */
9345 if (nx == 2) X[nc].angle[1] = X[nc].angle[0] + 180.0; /* If a 2nd crossing it must be really close so just add 180 */
9346 if (GMT->current.map.corner > 0) {
9347 X[nc].sides[0] = (GMT->current.map.corner < 3) ? 0 : 2;
9348 GMT->current.map.corner = 0;
9349 }
9350 gmt_M_memcpy (X[nc].lon, xlon, 2U, double);
9351 gmt_M_memcpy (X[nc].lat, xlat, 2U, double);
9352 }
9353 else if (GMT->current.map.is_world) /* Deal with possibility of wrapping around 360 */
9354 nx = (*GMT->current.map.wrap_around_check) (GMT, X[nc].angle, last_x, last_y, this_x, this_y, X[nc].xx, X[nc].yy, X[nc].sides);
9355 if (nx == 2 && fabs (fabs (X[nc].xx[1] - X[nc].xx[0]) - GMT->current.map.width) < GMT_CONV4_LIMIT && !GMT->current.map.is_world)
9356 /* I assume this means if we have a crossing and both left and right and not worldmap then skip as something went wrong? */
9357 go = false;
9358 else if (nx == 2 && (gap = fabs (X[nc].yy[1] - X[nc].yy[0])) > GMT_CONV4_LIMIT && fabs (gap - GMT->current.map.height) < GMT_CONV4_LIMIT && !GMT->current.map.is_world_tm)
9359 /* I assume this means if we have a crossing and both top and bottom and it is not a global UTM map then skip as something went wrong? */
9360 go = false;
9361 else if (nx > 0)
9362 go = true;
9363 if (go) {
9364 X[nc].nx = nx;
9365 nc++;
9366 if (nc == n_alloc) {
9367 n_alloc <<= 1;
9368 X = gmt_M_memory (GMT, X, n_alloc, struct GMT_XINGS);
9369 }
9370 go = false;
9371 }
9372 lat_old = lat;
9373 last_x = this_x; last_y = this_y;
9374 }
9375
9376 if (nc > 0) {
9377 X = gmt_M_memory (GMT, X, nc, struct GMT_XINGS);
9378 *xings = X;
9379 }
9380 else
9381 gmt_M_free (GMT, X);
9382
9383 return (nc);
9384 }
9385
gmtmap_reset_oblique_settings(struct GMT_CTRL * GMT)9386 void gmtmap_reset_oblique_settings (struct GMT_CTRL *GMT) {
9387 /* The modern mode default for MAP_ANNOT_OBLIQUE has settings that only makes sense for oblique plots with rectangular borders.
9388 * Thus, if the current projection is not like that then we reset the default to a more benign default setting. However, if
9389 * the user has actively changed the MAP_ANNOT_OBLIQUE value then we do nothing. */
9390
9391 if (GMT->current.setting.map_annot_oblique_set) return; /* User changed MAP_ANNOT_OBLIQUE so we cannot reset anything */
9392 if (GMT->common.R.oblique) return; /* These are the appropriate defaults for oblique projections with rectangular borders */
9393
9394 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Reset MAP_ANNOT_OBLIQUE to anywhere\n");
9395
9396 GMT->current.setting.map_annot_oblique = GMT_OBL_ANNOT_ANYWHERE; /* Reset to non-oblique map defaults */
9397 }
9398
gmt_proj_setup(struct GMT_CTRL * GMT,double wesn[])9399 int gmt_proj_setup (struct GMT_CTRL *GMT, double wesn[]) {
9400 /* Core map setup function for the projection. All program needing projection support
9401 * will call this either directly or indirectly. Programs that _just_ need to project
9402 * to/from may call this function only, while all the plotting program need to have more
9403 * things initialized and they will call gmt_map_setup instead, which starts by calling
9404 * gmt_proj_setup first. */
9405
9406 bool search = false;
9407 int error = GMT_NOERROR;
9408
9409 if (wesn[XHI] == wesn[XLO] && wesn[YHI] == wesn[YLO]) Error_and_return (GMT_MAP_NO_REGION, GMT_NOT_A_VALID_DOMAIN); /* Since -R may not be involved if there are grids */
9410
9411 if (!GMT->common.J.active) {
9412 char *def_args[2] = {"X15c", "Q15c"};
9413 unsigned int geo;
9414 if (GMT->current.setting.run_mode == GMT_CLASSIC) /* This is a fatal error in classic mode */
9415 Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR);
9416 if (!GMT->current.ps.active)
9417 Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR); /* Only auto-setup a projection for mapping and plots */
9418 /* Here we are in modern mode starting a new plot without a map projection. The rules says use -JQ15c (geo) or -JX15c (Cartesian) */
9419 geo = gmt_M_is_geographic (GMT, GMT_IN);
9420 gmt_parse_common_options (GMT, "J", 'J', def_args[geo]);
9421 GMT->common.J.active = true;
9422 }
9423
9424 gmtlib_init_ellipsoid (GMT); /* Set parameters depending on the ellipsoid since the latter could have been set explicitly */
9425
9426 if (gmt_M_x_is_lon (GMT, GMT_IN)) {
9427 /* Limit east-west range to 360 and make sure east > -180 and west < 360 */
9428 if (!GMT->common.R.oblique || gmt_M_is_rect_graticule (GMT)) { /* Only makes sense if not corner coordinates */
9429 if (wesn[XHI] < wesn[XLO]) wesn[XHI] += 360.0;
9430 if ((fabs (wesn[XHI] - wesn[XLO]) - 360.0) > GMT_CONV4_LIMIT) {
9431 Error_and_return (GMT_MAP_EXCEEDS_360, GMT_NOT_A_VALID_DOMAIN);
9432 }
9433 }
9434 while (wesn[XHI] < -180.0) {
9435 wesn[XLO] += 360.0;
9436 wesn[XHI] += 360.0;
9437 }
9438 while (wesn[XLO] > 360.0) {
9439 wesn[XLO] -= 360.0;
9440 wesn[XHI] -= 360.0;
9441 }
9442 }
9443 else if (gmt_M_y_is_lon (GMT, GMT_IN)) {
9444 /* Limit east-west range to 360 and make sure east > -180 and west < 360 */
9445 if (wesn[YHI] < wesn[YLO]) wesn[YHI] += 360.0;
9446 if ((fabs (wesn[YHI] - wesn[YLO]) - 360.0) > GMT_CONV4_LIMIT) {
9447 Error_and_return (GMT_MAP_EXCEEDS_360, GMT_NOT_A_VALID_DOMAIN);
9448 }
9449 while (wesn[YHI] < -180.0) {
9450 wesn[YLO] += 360.0;
9451 wesn[YHI] += 360.0;
9452 }
9453 while (wesn[YLO] > 360.0) {
9454 wesn[YLO] -= 360.0;
9455 wesn[YHI] -= 360.0;
9456 }
9457 }
9458 if (GMT->current.proj.got_elevations) {
9459 if (wesn[YLO] < 0.0 || wesn[YLO] >= 90.0) Error_and_return (GMT_MAP_BAD_ELEVATION_MIN, GMT_NOT_A_VALID_DOMAIN);
9460 if (wesn[YHI] <= 0.0 || wesn[YHI] > 90.0) Error_and_return (GMT_MAP_BAD_ELEVATION_MAX, GMT_NOT_A_VALID_DOMAIN);
9461 }
9462 if (gmt_M_y_is_lat (GMT, GMT_IN)) {
9463 if (wesn[YLO] < -90.0 || wesn[YLO] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MIN, GMT_NOT_A_VALID_DOMAIN);
9464 if (wesn[YHI] < -90.0 || wesn[YHI] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MAX, GMT_NOT_A_VALID_DOMAIN);
9465 }
9466 else if (gmt_M_x_is_lat (GMT, GMT_IN)) {
9467 if (wesn[XLO] < -90.0 || wesn[XLO] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MIN, GMT_NOT_A_VALID_DOMAIN);
9468 if (wesn[XHI] < -90.0 || wesn[XHI] > 90.0) Error_and_return (GMT_MAP_BAD_LAT_MAX, GMT_NOT_A_VALID_DOMAIN);
9469 }
9470
9471 if (GMT->common.R.wesn != wesn) /* In many cases they are both copies of same pointer */
9472 gmt_M_memcpy (GMT->common.R.wesn, wesn, 4, double);
9473 GMT->current.proj.GMT_convert_latitudes = false;
9474 if (GMT->current.proj.gave_map_width) GMT->current.proj.units_pr_degree = false;
9475
9476 GMT->current.map.meridian_straight = GMT->current.map.parallel_straight = 0;
9477 GMT->current.map.n_lon_nodes = GMT->current.map.n_lat_nodes = 0;
9478 GMT->current.map.wrap_around_check = &gmtmap_wrap_around_check_x;
9479 GMT->current.map.jump = &gmtmap_jump_x;
9480 GMT->current.map.will_it_wrap = &gmtmap_will_it_wrap_x;
9481 #if 0
9482 GMT->current.map.this_point_wraps = &gmtmap_this_point_wraps_x;
9483 #endif
9484 GMT->current.map.get_crossings = &gmtmap_get_crossings_x;
9485
9486 GMT->current.map.lon_wrap = true;
9487 GMT->current.map.lat_wrap = false;
9488 GMT->current.map.lon_wrap_range = 360.0;
9489
9490 gmtmap_reset_oblique_settings (GMT); /* Maybe reset MAP_ANNOT_OBLIQUE defaults depending on situation */
9491
9492 switch (GMT->current.proj.projection) {
9493
9494 case GMT_LINEAR: /* Linear transformations */
9495 error = gmtmap_init_linear (GMT, &search);
9496 break;
9497
9498 case GMT_POLAR: /* Both lon/lat are actually theta, radius */
9499 error = gmtmap_init_polar (GMT, &search);
9500 break;
9501
9502 case GMT_MERCATOR: /* Standard Mercator projection */
9503 error = gmtmap_init_merc (GMT, &search);
9504 break;
9505
9506 case GMT_STEREO: /* Stereographic projection */
9507 error = gmtmap_init_stereo (GMT, &search);
9508 break;
9509
9510 case GMT_LAMBERT: /* Lambert Conformal Conic */
9511 error = gmtmap_init_lambert (GMT, &search);
9512 break;
9513
9514 case GMT_OBLIQUE_MERC: /* Oblique Mercator */
9515 error = gmtmap_init_oblique (GMT, &search);
9516 break;
9517
9518 case GMT_TM: /* Transverse Mercator */
9519 error = gmtmap_init_tm (GMT, &search);
9520 break;
9521
9522 case GMT_UTM: /* Universal Transverse Mercator */
9523 error = gmtmap_init_utm (GMT, &search);
9524 break;
9525
9526 case GMT_LAMB_AZ_EQ: /* Lambert Azimuthal Equal-Area */
9527 error = gmtmap_init_lambeq (GMT, &search);
9528 break;
9529
9530 case GMT_ORTHO: /* Orthographic Projection */
9531 error = gmtmap_init_ortho (GMT, &search);
9532 break;
9533
9534 case GMT_GENPER: /* General Perspective Projection */
9535 error = gmtmap_init_genper (GMT, &search);
9536 break;
9537
9538 case GMT_AZ_EQDIST: /* Azimuthal Equal-Distance Projection */
9539 error = gmtmap_init_azeqdist (GMT, &search);
9540 break;
9541
9542 case GMT_GNOMONIC: /* Azimuthal Gnomonic Projection */
9543 error = gmtmap_init_gnomonic (GMT, &search);
9544 break;
9545
9546 case GMT_MOLLWEIDE: /* Mollweide Equal-Area */
9547 error = gmtmap_init_mollweide (GMT, &search);
9548 break;
9549
9550 case GMT_HAMMER: /* Hammer-Aitoff Equal-Area */
9551 error = gmtmap_init_hammer (GMT, &search);
9552 break;
9553
9554 case GMT_VANGRINTEN: /* Van der Grinten */
9555 error = gmtmap_init_grinten (GMT, &search);
9556 break;
9557
9558 case GMT_WINKEL: /* Winkel Tripel */
9559 error = gmtmap_init_winkel (GMT, &search);
9560 break;
9561
9562 case GMT_ECKERT4: /* Eckert IV */
9563 error = gmtmap_init_eckert4 (GMT, &search);
9564 break;
9565
9566 case GMT_ECKERT6: /* Eckert VI */
9567 error = gmtmap_init_eckert6 (GMT, &search);
9568 break;
9569
9570 case GMT_CYL_EQ: /* Cylindrical Equal-Area */
9571 error = gmtmap_init_cyleq (GMT, &search);
9572 break;
9573
9574 case GMT_CYL_STEREO: /* Cylindrical Stereographic */
9575 error = gmtmap_init_cylstereo (GMT, &search);
9576 break;
9577
9578 case GMT_MILLER: /* Miller Cylindrical */
9579 error = gmtmap_init_miller (GMT, &search);
9580 break;
9581
9582 case GMT_CYL_EQDIST: /* Cylindrical Equidistant */
9583 error = gmtmap_init_cyleqdist (GMT, &search);
9584 break;
9585
9586 case GMT_ROBINSON: /* Robinson */
9587 error = gmtmap_init_robinson (GMT, &search);
9588 break;
9589
9590 case GMT_SINUSOIDAL: /* Sinusoidal Equal-Area */
9591 error = gmtmap_init_sinusoidal (GMT, &search);
9592 break;
9593
9594 case GMT_CASSINI: /* Cassini cylindrical */
9595 error = gmtmap_init_cassini (GMT, &search);
9596 break;
9597
9598 case GMT_ALBERS: /* Albers Equal-Area Conic */
9599 error = gmtmap_init_albers (GMT, &search);
9600 break;
9601
9602 case GMT_ECONIC: /* Equidistant Conic */
9603 error = gmtmap_init_econic (GMT, &search);
9604 break;
9605
9606 case GMT_POLYCONIC: /* Polyconic */
9607 error = gmtmap_init_polyconic (GMT, &search);
9608 break;
9609
9610 #ifdef HAVE_GDAL
9611 case GMT_PROJ4_PROJS: /* All proj.4 projections */
9612 error = map_init_proj4 (GMT, &search);
9613 break;
9614 #endif
9615
9616 default: /* No projection selected, return to a horrible death */
9617 Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR);
9618 }
9619 if (error) return (GMT_PROJECTION_ERROR); /* Something went wrong in one of the map_init_* functions */
9620
9621 if (GMT->current.proj.fwd == NULL) /* Some error in projection projection parameters, return to a horrible death */
9622 Error_and_return (GMT_MAP_NO_PROJECTION, GMT_PROJECTION_ERROR);
9623
9624 GMT->current.proj.search = search;
9625
9626 GMT->current.proj.i_scale[GMT_X] = (GMT->current.proj.scale[GMT_X] != 0.0) ? 1.0 / GMT->current.proj.scale[GMT_X] : 1.0;
9627 GMT->current.proj.i_scale[GMT_Y] = (GMT->current.proj.scale[GMT_Y] != 0.0) ? 1.0 / GMT->current.proj.scale[GMT_Y] : 1.0;
9628 GMT->current.proj.i_scale[GMT_Z] = (GMT->current.proj.scale[GMT_Z] != 0.0) ? 1.0 / GMT->current.proj.scale[GMT_Z] : 1.0;
9629
9630 GMT->current.map.width = fabs (GMT->current.proj.rect[XHI] - GMT->current.proj.rect[XLO]);
9631 GMT->current.map.height = fabs (GMT->current.proj.rect[YHI] - GMT->current.proj.rect[YLO]);
9632 GMT->current.map.half_width = 0.5 * GMT->current.map.width;
9633 GMT->current.map.half_height = 0.5 * GMT->current.map.height;
9634
9635 if (gmt_M_x_is_lon (GMT, GMT_IN)) { /* x is longitude */
9636 if (GMT->current.proj.central_meridian < GMT->common.R.wesn[XLO] && (GMT->current.proj.central_meridian + 360.0) <= GMT->common.R.wesn[XHI]) GMT->current.proj.central_meridian += 360.0;
9637 if (GMT->current.proj.central_meridian > GMT->common.R.wesn[XHI] && (GMT->current.proj.central_meridian - 360.0) >= GMT->common.R.wesn[XLO]) GMT->current.proj.central_meridian -= 360.0;
9638 }
9639
9640 if (!GMT->current.map.n_lon_nodes) GMT->current.map.n_lon_nodes = urint (GMT->current.map.width / GMT->current.setting.map_line_step);
9641 if (!GMT->current.map.n_lat_nodes) GMT->current.map.n_lat_nodes = urint (GMT->current.map.height / GMT->current.setting.map_line_step);
9642
9643 error = gmtmap_init_three_D (GMT);
9644
9645 return (error);
9646 }
9647
gmt_map_perimeter_search(struct GMT_CTRL * GMT,double * wesn,bool add_pad)9648 int gmt_map_perimeter_search (struct GMT_CTRL *GMT, double *wesn, bool add_pad) {
9649 /* Loop around rectangular perimeter and determine min/max lon/lat extent.
9650 * If add_pad is true then we extend by 0.1 degrees (as per GMT historical settings) */
9651 int error = GMT_NOERROR;
9652 if (GMT->current.proj.projection_GMT == GMT_GENPER) /* Need special considerations for this projection */
9653 gmtmap_genper_search (GMT, &GMT->common.R.wesn[XLO], &GMT->common.R.wesn[XHI], &GMT->common.R.wesn[YLO], &GMT->common.R.wesn[YHI], add_pad);
9654 else /* Search along the projected border */
9655 gmt_wesn_search (GMT, GMT->current.proj.rect[XLO], GMT->current.proj.rect[XHI], GMT->current.proj.rect[YLO], GMT->current.proj.rect[YHI], &GMT->common.R.wesn[XLO], &GMT->common.R.wesn[XHI], &GMT->common.R.wesn[YLO], &GMT->common.R.wesn[YHI], add_pad);
9656 GMT->current.map.dlon = (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / GMT->current.map.n_lon_nodes;
9657 GMT->current.map.dlat = (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / GMT->current.map.n_lat_nodes;
9658 if (gmt_M_is_azimuthal(GMT) && GMT->common.R.oblique) {
9659 if ((error = gmtmap_horizon_search (GMT, wesn[XLO], wesn[XHI], wesn[YLO], wesn[YHI], GMT->current.proj.rect[XLO], GMT->current.proj.rect[XHI], GMT->current.proj.rect[YLO], GMT->current.proj.rect[YHI])))
9660 return (error);
9661 }
9662 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "gmt_map_setup perimeter search region: %.16lg/%.16lg/%.16lg/%.16lg.\n",
9663 GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
9664 return (GMT_NOERROR);
9665 }
9666
9667 /*! . */
gmt_map_setup(struct GMT_CTRL * GMT,double wesn[])9668 int gmt_map_setup (struct GMT_CTRL *GMT, double wesn[]) {
9669 unsigned int i;
9670 int error;
9671 bool search, double_auto[6];
9672 bool test_pole[2] = {true, true}, separate_intervals = false;
9673 double scale, i_scale;
9674
9675 if ((error = gmt_proj_setup (GMT, wesn)) != GMT_NOERROR) goto gmt_map_setup_end;
9676
9677 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 */
9678
9679 search = GMT->current.proj.search;
9680
9681 /* If intervals are not set specifically, round them to some "nice" values
9682 * Remember whether frame items in both directions were automatically set */
9683 for (i = 0; i < 6; i++)
9684 double_auto[i] = gmt_M_is_geographic (GMT, GMT_IN) && GMT->current.map.frame.set_both &&
9685 GMT->current.map.frame.axis[GMT_X].item[i].active && GMT->current.map.frame.axis[GMT_X].item[i].interval == 0.0 &&
9686 GMT->current.map.frame.axis[GMT_Y].item[i].active && GMT->current.map.frame.axis[GMT_Y].item[i].interval == 0.0;
9687
9688 gmt_auto_frame_interval (GMT, GMT_X, GMT_ANNOT_UPPER);
9689 gmt_auto_frame_interval (GMT, GMT_Y, GMT_ANNOT_UPPER);
9690 gmt_auto_frame_interval (GMT, GMT_Z, GMT_ANNOT_UPPER);
9691 gmt_auto_frame_interval (GMT, GMT_X, GMT_ANNOT_LOWER);
9692 gmt_auto_frame_interval (GMT, GMT_Y, GMT_ANNOT_LOWER);
9693 gmt_auto_frame_interval (GMT, GMT_Z, GMT_ANNOT_LOWER);
9694
9695 /* Then check if one or both poles are inside map. If they are then the longitude range will be 360
9696 * and depending on the latitude extent it may not make sense to use the coarse longitude-spacing determined
9697 * also for the latitude spacing. We check this and if a pole is inside and if the latitude range is < 1/4
9698 * of that of the longitude (e.g., 90 degrees for a 0-360 global map) we go separate. */
9699
9700 if (GMT->current.proj.projection_GMT == GMT_AZ_EQDIST) { /* Must be careful since if a pole equals an antipode we get NaNs as coordinates */
9701 double x, y;
9702 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, -90.0, &x, &y);
9703 if (gmt_M_is_dnan (x) && gmt_M_is_dnan (y)) test_pole[0] = false;
9704 gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian, +90.0, &x, &y);
9705 if (gmt_M_is_dnan (x) && gmt_M_is_dnan (y)) test_pole[1] = false;
9706 }
9707 if (test_pole[0] && !gmt_map_outside (GMT, GMT->current.proj.central_meridian, -90.0)) GMT->current.proj.pole_in_map[0] = separate_intervals = true;
9708 if (test_pole[1] && !gmt_map_outside (GMT, GMT->current.proj.central_meridian, +90.0)) GMT->current.proj.pole_in_map[1] = separate_intervals = true;
9709 if (separate_intervals && (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) > 0.25)
9710 separate_intervals = false; /* E.g, for a whole global hemisphere we keep them the same, else we adjust individually */
9711
9712 /* Now set the pairs of automatically set intervals to be the same in both x- and y-direction, except when a geographic pole is inside an oblique map frame */
9713 for (i = 0; i < 6; i++) {
9714 if (double_auto[i] && !separate_intervals) GMT->current.map.frame.axis[GMT_X].item[i].interval = GMT->current.map.frame.axis[GMT_Y].item[i].interval =
9715 MAX (GMT->current.map.frame.axis[GMT_X].item[i].interval, GMT->current.map.frame.axis[GMT_Y].item[i].interval);
9716 }
9717
9718 if (!GMT->current.map.n_lon_nodes) GMT->current.map.n_lon_nodes = urint (GMT->current.map.width / GMT->current.setting.map_line_step);
9719 if (!GMT->current.map.n_lat_nodes) GMT->current.map.n_lat_nodes = urint (GMT->current.map.height / GMT->current.setting.map_line_step);
9720
9721 GMT->current.map.dlon = (GMT->common.R.wesn[XHI] - GMT->common.R.wesn[XLO]) / GMT->current.map.n_lon_nodes;
9722 GMT->current.map.dlat = (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO]) / GMT->current.map.n_lat_nodes;
9723
9724 if (GMT->current.map.width > 400.0 && gmt_M_is_grdmapproject (GMT)) { /* ***project calling with true scale, probably */
9725 search = false; /* Safe-guard that prevents region search below for (map|grd)project and others (400 inch = ~> 10 meters) */
9726 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_map_setup perimeter search skipped when using true scale with grdproject or mapproject.\n");
9727 }
9728
9729 if (search) { /* Loop around rectangular perimeter and determine min/max lon/lat extent */
9730 if ((error = gmt_map_perimeter_search (GMT, wesn, true)))
9731 goto gmt_map_setup_end;
9732 }
9733
9734 /* Maximum step size (in degrees) used for interpolation of line segments along great circles (or meridians/parallels) before they are plotted */
9735 GMT->current.map.path_step = GMT->current.setting.map_line_step / GMT->current.proj.scale[GMT_X] / GMT->current.proj.M_PR_DEG;
9736
9737 i_scale = 1.0 / (0.0254 * GMT->current.proj.scale[GMT_X]);
9738 scale = 0.001 / (GMT->session.u2u[GMT_INCH][GMT->current.setting.proj_length_unit] * GMT->current.proj.scale[GMT_X]);
9739 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Map scale is %g km per %s or 1:%g.\n",
9740 scale, GMT->session.unit_name[GMT->current.setting.proj_length_unit], i_scale);
9741
9742 gmt_map_setup_end:
9743
9744 if (error) GMT_Report(GMT->parent, GMT_MSG_ERROR, "General map projection error\n");
9745 return (error);
9746 }
9747
9748 /*! . */
gmt_init_distaz(struct GMT_CTRL * GMT,char unit,unsigned int mode,unsigned int type)9749 unsigned int gmt_init_distaz (struct GMT_CTRL *GMT, char unit, unsigned int mode, unsigned int type) {
9750 /* Initializes distance calculation given the selected values for:
9751 * Distance unit: must be on of the following:
9752 * 1) d|e|f|k|m|M|n|s
9753 * 2) GMT (Cartesian distance after projecting with -J) | X (Cartesian)
9754 * 3) S (cosine distance) | P (cosine after first inverse projecting with -J)
9755 * distance-calculation modifier mode flags are:
9756 * 0 (Cartesian), 1 (flat Earth), 2 (great-circle [Default]), 3 (geodesic), 4 (loxodrome)
9757 * However, if -j<mode> is set it takes precedence.
9758 * type: 0 = map distances, 1 = contour distances, 2 = contour annotation distances
9759 * We set distance and azimuth functions and scales for this type.
9760 * At the moment there is only one azimuth function pointer for all.
9761 *
9762 * The input args for gmt_init_distaz normally comes from calling gmt_get_distance.
9763 */
9764
9765 unsigned int proj_type = GMT_GEOGRAPHIC; /* Default is to just use the geographic coordinates as they are */
9766 int err = GMT_NOERROR;
9767
9768 if (strchr (GMT_LEN_UNITS, unit) && gmt_M_is_cartesian (GMT, GMT_IN)) { /* Want geographic distance units but -fg (or -J) not set */
9769 gmt_parse_common_options (GMT, "f", 'f', "g");
9770 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Your distance unit (%c) implies geographic data; -fg has been set.\n", unit);
9771 }
9772
9773 /* Determine if -j selected a particular mode */
9774 if (gmt_M_is_geographic (GMT, GMT_IN) && GMT->common.j.active) { /* User specified a -j setting */
9775 static char *kind[5] = {"Cartesian", "Flat Earth", "Great Circle", "Geodesic", "Loxodrome"};
9776 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Spherical distance calculation mode: %s.\n", kind[GMT->common.j.active]);
9777 if (mode != GMT->common.j.mode) /* We override a selection due to deprecated leading -|+ signs before increment or radius */
9778 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your distance mode (%s) differs from your -j option (%s) which takes precedence.\n", kind[mode], kind[GMT->common.j.mode]);
9779 mode = GMT->common.j.mode; /* Override with what -j said */
9780 }
9781
9782 switch (unit) {
9783 /* First the three arc angular distance units */
9784
9785 case 'd': /* Arc degrees on spherical body using desired metric mode */
9786 err = gmtmap_set_distaz (GMT, GMT_DIST_DEG + mode, type, "arc-degree");
9787 GMT->current.map.dist[type].arc = true; /* Angular measure */
9788 break;
9789 case 'm': /* Arc minutes on spherical body using desired metric mode */
9790 err = gmtmap_set_distaz (GMT, GMT_DIST_DEG + mode, type, "arc-minute");
9791 GMT->current.map.dist[type].scale = GMT_DEG2MIN_F;
9792 GMT->current.map.dist[type].arc = true; /* Angular measure */
9793 break;
9794 case 's': /* Arc seconds on spherical body using desired metric mode */
9795 err = gmtmap_set_distaz (GMT, GMT_DIST_DEG + mode, type, "arc-second");
9796 GMT->current.map.dist[type].scale = GMT_DEG2SEC_F;
9797 GMT->current.map.dist[type].arc = true; /* Angular measure */
9798 break;
9799
9800 /* Various distance units on the planetary body */
9801
9802 case 'e': /* Meters on spherical body using desired metric mode */
9803 err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "meter");
9804 break;
9805 case 'f': /* Feet on spherical body using desired metric mode */
9806 err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "foot");
9807 GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_FOOT;
9808 break;
9809 case 'k': /* Kilometers on spherical body using desired metric mode */
9810 err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "km");
9811 GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_KM;
9812 break;
9813 case 'M': /* Statute Miles on spherical body using desired metric mode */
9814 err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "mile");
9815 GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_MILE;
9816 break;
9817 case 'n': /* Nautical miles on spherical body using desired metric mode */
9818 err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "nautical mile");
9819 GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_NAUTICAL_MILE;
9820 break;
9821 case 'u': /* Survey feet on spherical body using desired metric mode */
9822 err = gmtmap_set_distaz (GMT, GMT_DIST_M + mode, type, "survey feet");
9823 GMT->current.map.dist[type].scale = 1.0 / METERS_IN_A_SURVEY_FOOT;
9824 break;
9825
9826 /* Cartesian distances. Note: The X|C|R|Z|S|P 'units' are only passed internally and are not available as user selections directly */
9827
9828 case 'X': /* Cartesian distances in user units */
9829 proj_type = GMT_CARTESIAN;
9830 if (GMT->common.n.periodic[GMT_X] || GMT->common.n.periodic[GMT_Y])
9831 err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST_PERIODIC, type, "");
9832 else
9833 err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST, type, "");
9834 break;
9835 case 'C': /* Cartesian distances (in PROJ_LENGTH_UNIT) after first projecting input coordinates with -J is still just GMT_CARTESIAN_DIST */
9836 err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST, type, "");
9837 proj_type = GMT_GEO2CART;
9838 break;
9839
9840 case 'R': /* Cartesian distances squared in user units */
9841 proj_type = GMT_CARTESIAN;
9842 err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST2, type, "");
9843 break;
9844 case 'Z': /* Cartesian distances squared (in PROJ_LENGTH_UNIT^2) after first projecting input coordinates with -J */
9845 err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST_PROJ2, type, "");
9846 proj_type = GMT_GEO2CART;
9847 break;
9848
9849 /* Specialized cosine distances used internally only (e.g., greenspline) */
9850
9851 case 'S': /* Spherical cosine distances (for various gridding functions) */
9852 err = gmtmap_set_distaz (GMT, GMT_DIST_COS + mode, type, "");
9853 break;
9854 case 'P': /* Spherical distances after first inversely projecting Cartesian coordinates with -J */
9855 err = gmtmap_set_distaz (GMT, GMT_CARTESIAN_DIST_PROJ_INV, type, "");
9856 proj_type = GMT_CART2GEO;
9857 break;
9858
9859 default:
9860 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Distance units must be one of %s\n", GMT_LEN_UNITS_DISPLAY);
9861 err = GMT_NOT_A_VALID_TYPE;
9862 break;
9863 }
9864 if (err) return (err);
9865 GMT->current.map.dist[type].init = true; /* OK, we have now initialized the info for this type */
9866 return (proj_type);
9867 }
9868
9869 /*! . */
gmt_get_smallcircle(struct GMT_CTRL * GMT,double plon,double plat,double colat,uint64_t m)9870 struct GMT_DATASEGMENT * gmt_get_smallcircle (struct GMT_CTRL *GMT, double plon, double plat, double colat, uint64_t m) {
9871 /* Function to generate m equidistant coordinates for an oblique small circle about a given pole.
9872 * To avoid some downstream problems we make sure that if circle crosses Greenwhich or Dateline that
9873 * we insert a point at lon = 0 and/or lon = 180. I imagine that in the worst-case scenario we could
9874 * cross those lines twice each, hence I allocate 4 more spaces than needed. */
9875 double P[3], X[3], N[3], R[3][3], xlat, dlon, xx, yy, x, y, last_x = 0.0, last_y = 0.0, dx;
9876 uint64_t k, n;
9877 struct GMT_DATASEGMENT *S = NULL;
9878 if (m < 2) return NULL;
9879
9880 S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, m+4, 2, NULL, NULL); /* The output segment - allocate array space (+ 4 extra) */
9881 plat = gmt_lat_swap (GMT, plat, GMT_LATSWAP_G2O); /* Convert to geocentric coordinates */
9882 gmt_geo_to_cart (GMT, plat, plon, P, true); /* Get pole vector P */
9883 xlat = (plat > 0.0) ? plat - colat : plat + colat; /* Starting point along meridian through P but colat degrees away */
9884 xlat = gmt_lat_swap (GMT, xlat, GMT_LATSWAP_G2O); /* Convert to geocentric */
9885 gmt_geo_to_cart (GMT, xlat, plon, X, true); /* Generating vector X we will rotate about P */
9886 dlon = 360.0 / (m - 1); /* Point spacing along the small circle in oblique longitude degrees */
9887 for (k = n = 0; k < m; k++, n++) { /* Given how dlon was set we end up closing the polygon exactly */
9888 gmt_make_rot_matrix2 (GMT, P, k * dlon, R); /* Rotation matrix about P for this increment in oblique longitude */
9889 gmt_matrix_vect_mult (GMT, 3U, R, X, N); /* Rotate the X-vector to N */
9890 gmt_cart_to_geo (GMT, &y, &x, N, true); /* Recover lon,lat of rotated point */
9891 y = gmt_lat_swap (GMT, y, GMT_LATSWAP_O2G); /* Convert back to geodetic coordinates */
9892 if (k) {
9893 dx = x - last_x;
9894 if (fabs (dx) > 180.0) { /* Jump in longitudes */
9895 dx = copysign (360.0 - fabs (dx), -dx);
9896 xx = (fabs (last_x) > 270 || fabs(last_x) < 90.0) ? 0.0 : copysign (180.0, last_x);
9897 yy = last_y + (y - last_y) * (xx - last_x) / dx;
9898 S->data[GMT_X][n] = xx; S->data[GMT_Y][n++] = yy; /* Assign the extra element */
9899 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_get_smallcircle: Added extra point at %g/%g\n", xx, yy);
9900 }
9901 else if ((x > 0.0 && last_x < 0.0) || (x < 0.0 && last_x > 0.0)) { /* Crossing Greenwhich */
9902 xx = 0.0;
9903 yy = last_y + (y - last_y) * (xx - last_x) / dx;
9904 S->data[GMT_X][n] = xx; S->data[GMT_Y][n++] = yy; /* Assign the extra element */
9905 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmt_get_smallcircle: Added extra point at %g/%g\n", xx, yy);
9906 }
9907 }
9908 S->data[GMT_X][n] = x; S->data[GMT_Y][n] = y; /* Assign the array elements */
9909 last_x = x; last_y = y;
9910 }
9911 S->data[GMT_X][n - 1] = S->data[GMT_X][0]; /* Make really sure the polygon is closed */
9912 S->data[GMT_Y][n - 1] = S->data[GMT_Y][0];
9913 S->n_rows = n;
9914 gmt_set_seg_minmax (GMT, GMT_IS_POLY, 2, S);
9915 return (S); /* Pass out the results */
9916 }
9917
gmtmap_ellipse_point(struct GMT_CTRL * GMT,double lon,double lat,double center,double sinp,double cosp,double major,double minor,double cos_azimuth,double sin_azimuth,double angle,double * plon,double * plat)9918 GMT_LOCAL void gmtmap_ellipse_point (struct GMT_CTRL *GMT, double lon, double lat, double center, double sinp, double cosp, double major, double minor, double cos_azimuth, double sin_azimuth, double angle, double *plon, double *plat) {
9919 /* Lon, lat is center of ellipse, our point is making an angle with the major axis. */
9920 double x, y, x_prime, y_prime, rho, c, sin_c, cos_c;
9921 sincos (angle, &y, &x);
9922 x *= major; y *= minor;
9923 /* Get rotated coordinates in m */
9924 x_prime = x * cos_azimuth - y * sin_azimuth;
9925 y_prime = x * sin_azimuth + y * cos_azimuth;
9926 /* Convert m back to lon lat */
9927 rho = hypot (x_prime, y_prime);
9928 c = rho / GMT->current.proj.EQ_RAD;
9929 sincos (c, &sin_c, &cos_c);
9930 *plat = d_asind (cos_c * sinp + (y_prime * sin_c * cosp / rho));
9931 if ((lat - 90.0) > -GMT_CONV8_LIMIT) /* origin in Northern hemisphere */
9932 *plon = lon + d_atan2d (x_prime, -y_prime);
9933 else if ((lat + 90.0) < GMT_CONV8_LIMIT) /* origin in Southern hemisphere */
9934 *plon = lon + d_atan2d (x_prime, y_prime);
9935 else
9936 *plon = lon + d_atan2d (x_prime * sin_c, (rho * cosp * cos_c - y_prime * sinp * sin_c));
9937 while ((*plon - center) < -180.0) *plon += 360.0;
9938 while ((*plon - center) > +180.0) *plon -= 360.0;
9939 }
9940
9941 #define GMT_ELLIPSE_APPROX 72
9942
gmt_get_geo_ellipse(struct GMT_CTRL * GMT,double lon,double lat,double major_km,double minor_km,double azimuth,uint64_t m)9943 struct GMT_DATASEGMENT * gmt_get_geo_ellipse (struct GMT_CTRL *GMT, double lon, double lat, double major_km, double minor_km, double azimuth, uint64_t m) {
9944 /* gmt_get_geo_ellipse takes the location, axes (in km), and azimuth of the ellipse's major axis
9945 and computes coordinates for an approximate ellipse using N-sided polygon.
9946 If m > 0 then we l\set N = m and use that many points. Otherwise (m == 0), we will
9947 determine N by looking at the spacing between successive points for a trial N and
9948 then scale N up or down to satisfy the minimum point spacing criteria. */
9949
9950 uint64_t i, N;
9951 double delta_azimuth, sin_azimuth, cos_azimuth, sinp, cosp, ax, ay, axx, ayy, bx, by, bxx, byy, L;
9952 double major, minor, center, *px = NULL, *py = NULL;
9953 char header[GMT_LEN256] = {""};
9954 struct GMT_DATASEGMENT *S = NULL;
9955
9956 major = major_km * 500.0, minor = minor_km * 500.0; /* Convert to meters (x1000) of semi-major (/2) and semi-minor axes */
9957 /* Set up local azimuthal equidistant projection */
9958 sincosd (90.0 - azimuth, &sin_azimuth, &cos_azimuth);
9959 sincosd (lat, &sinp, &cosp);
9960
9961 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;
9962
9963 if (m == 0) { /* Determine N */
9964 delta_azimuth = 2.0 * M_PI / GMT_ELLIPSE_APPROX; /* Initial guess of angular spacing */
9965 /* Compute distance between first two points and compare to map_line_step to determine angular spacing */
9966 gmtmap_ellipse_point (GMT, lon, lat, center, sinp, cosp, major, minor, cos_azimuth, sin_azimuth, 0.0, &ax, &ay);
9967 gmtmap_ellipse_point (GMT, lon, lat, center, sinp, cosp, major, minor, cos_azimuth, sin_azimuth, delta_azimuth, &bx, &by);
9968 gmt_geo_to_xy (GMT, ax, ay, &axx, &ayy);
9969 gmt_geo_to_xy (GMT, bx, by, &bxx, &byy);
9970 L = hypot (axx - bxx, ayy - byy);
9971 N = (uint64_t) irint (GMT_ELLIPSE_APPROX * L / GMT->current.setting.map_line_step);
9972 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Ellipse will be approximated by %d-sided polygon\n", N);
9973 /* Approximate ellipse by a N-sided polygon */
9974 }
9975 else /* Approximate ellipse by a m-sided polygon */
9976 N = m;
9977 delta_azimuth = 2.0 * M_PI / N;
9978 /* Allocate datasetgment of the right size */
9979 S = GMT_Alloc_Segment (GMT->parent, GMT_NO_STRINGS, N+1, 2, NULL, NULL);
9980 px = S->data[GMT_X]; py = S->data[GMT_Y];
9981
9982 for (i = 0; i < N; i++)
9983 gmtmap_ellipse_point (GMT, lon, lat, center, sinp, cosp, major, minor, cos_azimuth, sin_azimuth, i * delta_azimuth, &px[i], &py[i]);
9984
9985 /* Explicitly close the polygon */
9986 px[N] = px[0], py[N] = py[0];
9987 if (doubleAlmostEqual (major_km, minor_km))
9988 snprintf (header, GMT_LEN256, "Circle around %g/%g with diameter %g km approximated by %" PRIu64 " points", lon, lat, major_km, N);
9989 else
9990 snprintf (header, GMT_LEN256, "Ellipse around %g/%g with major/minor axes %g/%g km and major axis azimuth %g approximated by %" PRIu64 " points", lon, lat, major_km, minor_km, azimuth, N);
9991 S->header = strdup (header);
9992 return (S);
9993 }
9994
gmt_segment_BB_outside_map_BB(struct GMT_CTRL * GMT,struct GMT_DATASEGMENT * S)9995 bool gmt_segment_BB_outside_map_BB (struct GMT_CTRL *GMT, struct GMT_DATASEGMENT *S) {
9996 /* Determine if the bounding box for this segment is completely outside the current map w/e/s/n region.
9997 * Note: If an oblique projection is selected then the gmt_wesn_search has updated the GMT->common.R.wesn
9998 * to be the bounding box of that oblique region. */
9999
10000 /* First check latitude or y-coordinates since they are straightforward to check */
10001 if (S->min[GMT_Y] > GMT->common.R.wesn[YHI]) return true; /* BB is above of our max region */
10002 if (S->max[GMT_Y] < GMT->common.R.wesn[YLO]) return true; /* BB is below of our max region */
10003 if (gmt_M_x_is_lon (GMT, GMT_IN)) { /* Must take periodicity of longitudes into account when checking if we are outside */
10004 if ((S->min[GMT_X] > GMT->common.R.wesn[XHI]) && ((S->max[GMT_X] - 360.0) < GMT->common.R.wesn[XLO])) return true; /* BB is to the right of our max region */
10005 if ((S->max[GMT_X] < GMT->common.R.wesn[XLO]) && ((S->min[GMT_X] + 360.0) > GMT->common.R.wesn[XHI])) return true; /* BB is to the left of our max region */
10006 }
10007 else { /* Cartesian x is easy to check */
10008 if (S->min[GMT_X] > GMT->common.R.wesn[XHI]) return true; /* BB is to the right of our max region */
10009 if (S->max[GMT_X] < GMT->common.R.wesn[XLO]) return true; /* BB is to the left of our max region */
10010
10011 }
10012 return false;
10013 }
10014
gmt_circle_to_region(struct GMT_CTRL * GMT,double lon,double lat,double radius,double * wesn)10015 int gmt_circle_to_region (struct GMT_CTRL *GMT, double lon, double lat, double radius, double *wesn) {
10016 /* Given a (lon,lat) point and a radius in spherical degrees, return w/e/s/n for a rectangular geographic region */
10017 int code = 0; /* Will return 1 if w/e extends the full 360, else we return 0 */
10018 gmt_M_unused (GMT);
10019 /* Set w/e to center point */
10020 wesn[XLO] = wesn[XHI] = lon;
10021 wesn[YLO] = wesn[YHI] = lat;
10022 /* First adjust the S and N boundaries */
10023 wesn[YLO] -= radius; /* Approximate south limit in degrees */
10024 if (wesn[YLO] < -90.0) /* Too far south, reset to S pole */
10025 wesn[YLO] = -90.0;
10026 wesn[YHI] += radius; /* Approximate north limit in degrees */
10027 if (wesn[YHI] > 90.0) /* Way north, reset to N pole */
10028 wesn[YHI] = 90.0;
10029 radius /= cosd (lat); /* Approximate e-w half-width in degrees longitude at the latitude of center point */
10030 if (doubleAlmostEqual (wesn[YLO], -90.0) || doubleAlmostEqual (wesn[YHI], 90.0) || radius >= 180.0) { /* Need all longitudes when a pole is inside the radius or the circle is really large */
10031 wesn[XLO] = lon - 180.0;
10032 wesn[XHI] = lon + 180.0;
10033 code = 1;
10034 }
10035 else { /* Determine longitude limits */
10036 wesn[XLO] -= radius; /* Approximate west limit in degrees */
10037 wesn[XHI] += radius; /* Approximate east limit in degrees */
10038 if ((wesn[XHI] - wesn[XLO]) >= 360.0) { /* Global reach after all */
10039 wesn[XLO] = lon - 180.0;
10040 wesn[XHI] = lon + 180.0;
10041 code = 1;
10042 }
10043 if (wesn[XHI] > 360.0) wesn[XLO] -= 360.0, wesn[XHI] -= 360.0;
10044 if (wesn[XHI] < 0.0) wesn[XLO] += 360.0, wesn[XHI] += 360.0;
10045 }
10046 return (code);
10047 }
10048