1 /*--------------------------------------------------------------------
2 *
3 * Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4 * See LICENSE.TXT file for copying and redistribution conditions.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; version 3 or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * Contact info: www.generic-mapping-tools.org
16 *--------------------------------------------------------------------*/
17 /*
18 *
19 * G M T _ G R D I O . C R O U T I N E S
20 *
21 * Generic routines that take care of all low-level gridfile input/output.
22 *
23 * Author: Paul Wessel
24 * Date: 1-JAN-2010
25 * Version: 5
26 * 64-bit Compliant: Yes
27 *
28 * NOTE: We do not support grids that have more rows or columns that can fit
29 * in a 32-bit integer, hence all linear dimensions (row, col, etc.) are addressed
30 * with 32-bit ints. However, 1-D array indices (e.g., ij = row*n_columns + col) are
31 * addressed with 64-bit integers.
32 *
33 * Public functions (42 [+2]):
34 *
35 * gmt_grd_get_format : Get format id, scale, offset and missing value for grdfile
36 * gmtlib_read_grd_info : Read header from file
37 * gmtlib_read_grd : Read data set from file (must be preceded by gmtlib_read_grd_info)
38 * gmt_update_grd_info : Update header in existing file (must be preceded by gmtlib_read_grd_info)
39 * gmtlib_write_grd_info : Write header to new file
40 * gmtlib_write_grd : Write header and data set to new file
41 * gmt_set_R_from_grd
42 * gmt_grd_coord :
43 * gmtlib_grd_real_interleave :
44 * gmt_grd_mux_demux :
45 * gmtlib_grd_set_units :
46 * gmt_grd_pad_status :
47 * gmt_grd_info_syntax
48 * gmtlib_get_grdtype :
49 * gmtlib_grd_data_size :
50 * gmt_grd_set_ij_inc :
51 * gmt_grd_format_decoder :
52 * gmt_grd_prep_io :
53 * gmt_set_grdinc :
54 * gmt_set_grddim :
55 * gmt_grd_pad_off :
56 * gmt_grd_pad_on :
57 * gmt_grd_pad_zero :
58 * gmt_create_grid :
59 * gmt_duplicate_grid :
60 * gmt_free_grid :
61 * gmt_set_outgrid :
62 * gmt_change_grdreg :
63 * gmt_grd_zminmax :
64 * gmt_grd_minmax :
65 * gmt_grd_detrend :
66 * gmtlib_init_complex :
67 * gmtlib_found_url_for_gdal :
68 * gmt_read_img : Read [subset from] a Sandwell/Smith *.img file
69 * gmt_grd_init : Initialize grd header structure
70 * gmt_grd_shift : Rotates grdfiles in x-direction
71 * gmt_grd_setregion : Determines subset coordinates for grdfiles
72 * gmt_grd_is_global : Determine whether grid is "global", i.e. longitudes are periodic
73 * gmt_adjust_loose_wesn : Ensures region, increments, and n_columns/n_rows are compatible
74 * gmt_decode_grd_h_info : Decodes a -Dstring into header text components
75 * gmt_grd_RI_verify : Test to see if region and incs are compatible
76 * gmt_scale_and_offset_f : Routine that scales and offsets the data in a vector
77 * gmt_grd_flip_vertical : Flips the grid in vertical direction
78 * gmtgrdio_pack_grid : Packs or unpacks a grid by calling gmt_scale_and_offset_f()
79 *
80 * Reading images via GDAL (if enabled):
81 * gmtlib_read_image : Read [subset of] an image via GDAL
82 * gmtlib_read_image_info : Get information for an image via GDAL
83 *
84 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
85
86 #include "gmt_dev.h"
87 #include "gmt_internals.h"
88 #include "gmt_common_byteswap.h"
89
90 struct GRD_PAD { /* Local structure */
91 double wesn[4];
92 unsigned int pad[4];
93 };
94
95 /* Local functions */
96
97 /*! gmt_M_grd_get_size computes grid size including the padding, and doubles it if complex values */
gmtgrdio_grd_get_size(struct GMT_GRID_HEADER * h)98 GMT_LOCAL size_t gmtgrdio_grd_get_size (struct GMT_GRID_HEADER *h) {
99 return ((((h->complex_mode & GMT_GRID_IS_COMPLEX_MASK) > 0) + 1ULL) * h->mx * h->my);
100 }
101
gmtgrdio_grd_layout(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,unsigned int complex_mode,unsigned int direction)102 GMT_LOCAL int gmtgrdio_grd_layout (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, unsigned int complex_mode, unsigned int direction) {
103 /* Checks or sets the array arrangement for a complex array */
104 size_t needed_size; /* Space required to hold both components of a complex grid */
105 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
106 if ((complex_mode & GMT_GRID_IS_COMPLEX_MASK) == 0) return GMT_OK; /* Regular, non-complex grid, nothing special to do */
107
108 needed_size = 2ULL * ((size_t)header->mx) * ((size_t)header->my); /* For the complex array */
109 if (header->size < needed_size) {
110 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Complex grid not large enough to hold both components!.\n");
111 return GMT_DIM_TOO_SMALL;
112 }
113 if (direction == GMT_IN) { /* About to read in a complex component; another one might have been read in earlier */
114 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
115 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Demultiplexing complex grid before reading can take place.\n");
116 gmt_grd_mux_demux (GMT, header, grid, GMT_GRID_IS_SERIAL);
117 }
118 if ((header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) == GMT_GRID_IS_COMPLEX_MASK) { /* Already have both component; this will overwrite one of them */
119 unsigned int type = (complex_mode & GMT_GRID_IS_COMPLEX_REAL) ? 0 : 1;
120 char *kind[2] = {"read", "imaginary"};
121 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Overwriting previously stored %s component in complex grid.\n", kind[type]);
122 }
123 header->complex_mode |= complex_mode; /* Update the grids complex mode */
124 }
125 else { /* About to write out a complex component */
126 if ((header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) == 0) { /* Not a complex grid */
127 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Asking to write out complex components from a non-complex grid.\n");
128 return GMT_WRONG_MATRIX_SHAPE;
129 }
130 if ((header->complex_mode & GMT_GRID_IS_COMPLEX_REAL) && (header->complex_mode & GMT_GRID_IS_COMPLEX_REAL) == 0) {
131 /* Programming error: Requesting to write real components when there are none */
132 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Complex grid has no real components that can be written to file.\n");
133 return GMT_WRONG_MATRIX_SHAPE;
134 }
135 else if ((header->complex_mode & GMT_GRID_IS_COMPLEX_IMAG) && (header->complex_mode & GMT_GRID_IS_COMPLEX_IMAG) == 0) {
136 /* Programming error: Requesting to write imag components when there are none */
137 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Complex grid has no imaginary components that can be written to file.\n");
138 return GMT_WRONG_MATRIX_SHAPE;
139 }
140 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) { /* Must first demultiplex the grid */
141 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Demultiplexing complex grid before writing can take place.\n");
142 gmt_grd_mux_demux (GMT, header, grid, GMT_GRID_IS_SERIAL);
143 }
144 }
145 /* header->arrangement might now have been changed accordingly */
146 return GMT_OK;
147 }
148
gmtgrdio_grd_parse_xy_units(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,char * file,unsigned int direction)149 GMT_LOCAL void gmtgrdio_grd_parse_xy_units (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, char *file, unsigned int direction) {
150 /* Decode the optional +u|U<unit> and determine scales */
151 enum gmt_enum_units u_number;
152 unsigned int mode = 0;
153 char *c = NULL, *name = NULL, *f = NULL;
154 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
155
156 if (gmt_M_is_geographic (GMT, direction)) return; /* Does not apply to geographic data */
157 name = (file) ? file : HH->name;
158 if ((f = gmt_strrstr (name, ".grd")) || (f = gmt_strrstr (file, ".nc")))
159 c = gmtlib_last_valid_file_modifier (GMT->parent, f, "Uu");
160 else
161 c = gmtlib_last_valid_file_modifier (GMT->parent, name, "Uu");
162 if (c == NULL) return; /* Did not find any modifier */
163 mode = (c[1] == 'u') ? 0 : 1;
164 u_number = gmtlib_get_unit_number (GMT, c[2]); /* Convert char unit to enumeration constant for this unit */
165 if (u_number == GMT_IS_NOUNIT) {
166 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Grid file x/y unit specification %s was unrecognized (part of file name?) and is ignored.\n", c);
167 return;
168 }
169 /* Got a valid unit */
170 HH->xy_unit_to_meter[direction] = GMT->current.proj.m_per_unit[u_number]; /* Converts unit to meters */
171 if (mode) HH->xy_unit_to_meter[direction] = 1.0 / HH->xy_unit_to_meter[direction]; /* Wanted the inverse */
172 HH->xy_unit[direction] = u_number; /* Unit ID */
173 HH->xy_adjust[direction] |= 1; /* Says we have successfully parsed and readied the x/y scaling */
174 HH->xy_mode[direction] = mode;
175 c[0] = '\0'; /* Chop off the unit specification from the file name */
176 }
177
gmtgrdio_grd_xy_scale(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,unsigned int direction)178 GMT_LOCAL void gmtgrdio_grd_xy_scale (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, unsigned int direction) {
179 unsigned int k;
180 /* Apply the scaling of wesn,inc as given by the header's xy_* settings.
181 * After reading a grid it will have wesn/inc in meters.
182 * Before writing a grid, it may have units changed back to original units
183 * or scaled to another set of units */
184 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
185
186 if (direction == GMT_IN) {
187 if (HH->xy_adjust[direction] == 0) return; /* Nothing to do */
188 if (HH->xy_adjust[GMT_IN] & 2) return; /* Already scaled them */
189 for (k = 0; k < 4; k++) h->wesn[k] *= HH->xy_unit_to_meter[GMT_IN];
190 for (k = 0; k < 2; k++) h->inc[k] *= HH->xy_unit_to_meter[GMT_IN];
191 HH->xy_adjust[GMT_IN] = 2; /* Now the grid is ready for use and in meters */
192 if (HH->xy_mode[direction])
193 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Input grid file x/y unit was converted from meters to %s after reading.\n", GMT->current.proj.unit_name[HH->xy_unit[GMT_IN]]);
194 else
195 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Input grid file x/y unit was converted from %s to meters after reading.\n", GMT->current.proj.unit_name[HH->xy_unit[GMT_IN]]);
196 }
197 else if (direction == GMT_OUT) { /* grid x/y are assumed to be in meters */
198 if (HH->xy_adjust[GMT_OUT] & 1) { /* Was given a new unit for output */
199 for (k = 0; k < 4; k++) h->wesn[k] /= HH->xy_unit_to_meter[GMT_OUT];
200 for (k = 0; k < 2; k++) h->inc[k] /= HH->xy_unit_to_meter[GMT_OUT];
201 HH->xy_adjust[GMT_OUT] = 2; /* Now we are ready for writing */
202 if (HH->xy_mode[GMT_OUT])
203 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output grid file x/y unit was converted from %s to meters before writing.\n", GMT->current.proj.unit_name[HH->xy_unit[GMT_OUT]]);
204 else
205 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output grid file x/y unit was converted from meters to %s before writing.\n", GMT->current.proj.unit_name[HH->xy_unit[GMT_OUT]]);
206 }
207 else if (HH->xy_adjust[GMT_IN] & 2) { /* Just undo old scaling */
208 for (k = 0; k < 4; k++) h->wesn[k] /= HH->xy_unit_to_meter[GMT_IN];
209 for (k = 0; k < 2; k++) h->inc[k] /= HH->xy_unit_to_meter[GMT_IN];
210 HH->xy_adjust[GMT_IN] -= 2; /* Now it is back to where we started */
211 if (HH->xy_mode[GMT_OUT])
212 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output grid file x/y unit was reverted back to %s from meters before writing.\n", GMT->current.proj.unit_name[HH->xy_unit[GMT_IN]]);
213 else
214 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Output grid file x/y unit was reverted back from meters to %s before writing.\n", GMT->current.proj.unit_name[HH->xy_unit[GMT_IN]]);
215 }
216 }
217 }
218
219 /* Routines to see if a particular grd file format is specified as part of filename. */
220
gmtgrdio_expand_filename(struct GMT_CTRL * GMT,const char * file,char * fname)221 GMT_LOCAL void gmtgrdio_expand_filename (struct GMT_CTRL *GMT, const char *file, char *fname) {
222 bool found;
223 unsigned int i;
224 size_t f_length, length;
225
226 if (GMT->current.setting.io_gridfile_shorthand) { /* Look for matches */
227 f_length = strlen (file);
228 for (i = 0, found = false; !found && i < GMT->session.n_shorthands; ++i) {
229 length = strlen (GMT->session.shorthand[i].suffix);
230 found = (length > f_length) ? false : !strncmp (&file[f_length - length], GMT->session.shorthand[i].suffix, length);
231 }
232 if (found) { /* file ended in a recognized shorthand extension */
233 --i;
234 sprintf (fname, "%s=%s", file, GMT->session.shorthand[i].format);
235 }
236 else
237 strcpy (fname, file);
238 }
239 else /* Simply copy the full name */
240 strcpy (fname, file);
241 }
242
243 enum Grid_packing_mode {
244 k_grd_pack = 0, /* scale and offset before writing to disk */
245 k_grd_unpack /* remove scale and offset after reading packed data */
246 };
247
gmtgrdio_pack_grid(struct GMT_CTRL * Ctrl,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,unsigned pack_mode)248 GMT_LOCAL void gmtgrdio_pack_grid (struct GMT_CTRL *Ctrl, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, unsigned pack_mode) {
249 size_t n_representations = 0; /* number of distinct values >= 0 that a signed integral type can represent */
250 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
251
252 if (pack_mode == k_grd_pack && (HH->z_scale_autoadjust || HH->z_offset_autoadjust)) {
253 switch (Ctrl->session.grdformat[header->type][1]) {
254 case 'b':
255 n_representations = 128; /* exp2 (8 * sizeof (int8_t)) / 2 */
256 break;
257 case 's':
258 n_representations = 32768; /* exp2 (8 * sizeof (int16_t)) / 2 */
259 break;
260 case 'i':
261 /* A single precision float's significand has a precision of 24 bits.
262 * In order to avoid round-off errors, we must not use all 2^32
263 * n_representations of an int32_t. */
264 n_representations = 0x1000000; /* exp2 (24) */
265 break;
266 /* default: do not auto-scale floating point */
267 }
268 }
269
270 if (n_representations != 0) {
271 /* Calculate auto-scale and offset */
272 gmt_grd_zminmax (Ctrl, header, grid); /* Calculate z_min/z_max */
273 if (HH->z_offset_autoadjust) {
274 /* shift to center values around 0 but shift only by integral value */
275 double z_range = header->z_max - header->z_min;
276 if (isfinite (z_range))
277 header->z_add_offset = rint(z_range / 2.0 + header->z_min);
278 }
279 if (HH->z_scale_autoadjust) {
280 /* scale z-range to use all n_representations */
281 double z_max = header->z_max - header->z_add_offset;
282 double z_min = fabs(header->z_min - header->z_add_offset);
283 double z_0_n_range = MAX (z_max, z_min); /* use [0,n] range because of signed int */
284 --n_representations; /* subtract 1 for NaN value */
285 if (isnormal (z_0_n_range))
286 header->z_scale_factor = z_0_n_range / n_representations;
287 }
288 }
289
290 /* Do actual packing/unpacking: */
291 switch (pack_mode) {
292 case k_grd_unpack:
293 gmt_scale_and_offset_f (Ctrl, grid, header->size, header->z_scale_factor, header->z_add_offset);
294 /* Adjust z-range in header: */
295 header->z_min = header->z_min * header->z_scale_factor + header->z_add_offset;
296 header->z_max = header->z_max * header->z_scale_factor + header->z_add_offset;
297 if (header->z_scale_factor < 0.0) gmt_M_double_swap (header->z_min, header->z_max);
298 break;
299 case k_grd_pack:
300 gmt_scale_and_offset_f (Ctrl, grid, header->size, 1.0/header->z_scale_factor, -header->z_add_offset/header->z_scale_factor);
301 break;
302 default:
303 assert (false); /* gmtgrdio_pack_grid() called with illegal pack_mode */
304 }
305 }
306
gmtgrdio_parse_grd_format_scale_old(struct GMT_CTRL * Ctrl,struct GMT_GRID_HEADER * header,char * format)307 GMT_LOCAL int gmtgrdio_parse_grd_format_scale_old (struct GMT_CTRL *Ctrl, struct GMT_GRID_HEADER *header, char *format) {
308 /* parses format string after =-suffix: ff/scale/offset/invalid
309 * ff: can be one of [abcegnrs][bsifd]
310 * scale: can be any non-zero normalized number or 'a' for scale and
311 * offset auto-adjust, defaults to 1.0 if omitted
312 * offset: can be any finite number or 'a' for offset auto-adjust, defaults to 0 if omitted
313 * invalid: can be any finite number, defaults to NaN if omitted
314 * scale and offset may be left empty (e.g., ns//a will auto-adjust the offset only)
315 */
316
317 char type_code[3];
318 char *p;
319 int err; /* gmt_M_err_trap */
320 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
321
322 /* decode grid type */
323 strncpy (type_code, format, 2);
324 type_code[2] = '\0';
325 if (type_code[0] == '/') /* user passed a scale with no id code =/scale[...]. Assume "nf" */
326 header->type = GMT_GRID_IS_NF;
327 else {
328 err = gmt_grd_format_decoder (Ctrl, type_code, &header->type); /* update header type id */
329 if (err != GMT_NOERROR)
330 return err;
331 }
332
333 /* parse scale/offset/invalid if any */
334 p = strchr (format, '/');
335 if (p != NULL && *p) {
336 ++p;
337 /* parse scale */
338 if (*p == 'a')
339 HH->z_scale_autoadjust = HH->z_offset_autoadjust = true;
340 else
341 sscanf (p, "%lf", &header->z_scale_factor);
342 }
343 else
344 return GMT_NOERROR;
345
346 p = strchr (p, '/');
347 if (p != NULL && *p) {
348 ++p;
349 /* parse offset */
350 if (*p != 'a') {
351 HH->z_offset_autoadjust = false;
352 sscanf (p, "%lf", &header->z_add_offset);
353 }
354 else
355 HH->z_offset_autoadjust = true;
356 }
357 else
358 return GMT_NOERROR;
359
360 p = strchr (p, '/');
361 if (p != NULL && *p) {
362 ++p;
363 /* parse invalid value */
364 #ifdef DOUBLE_PRECISION_GRID
365 sscanf (p, "%lf", &header->nan_value);
366 #else
367 sscanf (p, "%f", &header->nan_value);
368 #endif
369
370 /* header->nan_value should be of same type as (float)*grid to avoid
371 * round-off errors. For example, =gd///-3.4028234e+38:gtiff, would fail
372 * otherwise because the GTiff'd NoData values are of type double but the
373 * grid is truncated to float.
374 * Don't allow infitiy: */
375 if (!isfinite (header->nan_value))
376 header->nan_value = (gmt_grdfloat)NAN;
377 }
378
379 return GMT_NOERROR;
380 }
381
gmtgrdio_parse_grd_format_scale_new(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,char * format)382 GMT_LOCAL int gmtgrdio_parse_grd_format_scale_new (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, char *format) {
383 /* parses format string after = suffix: ff[+d<divisor>][+s<scale>][+o<offset>/][+n<invalid>]
384 * ff: can be one of [abcegnrs][bsifd]
385 * scale: can be any non-zero normalized number or 'a' for scale and
386 * offset auto-adjust, defaults to 1.0 if omitted
387 * offset: can be any finite number or 'a' for offset auto-adjust, defaults to 0 if omitted
388 * invalid: can be any finite number, defaults to NaN if omitted
389 * E.g., ns+oa will auto-adjust the offset only.
390 */
391
392 char type_code[3];
393 char p[GMT_BUFSIZ] = {""};
394 unsigned int pos = 0, uerr = 0;
395 int err; /* gmt_M_err_trap */
396 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
397
398 /* decode grid type */
399 strncpy (type_code, format, 2);
400 type_code[2] = '\0';
401 if (type_code[0] == '+') /* User passed a scale, offset, or nan-modifier with no id code, e.g., =+s<scale>[...]. Assume "nf" format */
402 header->type = GMT_GRID_IS_NF;
403 else { /* Match given code with known codes */
404 err = gmt_grd_format_decoder (GMT, type_code, &header->type); /* update header type id */
405 if (err != GMT_NOERROR)
406 return err;
407 }
408
409 while (gmt_getmodopt (GMT, 0, format, "bdnos", &pos, p, &uerr) && uerr == 0) { /* Looking for +b, +d, +n, +o, +s */
410 switch (p[0]) {
411 case 'b': /* bands - not parsed here */
412 break;
413 case 'd': /* parse divisor */
414 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_parse_grd_format_scale_new: Setting divisor as %s\n", &p[1]);
415 header->z_scale_factor = 1.0 / atof (&p[1]); /* Just convert to a scale */
416 HH->z_scale_given = true;
417 break;
418 case 'n': /* Nan value */
419 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_parse_grd_format_scale_new: Using %s to represent missing value (NaN)\n", &p[1]);
420 header->nan_value = (gmt_grdfloat)atof (&p[1]);
421 /* header->nan_value should be of same type as (gmt_grdfloat)*grid to avoid
422 * round-off errors. For example, =gd+n-3.4028234e+38:gtiff, would fail
423 * otherwise because the GTiff'd NoData values are of type double but the
424 * grid is truncated to gmt_grdfloat. Don't allow infinity: */
425 if (!isfinite (header->nan_value))
426 header->nan_value = (gmt_grdfloat)NAN;
427 break;
428 case 'o': /* parse offset */
429 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_parse_grd_format_scale_new: Setting offset as %s\n", &p[1]);
430 if (p[1] != 'a') {
431 HH->z_offset_autoadjust = false;
432 HH->z_offset_given = true;
433 header->z_add_offset = atof (&p[1]);
434 }
435 else
436 HH->z_offset_autoadjust = true;
437 break;
438 case 's': /* parse scale */
439 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_parse_grd_format_scale_new: Setting scale as %s\n", &p[1]);
440 if (p[1] == 'a') /* This sets both scale and offset to auto */
441 HH->z_scale_autoadjust = HH->z_offset_autoadjust = true;
442 else {
443 header->z_scale_factor = atof (&p[1]);
444 HH->z_scale_given = true;
445 }
446 break;
447 default: /* These are caught in gmt_getmodopt so break is just for Coverity */
448 break;
449 }
450 }
451 return (uerr) ? GMT_NOT_A_VALID_MODIFIER : GMT_NOERROR;
452 }
453
gmtgrdio_parse_grd_format_scale(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,char * format)454 GMT_LOCAL int gmtgrdio_parse_grd_format_scale (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, char *format) {
455 int code;
456 if (gmt_found_modifier (GMT, format, "bdnos"))
457 code = gmtgrdio_parse_grd_format_scale_new (GMT, header, format);
458 else
459 code = gmtgrdio_parse_grd_format_scale_old (GMT, header, format);
460 return (code);
461 }
462
gmtgrdio_eq(double this,double that,double inc)463 GMT_LOCAL bool gmtgrdio_eq (double this, double that, double inc) {
464 /* this and that are the same value if less than 10e-4 * inc apart */
465 return ((fabs (this - that) / inc) < GMT_CONV4_LIMIT);
466 }
467
gmtgrdio_padspace(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,double * wesn,bool image,unsigned int * pad,struct GRD_PAD * P)468 GMT_LOCAL int gmtgrdio_padspace (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, double *wesn, bool image, unsigned int *pad, struct GRD_PAD *P) {
469 /* When padding is requested it is usually used to set boundary conditions based on
470 * two extra rows/columns around the domain of interest. BCs like natural or periodic
471 * can then be used to fill in the pad. However, if the domain is taken from a grid
472 * whose full domain exceeds the region of interest we are better off using the extra
473 * data to fill those pad rows/columns. Thus, this function tries to determine if the
474 * input grid has the extra data we need to fill the BC pad with observations
475 * P returns the correct region and pad to use regardless if function returns true or false */
476 bool wrap;
477 unsigned int side, n_sides = 0;
478 double wesn2[4];
479 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
480 gmt_M_unused(GMT);
481
482 /* First copy over original settings to the Pad structure */
483 gmt_M_memset (P, 1, struct GRD_PAD); /* Initialize to zero */
484 gmt_M_memcpy (P->pad, pad, 4, int); /* Duplicate the pad */
485 gmt_M_memcpy (P->wesn, header->wesn, 4, double); /* Copy the header boundaries */
486 if (!wesn) return (false); /* No subset requested */
487 if (wesn[XLO] == wesn[XHI] && wesn[YLO] == wesn[YHI]) return (false); /* Subset not set */
488 if (gmtgrdio_eq (wesn[XLO], header->wesn[XLO], header->inc[GMT_X]) && gmtgrdio_eq (wesn[XHI], header->wesn[XHI], header->inc[GMT_X])
489 && gmtgrdio_eq (wesn[YLO], header->wesn[YLO], header->inc[GMT_Y]) && gmtgrdio_eq (wesn[YHI], header->wesn[YHI], header->inc[GMT_Y]))
490 return (false); /* Subset equals whole area */
491 gmt_M_memcpy (P->wesn, wesn, 4, double); /* Copy the subset boundaries */
492 if (pad[XLO] == 0 && pad[XHI] == 0 && pad[YLO] == 0 && pad[YHI] == 0) return (false); /* No padding requested */
493 if (!GMT->current.io.grid_padding) return (false); /* Not requested */
494
495 /* Determine if data exist for a pad on all four sides. If not we give up */
496 wrap = !image && gmt_grd_is_global (GMT, header); /* If global grid wrap then we cannot be outside */
497 if ((wesn2[XLO] = wesn[XLO] - pad[XLO] * header->inc[GMT_X]) < header->wesn[XLO] && !wrap) /* Cannot extend west/xmin */
498 { n_sides++; wesn2[XLO] = wesn[XLO]; }
499 else /* OK to load left pad with data */
500 P->pad[XLO] = 0;
501 if ((wesn2[XHI] = wesn[XHI] + pad[XHI] * header->inc[GMT_X]) > header->wesn[XHI] && !wrap) /* Cannot extend east/xmax */
502 { n_sides++; wesn2[XHI] = wesn[XHI]; }
503 else /* OK to load right pad with data */
504 P->pad[XHI] = 0;
505 if ((wesn2[YLO] = wesn[YLO] - pad[YLO] * header->inc[GMT_Y]) < header->wesn[YLO]) /* Cannot extend south/ymin */
506 { n_sides++; wesn2[YLO] = wesn[YLO]; }
507 else /* OK to load bottom pad with data */
508 P->pad[YLO] = 0;
509 if ((wesn2[YHI] = wesn[YHI] + pad[YHI] * header->inc[GMT_Y]) > header->wesn[YHI]) /* Cannot extend north/ymax */
510 { n_sides++; wesn2[YHI] = wesn[YHI]; }
511 else /* OK to load top pad with data */
512 P->pad[YHI] = 0;
513 if (n_sides == 4) return (false); /* No can do */
514
515 /* Here we know that there is enough input data to fill some or all of the BC pad with actual data values */
516 /* We have temporarily set padding to zero (since the pad is now part of the region) for those sides we can extend */
517
518 /* Temporarily enlarge the region so it now includes the padding we need */
519 gmt_M_memcpy (P->wesn, wesn2, 4, double);
520
521 /* Set BC */
522 for (side = 0; side < 4; side++) {
523 if (P->pad[side] == 0)
524 HH->BC[side] = GMT_BC_IS_DATA;
525 }
526
527 return (true); /* Return true so the calling function can take appropriate action */
528 }
529
gmt_grd_set_datapadding(struct GMT_CTRL * GMT,bool set)530 void gmt_grd_set_datapadding (struct GMT_CTRL *GMT, bool set) {
531 /* Changes the value of GMT->current.io.grid_padding.
532 * If set = true we turn padding on, else off. */
533 GMT->current.io.grid_padding = set;
534 }
535
gmtgrdio_handle_pole_averaging(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,gmt_grdfloat f_value,int pole)536 GMT_LOCAL void gmtgrdio_handle_pole_averaging (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, gmt_grdfloat f_value, int pole) {
537 uint64_t node;
538 unsigned int col = 0;
539 char *name[3] = {"south", "", "north"};
540 gmt_M_unused(GMT);
541
542 if (pole == -1)
543 node = gmt_M_ijp (header, header->n_rows-1, 0); /* First node at S pole */
544 else
545 node = gmt_M_ijp (header, 0, 0); /* First node at N pole */
546 if (gmt_M_type (GMT, GMT_OUT, GMT_Z) == GMT_IS_AZIMUTH || gmt_M_type (GMT, GMT_OUT, GMT_Z) == GMT_IS_ANGLE) { /* Must average azimuths */
547 uint64_t orig = node;
548 double s, c, sum_s = 0.0, sum_c = 0.0;
549 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Average %d angles at the %s pole\n", header->n_columns, name[pole+1]);
550 for (col = 0; col < header->n_columns; col++, node++) {
551 sincosd ((double)grid[node], &s, &c);
552 sum_s += s; sum_c += c;
553 }
554 f_value = (gmt_grdfloat)atan2d (sum_s, sum_c);
555 node = orig;
556 }
557 for (col = 0; col < header->n_columns; col++, node++) grid[node] = f_value;
558 }
559
gmtgrdio_grd_check_consistency(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid)560 GMT_LOCAL void gmtgrdio_grd_check_consistency (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid) {
561 /* Enforce before writing a grid that periodic grids with repeating columns
562 * agree on the node values in those columns; if different replace with average.
563 * This only affects geographic grids of 360-degree extent with gridline registration.
564 * Also, if geographic grid with gridline registration, if the N or S pole row is present
565 * we ensure that they all have identical values, otherwise replace by mean value */
566 unsigned int row = 0, col = 0;
567 unsigned int we_conflicts = 0, p_conflicts = 0;
568 uint64_t left = 0, right = 0, node = 0;
569
570 if (header->registration == GMT_GRID_PIXEL_REG) return; /* Not gridline registered */
571 if (gmt_M_is_cartesian (GMT, GMT_OUT)) return; /* Not geographic */
572 if (header->wesn[YLO] == -90.0) { /* Check consistency of S pole duplicates */
573 double sum;
574 node = gmt_M_ijp (header, header->n_rows-1, 0); /* First node at S pole */
575 sum = grid[node++];
576 p_conflicts = 0;
577 for (col = 1; col < header->n_columns; col++, node++) {
578 if (grid[node] != grid[node-1]) p_conflicts++;
579 sum += grid[node];
580 }
581 if (p_conflicts) {
582 gmt_grdfloat f_value = (gmt_grdfloat)(sum / header->n_columns);
583 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Detected %u inconsistent values at south pole. Values fixed by setting all to average row value.\n", p_conflicts);
584 gmtgrdio_handle_pole_averaging (GMT, header, grid, f_value, -1);
585 }
586 }
587 if (header->wesn[YHI] == +90.0) { /* Check consistency of N pole duplicates */
588 double sum;
589 node = gmt_M_ijp (header, 0, 0); /* First node at N pole */
590 sum = grid[node++];
591 p_conflicts = 0;
592 for (col = 1; col < header->n_columns; col++, node++) {
593 if (grid[node] != grid[node-1]) p_conflicts++;
594 sum += grid[node];
595 }
596 if (p_conflicts) {
597 gmt_grdfloat f_value = (gmt_grdfloat)(sum / header->n_columns);
598 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Detected %u inconsistent values at north pole. Values fixed by setting all to average row value.\n", p_conflicts);
599 gmtgrdio_handle_pole_averaging (GMT, header, grid, f_value, +1);
600 }
601 }
602 if (!gmt_M_360_range (header->wesn[XLO], header->wesn[XHI])) return; /* Not 360-degree range */
603
604 for (row = 0; row < header->n_rows; row++) {
605 left = gmt_M_ijp (header, row, 0); /* Left node */
606 right = left + header->n_columns - 1; /* Right node */
607 if (grid[right] != grid[left]) {
608 grid[right] = grid[left];
609 we_conflicts++;
610 }
611 }
612 if (we_conflicts)
613 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Detected %u inconsistent values along periodic east boundary of grid. Values fixed by duplicating west boundary.\n", we_conflicts);
614 }
615
gmtgrdio_grd_wipe_pad(struct GMT_CTRL * GMT,struct GMT_GRID * G)616 GMT_LOCAL void gmtgrdio_grd_wipe_pad (struct GMT_CTRL *GMT, struct GMT_GRID *G) {
617 /* Reset padded areas to 0. */
618 unsigned int row;
619 size_t ij0;
620 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
621
622 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
623 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Calling gmtgrdio_grd_wipe_pad on interleaved complex grid! Programming error?\n");
624 return;
625 }
626 if (G->header->pad[YHI]) gmt_M_memset (G->data, G->header->mx * G->header->pad[YHI], gmt_grdfloat); /* Wipe top pad */
627 if (G->header->pad[YLO]) { /* Wipe bottom pad */
628 ij0 = gmt_M_ij (G->header, G->header->my - G->header->pad[YLO], 0); /* Index of start of bottom pad */
629 gmt_M_memset (&(G->data[ij0]), G->header->mx * G->header->pad[YLO], gmt_grdfloat);
630 }
631 if (G->header->pad[XLO] == 0 && G->header->pad[XHI] == 0) return; /* Nothing to do */
632 for (row = G->header->pad[YHI]; row < G->header->my - G->header->pad[YLO]; row++) { /* Wipe left and right pad which is trickier */
633 ij0 = gmt_M_ij (G->header, row, 0); /* Index of this row's left column (1st entry in west pad) */
634 if (G->header->pad[XLO]) gmt_M_memset (&(G->data[ij0]), G->header->pad[XLO], gmt_grdfloat);
635 ij0 += (G->header->mx - G->header->pad[XHI]); /* Start of this rows east pad's 1st column */
636 if (G->header->pad[XHI]) gmt_M_memset (&(G->data[ij0]), G->header->pad[XHI], gmt_grdfloat);
637 }
638 }
639
gmtgrdio_pad_grd_off_sub(struct GMT_GRID * G,gmt_grdfloat * data)640 GMT_LOCAL void gmtgrdio_pad_grd_off_sub (struct GMT_GRID *G, gmt_grdfloat *data) {
641 /* Remove the current grid pad and shuffle all rows to the left */
642 uint64_t ijp, ij0;
643 unsigned int row;
644 for (row = 0; row < G->header->n_rows; row++) {
645 ijp = gmt_M_ijp (G->header, row, 0); /* Index of start of this row's first column in padded grid */
646 ij0 = gmt_M_ij0 (G->header, row, 0); /* Index of start of this row's first column in unpadded grid */
647 gmt_M_memcpy (&(data[ij0]), &(data[ijp]), G->header->n_columns, gmt_grdfloat); /* Only copy the n_columns data values */
648 }
649 }
650
gmtgrdio_pad_grd_on_sub(struct GMT_CTRL * GMT,struct GMT_GRID * G,struct GMT_GRID_HEADER * h_old,gmt_grdfloat * data)651 GMT_LOCAL void gmtgrdio_pad_grd_on_sub (struct GMT_CTRL *GMT, struct GMT_GRID *G, struct GMT_GRID_HEADER *h_old, gmt_grdfloat *data) {
652 /* Use G for dimensions but operate on data array which points to either the real or imaginary section */
653 uint64_t ij_new, ij_old, row, col, start_last_new_row, end_last_old_row;
654
655 /* See if the index of start of last new row exceeds index of last node in old grid */
656 start_last_new_row = gmt_M_get_nm (GMT, G->header->pad[YHI] + G->header->n_rows - 1, G->header->pad[XLO] + G->header->n_columns + G->header->pad[XHI]) + G->header->pad[XLO];
657 end_last_old_row = gmt_M_get_nm (GMT, h_old->pad[YHI] + h_old->n_rows - 1, h_old->pad[XLO] + h_old->n_columns + h_old->pad[XHI]) + h_old->pad[XLO] + h_old->n_columns - 1;
658 if (start_last_new_row > end_last_old_row && (start_last_new_row - end_last_old_row) > G->header->n_columns) { /* May copy whole rows from bottom to top */
659 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_pad_grd_on_sub can copy row-by-row\n");
660 for (row = G->header->n_rows; row > 0; row--) {
661 ij_new = gmt_M_ijp (G->header, row-1, 0); /* Index of this row's first column in new padded grid */
662 ij_old = gmt_M_ijp (h_old, row-1, 0); /* Index of this row's first column in old padded grid */
663 gmt_M_memcpy (&(data[ij_new]), &(data[ij_old]), G->header->n_columns, gmt_grdfloat);
664 }
665 }
666 else { /* Must do it from bottom to top on a per node basis */
667 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_pad_grd_on_sub must copy node-by-node\n");
668 ij_new = gmt_M_ijp (G->header, G->header->n_rows-1, G->header->n_columns-1); /* Index of this row's last column in new padded grid */
669 ij_old = gmt_M_ijp (h_old, h_old->n_rows-1, h_old->n_columns-1); /* Index of this row's last column in old padded grid */
670 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_pad_grd_on_sub: last ij_new = %d, last ij_old = %d\n", (int)ij_new, (int)ij_old);
671
672 if (ij_new > ij_old) { /* Can go back to front */
673 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_pad_grd_on_sub: Must loop from end to front\n");
674 for (row = G->header->n_rows; row > 0; row--) {
675 ij_new = gmt_M_ijp (G->header, row-1, G->header->n_columns-1); /* Index of this row's last column in new padded grid */
676 ij_old = gmt_M_ijp (h_old, row-1, h_old->n_columns-1); /* Index of this row's last column in old padded grid */
677 for (col = 0; col < G->header->n_columns; col++)
678 data[ij_new--] = data[ij_old--];
679 }
680 }
681 else {
682 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "gmtgrdio_pad_grd_on_sub: Must loop from front to end\n");
683 for (row = 0; row < G->header->n_rows; row++) {
684 ij_new = gmt_M_ijp (G->header, row, 0); /* Index of this row's last column in new padded grid */
685 ij_old = gmt_M_ijp (h_old, row, 0); /* Index of this row's last column in old padded grid */
686 for (col = 0; col < G->header->n_columns; col++)
687 data[ij_new++] = data[ij_old++];
688 }
689 }
690 }
691 gmtgrdio_grd_wipe_pad (GMT, G); /* Set pad areas to 0 */
692 }
693
gmtgrdio_pad_grd_zero_sub(struct GMT_GRID * G,gmt_grdfloat * data)694 GMT_LOCAL void gmtgrdio_pad_grd_zero_sub (struct GMT_GRID *G, gmt_grdfloat *data) {
695 unsigned int row, col, nx1;
696 uint64_t ij_f, ij_l;
697
698 if (G->header->pad[YHI]) gmt_M_memset (data, G->header->pad[YHI] * G->header->mx, gmt_grdfloat); /* Zero the top pad */
699 nx1 = G->header->n_columns - 1; /* Last column */
700 gmt_M_row_loop (NULL, G, row) {
701 ij_f = gmt_M_ijp (G->header, row, 0); /* Index of first column this row */
702 ij_l = gmt_M_ijp (G->header, row, nx1); /* Index of last column this row */
703 for (col = 1; col <= G->header->pad[XLO]; col++) data[ij_f-col] = 0.0f; /* Zero the left pad at this row */
704 for (col = 1; col <= G->header->pad[XHI]; col++) data[ij_l+col] = 0.0f; /* Zero the left pad at this row */
705 }
706 if (G->header->pad[YLO]) {
707 int pad = G->header->pad[XLO];
708 ij_f = gmt_M_ijp (G->header, G->header->n_rows, -pad); /* Index of first column of bottom pad */
709 gmt_M_memset (&(data[ij_f]), G->header->pad[YLO] * G->header->mx, gmt_grdfloat); /* Zero the bottom pad */
710 }
711 }
712
gmtgrdio_duplicate_gridheader(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)713 GMT_LOCAL struct GMT_GRID_HEADER *gmtgrdio_duplicate_gridheader (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
714 /* Duplicates a grid header. */
715 struct GMT_GRID_HEADER *hnew = gmt_get_header (GMT);
716 gmt_copy_gridheader (GMT, hnew, h);
717 return (hnew);
718 }
719
720 /*----------------------------------------------------------|
721 * Public functions that are part of the GMT Devel library |
722 *----------------------------------------------------------|
723 */
724
725 #ifdef DEBUG
726 /* Uncomment this to have gmt_grd_dump be called and do something */
727 /* #define GMT_DUMPING */
728 #ifdef GMT_DUMPING
gmt_grd_dump(struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,bool is_complex,char * txt)729 void gmt_grd_dump (struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, bool is_complex, char *txt) {
730 unsigned int row, col;
731 uint64_t k = 0U;
732 fprintf (stderr, "Dump [%s]:\n---------------------------------------------\n", txt);
733 for (row = 0; row < header->my; row++) {
734 if (is_complex)
735 for (col = 0; col < header->mx; col++, k+= 2) fprintf (stderr, "(%g,%g)\t", grid[k], grid[k+1]);
736 else
737 for (col = 0; col < header->mx; col++, k++) fprintf (stderr, "%g\t", grid[k]);
738 fprintf (stderr, "\n");
739 }
740 fprintf (stderr, "---------------------------------------------\n");
741 }
742 #else /* Just a dummy */
gmt_grd_dump(struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,bool is_complex,char * txt)743 void gmt_grd_dump (struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, bool is_complex, char *txt) {
744 gmt_M_unused(header); gmt_M_unused(grid); gmt_M_unused(is_complex); gmt_M_unused(txt);
745 /* Nothing */
746 }
747 #endif
748 #endif
749
gmt_copy_gridheader(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * to,struct GMT_GRID_HEADER * from)750 void gmt_copy_gridheader (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *to, struct GMT_GRID_HEADER *from) {
751 /* Destination must exist */
752 struct GMT_GRID_HEADER_HIDDEN *Hfrom = gmt_get_H_hidden (from), *Hto = gmt_get_H_hidden (to);
753 gmt_M_unused(GMT);
754 if (to->ProjRefWKT) gmt_M_str_free (to->ProjRefWKT); /* Since we will duplicate via from */
755 if (to->ProjRefPROJ4) gmt_M_str_free (to->ProjRefPROJ4); /* Since we will duplicate via from */
756 if (Hto->pocket) gmt_M_str_free (Hto->pocket); /* Since we will duplicate via from */
757 gmt_M_memcpy (to, from, 1, struct GMT_GRID_HEADER); /* Copies full contents but also duplicates the hidden address */
758 to->hidden = Hto; /* Restore the original hidden address in to */
759 gmt_M_memcpy (to->hidden, from->hidden, 1, struct GMT_GRID_HEADER_HIDDEN); /* Copies full contents of hidden area */
760 /* Must deal with three pointers individually */
761 if (from->ProjRefWKT) to->ProjRefWKT = strdup (from->ProjRefWKT);
762 if (from->ProjRefPROJ4) to->ProjRefPROJ4 = strdup (from->ProjRefPROJ4);
763 if (Hfrom->pocket) Hto->pocket = strdup (Hfrom->pocket);
764 }
765
766 /*! gmt_grd_is_global returns true for a geographic grid with exactly 360-degree range (with or without repeating column) */
gmt_grd_is_global(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)767 bool gmt_grd_is_global (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
768 bool global;
769 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
770 gmt_M_unused(GMT);
771 global = (HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_NOREPEAT || HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT);
772 return (global);
773 }
774
775 /*! gmt_grd_is_polar returns true for a geographic grid with exactly 180-degree range in latitude (with or without repeating column) */
gmt_grd_is_polar(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)776 bool gmt_grd_is_polar (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
777 if (!gmt_M_y_is_lat (GMT, GMT_IN))
778 return false;
779 if (gmt_M_180_range (h->wesn[YHI], h->wesn[YLO]))
780 return true;
781 if (fabs (h->n_rows * h->inc[GMT_Y] - 180.0) < GMT_CONV4_LIMIT)
782 return true;
783 return false;
784 }
785
gmt_set_R_from_grd(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)786 void gmt_set_R_from_grd (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
787 /* When no -R was given we will inherit the region from the grid. However,
788 * many grids are hobbled by not clearly specifying they are truly global grids.
789 * What frequently happens is that gridnode-registered grids omit the repeating
790 * column in the east, leading to regions such as -R0/359/-90/90 for a 1-degree grid.
791 * Since these are clearly global we do now want to pass 0/359 to the projection
792 * machinery but 0/360. Hence we test if the grid is truly global and make this decision. */
793 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
794
795 gmt_M_memcpy (GMT->common.R.wesn, header->wesn, 4, double); /* Initially we set -R as is from grid header */
796 if (HH->grdtype != GMT_GRID_GEOGRAPHIC_EXACT360_NOREPEAT) return; /* Nothing to do */
797 if (!gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]) && fabs (header->n_columns * header->inc[GMT_X] - 360.0) < GMT_CONV4_LIMIT) {
798 /* The w/e need to state the complete 360 range: Let east = 360 + west */
799 GMT->common.R.wesn[XHI] = GMT->common.R.wesn[XLO] + 360.0;
800 }
801 if (!gmt_M_180_range (GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]) && fabs (header->n_rows * header->inc[GMT_Y] - 180.0) < GMT_CONV4_LIMIT) {
802 /* The s/n need to state the complete 180 range */
803 GMT->common.R.wesn[YLO] = -90.0;
804 GMT->common.R.wesn[YHI] = +90.0;
805 }
806 }
807
gmtlib_grd_get_units(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)808 void gmtlib_grd_get_units (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
809 /* Set input data types for columns 0, 1 and 2 based on unit strings for
810 grid coordinates x, y and z.
811 When "Time": transform the data scale and offset to match the current time system.
812 */
813 unsigned int i;
814 char string[3][GMT_LEN256], *units = NULL;
815 double scale = 1.0, offset = 0.0;
816 struct GMT_TIME_SYSTEM time_system;
817
818 /* Copy unit strings */
819 strncpy (string[0], header->x_units, GMT_GRID_UNIT_LEN80);
820 strncpy (string[1], header->y_units, GMT_GRID_UNIT_LEN80);
821 strncpy (string[2], header->z_units, GMT_GRID_UNIT_LEN80);
822
823 /* Parse the unit strings one by one */
824 for (i = 0; i < 3; i++) {
825 /* Skip parsing when input data type is already set */
826 if (GMT->current.io.col_type[GMT_IN][i] & GMT_IS_GEO) continue;
827 if (GMT->current.io.col_type[GMT_IN][i] & GMT_IS_RATIME) {
828 GMT->current.proj.xyz_projection[i] = GMT_TIME;
829 continue;
830 }
831
832 /* Change name of variable and unit to lower case for comparison */
833 gmt_str_tolower (string[i]);
834
835 /* PW: This test was <= 360 but if you have grids from 340-375 and it says longitude then we should not make it Cartesian */
836 if ((!strncmp (string[i], "longitude", 9U) || strstr (string[i], "degrees_e")) && (header->wesn[XLO] > -360.0 && header->wesn[XHI] < 720.0)) {
837 /* Input data type is longitude */
838 gmt_set_column_type (GMT, GMT_IN, i, GMT_IS_LON);
839 }
840 else if ((!strncmp (string[i], "latitude", 8U) || strstr (string[i], "degrees_n")) && (header->wesn[YLO] >= -90.0 && header->wesn[YHI] <= 90.0)) {
841 /* Input data type is latitude */
842 gmt_set_column_type (GMT, GMT_IN, i, GMT_IS_LAT);
843 }
844 else if (!strcmp (string[i], "time") || !strncmp (string[i], "time [", 6U)) {
845 /* Input data type is time */
846 gmt_set_column_type (GMT, GMT_IN, i, GMT_IS_RELTIME);
847 GMT->current.proj.xyz_projection[i] = GMT_TIME;
848
849 /* Determine coordinates epoch and units (default is internal system) */
850 gmt_M_memcpy (&time_system, &GMT->current.setting.time_system, 1, struct GMT_TIME_SYSTEM);
851 units = strchr (string[i], '[');
852 if (!units || gmt_get_time_system (GMT, ++units, &time_system) || gmt_init_time_system_structure (GMT, &time_system))
853 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Time units [%s] in grid not recognized, defaulting to %s settings.\n", units, GMT_SETTINGS_FILE);
854
855 /* Determine scale between grid and internal time system, as well as the offset (in internal units) */
856 scale = time_system.scale * GMT->current.setting.time_system.i_scale;
857 offset = (time_system.rata_die - GMT->current.setting.time_system.rata_die) + (time_system.epoch_t0 - GMT->current.setting.time_system.epoch_t0);
858 offset *= GMT_DAY2SEC_F * GMT->current.setting.time_system.i_scale;
859
860 /* Scale data scale and extremes based on scale and offset */
861 if (i == 0) {
862 header->wesn[XLO] = header->wesn[XLO] * scale + offset;
863 header->wesn[XHI] = header->wesn[XHI] * scale + offset;
864 header->inc[GMT_X] *= scale;
865 }
866 else if (i == 1) {
867 header->wesn[YLO] = header->wesn[YLO] * scale + offset;
868 header->wesn[YHI] = header->wesn[YHI] * scale + offset;
869 header->inc[GMT_Y] *= scale;
870 }
871 else {
872 header->z_add_offset = header->z_add_offset * scale + offset;
873 header->z_scale_factor *= scale;
874 }
875 }
876 }
877 }
878
gmt_grd_coord(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,int dir)879 double * gmt_grd_coord (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, int dir) {
880 /* Allocate, compute, and return the x- or y-coordinates for a grid */
881 unsigned int k;
882 double *coord = NULL;
883 assert (dir == GMT_X || dir == GMT_Y);
884 if (dir == GMT_X) {
885 coord = gmt_M_memory (GMT, NULL, header->n_columns, double);
886 for (k = 0; k < header->n_columns; k++) coord[k] = gmt_M_grd_col_to_x (GMT, k, header);
887 }
888 else if (dir == GMT_Y) {
889 coord = gmt_M_memory (GMT, NULL, header->n_rows, double);
890 for (k = 0; k < header->n_rows; k++) coord[k] = gmt_M_grd_row_to_y (GMT, k, header);
891 }
892
893 return (coord);
894 }
895
gmtlib_grd_real_interleave(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,gmt_grdfloat * data)896 void gmtlib_grd_real_interleave (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *data) {
897 /* Sub-function since this step is also needed in the specific case when a module
898 * calls GMT_Read_Data with GMT_GRID_IS_COMPLEX_REAL but input comes via an
899 * external interface (MATLAB, Python) and thus has not been multiplexed yet.
900 * We assume the data array is already large enough to hold the double-sized grid.
901 */
902 uint64_t row, col, col_1, col_2, left_node_1, left_node_2;
903 gmt_M_unused(GMT);
904 /* Here we have a grid with RRRRRR..._________ and want R_R_R_R_... */
905 for (row = header->my; row > 0; row--) { /* Going from last to first row */
906 left_node_1 = gmt_M_ij (header, row-1, 0); /* Start of row in RRRRR layout */
907 left_node_2 = 2 * left_node_1; /* Start of same row in R_R_R_ layout */
908 for (col = header->mx, col_1 = col - 1, col_2 = 2*col - 1; col > 0; col--, col_1--) { /* Go from right to left */
909 data[left_node_2+col_2] = 0.0f; col_2--; /* Set the Imag component to zero */
910 data[left_node_2+col_2] = data[left_node_1+col_1]; col_2--;
911 }
912 }
913 }
914
gmt_grd_mux_demux(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,gmt_grdfloat * data,unsigned int desired_mode)915 void gmt_grd_mux_demux (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *data, unsigned int desired_mode) {
916 /* Multiplex and demultiplex complex grids.
917 * Complex grids are read/written by dealing with just one component: real or imag.
918 * Thus, at read|write the developer must specify which component (GMT_CMPLX_REAL|IMAG).
919 * For fast disk i/o we read complex data in serial. I.e., if we ask for GMT_CMPLX_REAL
920 * then the array will contain RRRRR....________, where ______ is unused space for the
921 * imaginary components. Likewise, if we requested GMT_CMPLX_IMAG then the array will
922 * be returned as _______...IIIIIII....
923 * Operations like FFTs typically required the data to be interleaved, i.e., in the
924 * form RIRIRIRI.... Then, when the FFT work is done and we wish to write out the
925 * result we will need to demultiplex the array back to its serial RRRRR....IIIII
926 * format before writing takes place.
927 * gmt_grd_mux_demux performs either multiplex or demultiplex, depending on desired_mode.
928 * If grid is not complex then we just return doing nothing.
929 * Note: At this point the grid is mx * my and we visit all the nodes, including the pads.
930 * hence we use header->mx/my and gmt_M_ij below.
931 */
932 uint64_t row, col, col_1, col_2, left_node_1, left_node_2, offset, ij, ij2;
933 gmt_grdfloat *array = NULL;
934 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
935
936 if (! (desired_mode == GMT_GRID_IS_INTERLEAVED || desired_mode == GMT_GRID_IS_SERIAL)) {
937 GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmt_grd_mux_demux called with inappropriate mode - skipped.\n");
938 return;
939 }
940 if ((header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) == 0) return; /* Nuthin' to do */
941 if (HH->arrangement == desired_mode) return; /* Already has the right layout */
942
943 /* In most cases we will actually have RRRRR...______ or _____...IIIII..
944 * which means half the array is empty and it is easy to shuffle. However,
945 * in the case with actual RRRR...IIII there is no simple way to do this inplace;
946 * see http://stackoverflow.com/questions/1777901/array-interleaving-problem */
947
948 if (desired_mode == GMT_GRID_IS_INTERLEAVED) { /* Must multiplex the grid */
949 if ((header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) == GMT_GRID_IS_COMPLEX_MASK) {
950 /* Transform from RRRRR...IIIII to RIRIRIRIRI... */
951 /* Implement properly later; for now waste memory by duplicating [memory is cheap and plentiful] */
952 /* One advantage is that the padding is all zero by virtue of the allocation */
953 array = gmt_M_memory_aligned (GMT, NULL, header->size, gmt_grdfloat);
954 offset = header->size / 2; /* Position of 1st row in imag portion of RRRR...IIII... */
955 for (row = 0; row < header->my; row++) { /* Going from first to last row */
956 for (col = 0; col < header->mx; col++) {
957 ij = gmt_M_ij (header, row, col); /* Position of an 'R' in the RRRRR portion */
958 ij2 = 2 * ij;
959 array[ij2++] = data[ij];
960 array[ij2] = data[ij+offset];
961 }
962 }
963 gmt_M_memcpy (data, array, header->size, gmt_grdfloat); /* Overwrite serial array with interleaved array */
964 gmt_M_free (GMT, array);
965 }
966 else if (header->complex_mode & GMT_GRID_IS_COMPLEX_REAL) {
967 /* Here we have RRRRRR..._________ and want R_R_R_R_... */
968 gmtlib_grd_real_interleave (GMT, header, data);
969 }
970 else {
971 /* Here we have _____...IIIII and want _I_I_I_I */
972 offset = header->size / 2; /* Position of 1st row in imag portion of ____...IIII... */
973 for (row = 0; row < header->my; row++) { /* Going from first to last row */
974 left_node_1 = gmt_M_ij (header, row, 0); /* Start of row in _____IIII layout not counting ____*/
975 left_node_2 = 2 * left_node_1; /* Start of same row in _I_I_I... layout */
976 left_node_1 += offset; /* Move past length of all ____... */
977 for (col_1 = 0, col_2 = 1; col_1 < header->mx; col_1++, col_2 += 2) {
978 data[left_node_2+col_2] = data[left_node_1+col_1];
979 data[left_node_1+col_1] = 0.0f; /* Set the Real component to zero */
980 }
981 }
982 }
983 }
984 else if (desired_mode == GMT_GRID_IS_SERIAL) { /* Must demultiplex the grid */
985 if ((header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) == GMT_GRID_IS_COMPLEX_MASK) {
986 /* Transform from RIRIRIRIRI... to RRRRR...IIIII */
987 /* Implement properly later; for now waste memory by duplicating [memory is cheap and plentiful] */
988 /* One advantage is that the padding is all zero by virtue of the allocation */
989 array = gmt_M_memory_aligned (GMT, NULL, header->size, gmt_grdfloat);
990 offset = header->size / 2; /* Position of 1st row in imag portion of RRRR...IIII... */
991 for (row = 0; row < header->my; row++) { /* Going from first to last row */
992 for (col = 0; col < header->mx; col++) {
993 ij = gmt_M_ij (header, row, col); /* Position of an 'R' in the RRRRR portion */
994 ij2 = 2 * ij;
995 array[ij] = data[ij2++];
996 array[ij+offset] = data[ij2];
997 }
998 }
999 gmt_M_memcpy (data, array, header->size, gmt_grdfloat); /* Overwrite interleaved array with serial array */
1000 gmt_M_free (GMT, array);
1001 }
1002 else if (header->complex_mode & GMT_GRID_IS_COMPLEX_REAL) {
1003 /* Here we have R_R_R_R_... and want RRRRRR..._______ */
1004 for (row = 0; row < header->my; row++) { /* Doing from first to last row */
1005 left_node_1 = gmt_M_ij (header, row, 0); /* Start of row in RRRRR... */
1006 left_node_2 = 2 * left_node_1; /* Start of same row in R_R_R_R... layout */
1007 for (col_1 = col_2 = 0; col_1 < header->mx; col_1++, col_2 += 2) {
1008 data[left_node_1+col_1] = data[left_node_2+col_2];
1009 }
1010 }
1011 offset = header->size / 2; /* Position of 1st _ in RRRR...____ */
1012 gmt_M_memset (&data[offset], offset, gmt_grdfloat); /* Wipe _____ portion clean */
1013 }
1014 else { /* Here we have _I_I_I_I and want _____...IIIII */
1015 offset = header->size / 2; /* Position of 1st row in imag portion of ____...IIII... */
1016 for (row = header->my; row > 0; row--) { /* Going from last to first row */
1017 left_node_1 = gmt_M_ij (header, row, 0); /* Start of row in _____IIII layout not counting ____*/
1018 left_node_2 = 2 * left_node_1; /* Start of same row in _I_I_I... layout */
1019 left_node_1 += offset; /* Move past length of all ____... */
1020 for (col = header->mx, col_1 = col - 1, col_2 = 2*col - 1; col > 0; col--, col_1--, col_2 -= 2) { /* Go from right to left */
1021 data[left_node_1+col_1] = data[left_node_2+col_2];
1022 }
1023 }
1024 gmt_M_memset (data, offset, gmt_grdfloat); /* Wipe leading _____ portion clean */
1025 }
1026 }
1027 HH->arrangement = desired_mode;
1028 }
1029
gmt_grd_get_format(struct GMT_CTRL * GMT,char * file,struct GMT_GRID_HEADER * header,bool magic)1030 int gmt_grd_get_format (struct GMT_CTRL *GMT, char *file, struct GMT_GRID_HEADER *header, bool magic) {
1031 /* This functions does a couple of things:
1032 * 1. It tries to determine what kind of grid file this is. If a file is openeed for
1033 * reading we see if (a) a particular format has been specified with
1034 * the =<code> suffix, or (b) we are able to guess the format based on known
1035 * characteristics of various formats, or (c) assume the default grid format.
1036 * If a file is opened for writing, only option (a) and (c) apply.
1037 * If we cannot obtain the format we return an error.
1038 * 2. We strip the suffix off. The relevant info is stored in the header struct.
1039 * 3. In case of netCDF grids, the optional ?<varname> is stripped off as well.
1040 * The info is stored in header->varname.
1041 * 4. If a file is open for reading, we set header->name to the full path of the file
1042 * by searching in current dir and the various GMT_*DIR paths.
1043 */
1044
1045 size_t i = 0, j;
1046 int val;
1047 unsigned int direction = (magic) ? GMT_IN : GMT_OUT, pos = 0;
1048 char tmp[GMT_LEN512] = {""}; /* But it's copied at most 256 chars into header->name so 256 should do */
1049 char *c = NULL, *f = NULL, *ext = NULL; /* Various char pointers used to deal with file modifiers */
1050 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1051
1052 if (file[0] == '@') pos = 1; /* At this point we will already have downloaded any remote file so skip the @ */
1053 gmtgrdio_grd_parse_xy_units (GMT, header, &file[pos], direction); /* Parse and strip xy scaling via +u<unit> modifier */
1054
1055 gmtgrdio_expand_filename (GMT, &file[pos], HH->name); /* May append a suffix to header->name */
1056
1057 /* Must reset scale and invalid value because sometimes headers from input grids are
1058 * 'recycled' and used for output grids that may have a different type and z-range: */
1059 header->z_scale_factor = 1.0;
1060 header->z_add_offset = 0.0;
1061 header->nan_value = (gmt_grdfloat)NAN;
1062
1063 i = strcspn (HH->name, "="); /* get number of chars until first '=' or '\0' */
1064
1065 if ((f = gmt_strrstr (HH->name, ".grd")) || (f = gmt_strrstr (HH->name, ".nc")))
1066 c = gmtlib_last_valid_file_modifier (GMT->parent, f, GMT_GRIDFILE_MODIFIERS);
1067 else
1068 c = gmtlib_last_valid_file_modifier (GMT->parent, HH->name, GMT_GRIDFILE_MODIFIERS);
1069
1070 if (HH->name[i] == '\0' && c) { /* No grid type but gave valid modifiers */
1071 /* parse grid format string: */
1072 if ((val = gmtgrdio_parse_grd_format_scale (GMT, header, c)) != GMT_NOERROR)
1073 return val;
1074 c[0] = '\0'; /* Chop off since we did got the scalings */
1075 }
1076 if (HH->name[i]) { /* Reading or writing when =suffix is present: get format type, scale, offset and missing value */
1077 i++;
1078 /* parse grid format string: */
1079 if (c && (val = gmtgrdio_parse_grd_format_scale (GMT, header, c)) != GMT_NOERROR)
1080 return val;
1081 else if ((val = gmtgrdio_parse_grd_format_scale (GMT, header, &HH->name[i])) != GMT_NOERROR)
1082 return val;
1083 if (header->type == GMT_GRID_IS_GD && HH->name[i+2] && HH->name[i+2] == '?') { /* A SUBDATASET request for GDAL */
1084 char *pch = strstr(&HH->name[i+3], "::");
1085 if (pch) { /* The file name was omitted within the SUBDATASET. Must put it there for GDAL */
1086 strncpy (tmp, &HH->name[i+3], pch - &HH->name[i+3] + 1);
1087 strcat (tmp, "\""); strncat(tmp, HH->name, i-1); strcat(tmp, "\"");
1088 if (strlen(&pch[1]) < (GMT_LEN512-strlen(tmp)-1)) strncat (tmp, &pch[1], GMT_LEN512-1);
1089 strncpy (HH->name, tmp, GMT_LEN256-1);
1090 }
1091 else
1092 memmove (HH->name, &HH->name[i+3], strlen(&HH->name[i+3])+1);
1093 magic = 0; /* We don't want it to try to prepend any path */
1094 } /* if (header->type == GMT_GRID_IS_GD && HH->name[i+2] && HH->name[i+2] == '?') */
1095 else if (header->type == GMT_GRID_IS_GD && HH->name[i+2] && HH->name[i+2] == '+' && HH->name[i+3] == 'b') { /* A Band request for GDAL */
1096 HH->pocket = strdup(&HH->name[i+4]);
1097 HH->name[i-1] = '\0';
1098 }
1099 else if (header->type == GMT_GRID_IS_GD && HH->name[i+2] && strchr(&HH->name[i+2], ':')) {
1100 char *pch;
1101 size_t nc_limit = 2147483647U; /* 2^31 - 1 is the max length of a 1-D array in netCDF */
1102 pch = strchr(&HH->name[i+2], ':');
1103 HH->pocket = strdup(++pch);
1104 HH->name[i-1] = '\0'; /* Done, rip the driver/outtype info from file name */
1105 if (!strncmp (HH->pocket, "GMT", 3U) && header->nm > nc_limit) {
1106 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Your grid contains more than 2^31 - 1 nodes (%" PRIu64 ") and cannot be stored with the deprecated GMT netCDF format via GDAL.\n", header->nm);
1107 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Please choose another grid format such as the default netCDF 4 COARDS-compliant grid format.\n");
1108 return (GMT_GRDIO_BAD_DIM); /* Grid is too large */
1109 }
1110 }
1111 else {
1112 j = (i == 1) ? i : i - 1;
1113 HH->name[j] = 0;
1114 }
1115 sscanf (HH->name, "%[^?]?%s", tmp, HH->varname); /* Strip off variable name */
1116 if (magic) { /* Reading: possibly prepend a path from GMT_[GRID|DATA|IMG]DIR */
1117 if (header->type != GMT_GRID_IS_GD || !gmtlib_found_url_for_gdal(tmp)) /* Do not try path stuff with Web files (accessed via GDAL) */
1118 if (!gmt_getdatapath (GMT, tmp, HH->name, R_OK))
1119 return (GMT_GRDIO_FILE_NOT_FOUND);
1120 }
1121 else /* Writing: store truncated pathname */
1122 strncpy (HH->name, tmp, GMT_LEN256-1);
1123 } /* if (header->name[i]) */
1124 else if (magic) { /* Reading: determine file format automatically based on grid content */
1125 int choice = 0;
1126 sscanf (HH->name, "%[^?]?%s", tmp, HH->varname); /* Strip off variable name */
1127 #ifdef HAVE_GDAL
1128 /* Check if file is an URL */
1129 if (gmtlib_found_url_for_gdal(HH->name)) {
1130 /* Then check for GDAL grid */
1131 if (gmtlib_is_gdal_grid (GMT, header) == GMT_NOERROR)
1132 return (GMT_NOERROR);
1133 }
1134 #endif
1135 if (!gmt_getdatapath (GMT, tmp, HH->name, R_OK))
1136 return (GMT_GRDIO_FILE_NOT_FOUND); /* Possibly prepended a path from GMT_[GRID|DATA|IMG]DIR */
1137 /* First check if we have a netCDF grid. This MUST be first, because ?var needs to be stripped off. */
1138 if ((val = gmtlib_is_nc_grid (GMT, header)) == GMT_NOERROR)
1139 return (GMT_NOERROR);
1140 /* Continue only when file was not a pipe or when gmt_nc_open didn't like the file or when the grid was COARDS-compliant. */
1141 if (val != GMT_GRDIO_NC_NO_PIPE && val != GMT_GRDIO_OPEN_FAILED && val != GMT_GRDIO_NC_NOT_COARDS)
1142 return (val);
1143 /* Then check for native binary GMT grid */
1144 if ((choice = gmtlib_is_native_grid (GMT, header)) == GMT_NOERROR)
1145 return (GMT_NOERROR);
1146 else if (choice == GMT_GRDIO_NONUNIQUE_FORMAT)
1147 return (GMT_GRDIO_NONUNIQUE_FORMAT);
1148 /* Next check for Sun raster grid */
1149 if (gmtlib_is_ras_grid (GMT, header) == GMT_NOERROR)
1150 return (GMT_NOERROR);
1151 /* Then check for Golden Software surfer grid */
1152 if (gmtlib_is_srf_grid (GMT, header) == GMT_NOERROR)
1153 return (GMT_NOERROR);
1154 /* Then check for NGDC GRD98 grid */
1155 if (gmtlib_is_mgg2_grid (GMT, header) == GMT_NOERROR)
1156 return (GMT_NOERROR);
1157 /* Then check for AGC grid */
1158 if (gmtlib_is_agc_grid (GMT, header) == GMT_NOERROR)
1159 return (GMT_NOERROR);
1160 /* Then check for ESRI grid */
1161 if (gmtlib_is_esri_grid (GMT, header) == GMT_NOERROR)
1162 return (GMT_NOERROR);
1163 /* Rule out dumb *.txt and *.lis files before GDAL checks to avoid dumb error message */
1164 ext = gmt_get_ext (file);
1165 if (ext && (!strcmp (ext, "txt") || !strcmp (ext, "lis")))
1166 return (GMT_GRDIO_UNKNOWN_FORMAT);
1167 #ifdef HAVE_GDAL
1168 /* Then check for GDAL grid */
1169 if (gmtlib_is_gdal_grid (GMT, header) == GMT_NOERROR)
1170 return (GMT_NOERROR);
1171 #endif
1172 return (GMT_GRDIO_UNKNOWN_FORMAT); /* No supported format found */
1173 }
1174 else { /* Writing: get format type, scale, offset and missing value from GMT->current.setting.io_gridfile_format */
1175 if (sscanf (HH->name, "%[^?]?%s", tmp, HH->varname) > 1)
1176 strncpy (HH->name, tmp, GMT_LEN256-1); /* Strip off variable name */
1177 /* parse grid format string: */
1178 if ((val = gmtgrdio_parse_grd_format_scale (GMT, header, GMT->current.setting.io_gridfile_format)) != GMT_NOERROR)
1179 return val;
1180 }
1181 if (header->type == GMT_GRID_IS_AF)
1182 header->nan_value = 0.0f; /* NaN value for AGC format */
1183 return (GMT_NOERROR);
1184 }
1185
gmtlib_grd_set_units(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)1186 void gmtlib_grd_set_units (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
1187 /* Set unit strings for grid coordinates x, y and z based on
1188 output data types for columns 0, 1, and 2.
1189 */
1190 unsigned int i;
1191 char *string[3] = {NULL, NULL, NULL}, unit[GMT_GRID_UNIT_LEN80] = {""};
1192 char date[GMT_LEN16] = {""}, clock[GMT_LEN16] = {""};
1193 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1194
1195 /* Copy pointers to unit strings */
1196 string[0] = header->x_units;
1197 string[1] = header->y_units;
1198 string[2] = header->z_units;
1199
1200 /* Use input data type as backup for output data type */
1201 for (i = 0; i < 3; i++)
1202 if (gmt_M_type (GMT, GMT_OUT, i) == GMT_IS_UNKNOWN) GMT->current.io.col_type[GMT_OUT][i] = GMT->current.io.col_type[GMT_IN][i];
1203
1204 /* Catch some anomalies */
1205 if (gmt_M_type (GMT, GMT_OUT, GMT_X) == GMT_IS_LAT && gmt_M_type (GMT, GMT_OUT, GMT_Y) == GMT_IS_LAT) {
1206 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Output type for X-coordinate of grid %s is LAT. Replaced by LON.\n", HH->name);
1207 gmt_set_column_type (GMT, GMT_OUT, GMT_X, GMT_IS_LON);
1208
1209 }
1210 if (gmt_M_type (GMT, GMT_OUT, GMT_Y) == GMT_IS_LON && gmt_M_type (GMT, GMT_OUT, GMT_X) == GMT_IS_LON) {
1211 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Output type for Y-coordinate of grid %s is LON. Replaced by LAT.\n", HH->name);
1212 gmt_set_column_type (GMT, GMT_OUT, GMT_Y, GMT_IS_LAT);
1213 }
1214
1215 /* Set unit strings one by one based on output type */
1216 for (i = 0; i < 3; i++) {
1217 switch (gmt_M_type (GMT, GMT_OUT, i)) {
1218 case GMT_IS_LON:
1219 strcpy (string[i], "longitude [degrees_east]"); break;
1220 case GMT_IS_LAT:
1221 strcpy (string[i], "latitude [degrees_north]"); break;
1222 case GMT_IS_ABSTIME:
1223 case GMT_IS_RELTIME:
1224 case GMT_IS_RATIME:
1225 /* Determine time unit */
1226 switch (GMT->current.setting.time_system.unit) {
1227 case 'y':
1228 strcpy (unit, "years"); break;
1229 case 'o':
1230 strcpy (unit, "months"); break;
1231 case 'd':
1232 strcpy (unit, "days"); break;
1233 case 'h':
1234 strcpy (unit, "hours"); break;
1235 case 'm':
1236 strcpy (unit, "minutes"); break;
1237 default:
1238 strcpy (unit, "seconds"); break;
1239 }
1240 gmt_format_calendar (GMT, date, clock, &GMT->current.io.date_output, &GMT->current.io.clock_output, false, 1, 0.0);
1241 snprintf (string[i], GMT_GRID_UNIT_LEN80, "time [%s since %s %s]", unit, date, clock);
1242 /* Warning for non-double grids */
1243 if (i == 2 && header->n_bands == 1 && GMT->session.grdformat[header->type][1] != 'd')
1244 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Use double precision output grid to avoid loss of significance of time coordinate.\n");
1245 break;
1246 }
1247 }
1248 }
1249
gmtgrdio_pad_status(struct GMT_CTRL * GMT,unsigned int * pad)1250 bool gmtgrdio_pad_status (struct GMT_CTRL *GMT, unsigned int *pad) {
1251 unsigned int side;
1252 gmt_M_unused(GMT);
1253 for (side = 0; side < 4; side++) if (pad[side]) return (true); /* Grid has a pad */
1254 return (false); /* Grid has no pad */
1255 }
1256
gmt_grd_pad_status(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,unsigned int * pad)1257 bool gmt_grd_pad_status (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, unsigned int *pad) {
1258 /* Determines if this grid has padding at all (pad = NULL) OR
1259 * if pad is given, determines if the pads are different.
1260 * Return codes are:
1261 * 1) If pad == NULL:
1262 * false: Grid has zero padding.
1263 * true: Grid has non-zero padding.
1264 * 2) If pad contains the desired pad:
1265 * true: Grid padding matches pad exactly.
1266 * false: Grid padding failed to match pad exactly.
1267 */
1268 unsigned int side;
1269 gmt_M_unused(GMT);
1270
1271 if (pad) { /* Determine if the grid's pad differ from given pad (false) or not (true) */
1272 for (side = 0; side < 4; side++) if (header->pad[side] != pad[side]) return (false); /* Pads differ */
1273 return (true); /* Pads match */
1274 }
1275 else /* We just want to determine if the grid has padding already (true) or not (false) */
1276 return (gmtgrdio_pad_status (GMT, header->pad));
1277 }
1278
gmtlib_get_matrixtype(struct GMT_CTRL * GMT,unsigned int direction,struct GMT_MATRIX * M)1279 int gmtlib_get_matrixtype (struct GMT_CTRL *GMT, unsigned int direction, struct GMT_MATRIX *M) {
1280 /* Determine if input or output matrix is Cartesian or geographic, and if so if longitude range is <360, ==360, or >360 */
1281 char *dir[2] = {"input", "output"};
1282 if (gmt_M_x_is_lon (GMT, direction)) { /* Data set is geographic with x = longitudes */
1283 if (fabs (M->range[XHI] - M->range[XLO] - 360.0) < GMT_CONV4_LIMIT) {
1284 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s matrix, longitudes span exactly 360\n", dir[direction]);
1285 /* If w/e is 360 and gridline reg then we have a repeat entry for 360. For pixel there are never repeat pixels */
1286 return ((M->registration == GMT_GRID_NODE_REG) ? GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT : GMT_GRID_GEOGRAPHIC_EXACT360_NOREPEAT);
1287 }
1288 else if (fabs (M->n_columns * M->inc[GMT_X] - 360.0) < GMT_CONV4_LIMIT) {
1289 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s matrix, longitude cells span exactly 360\n", dir[direction]);
1290 /* If n*xinc = 360 and previous test failed then we do not have a repeat node */
1291 return (GMT_GRID_GEOGRAPHIC_EXACT360_NOREPEAT);
1292 }
1293 else if ((M->range[XHI] - M->range[XLO]) > 360.0) {
1294 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s matrix, longitudes span more than 360\n", dir[direction]);
1295 return (GMT_GRID_GEOGRAPHIC_MORE360);
1296 }
1297 else {
1298 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s matrix, longitudes span less than 360\n", dir[direction]);
1299 return (GMT_GRID_GEOGRAPHIC_LESS360);
1300 }
1301 }
1302 else if (M->range[YLO] >= -90.0 && M->range[YHI] <= 90.0) { /* Here we simply advice the user if matrix looks like geographic but is not set as such */
1303 if (fabs (M->range[XHI] - M->range[XLO] - 360.0) < GMT_CONV4_LIMIT) {
1304 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Cartesian %s matrix, yet x spans exactly 360 and -90 <= y <= 90.\n", dir[direction]);
1305 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, " To make sure the matrix is recognized as geographical and global, use the -fg option\n");
1306 return (GMT_GRID_CARTESIAN);
1307 }
1308 else if (fabs (M->n_columns * M->inc[GMT_X] - 360.0) < GMT_CONV4_LIMIT) {
1309 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Cartesian %s matrix, yet x cells span exactly 360 and -90 <= y <= 90.\n", dir[direction]);
1310 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, " To make sure the matrix is recognized as geographical and global, use the -fg option\n");
1311 return (GMT_GRID_CARTESIAN);
1312 }
1313 }
1314 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Cartesian %s matrix\n", dir[direction]);
1315 return (GMT_GRID_CARTESIAN);
1316 }
1317
gmtlib_get_grdtype(struct GMT_CTRL * GMT,unsigned int direction,struct GMT_GRID_HEADER * h)1318 int gmtlib_get_grdtype (struct GMT_CTRL *GMT, unsigned int direction, struct GMT_GRID_HEADER *h) {
1319 /* Determine if input or output grid is Cartesian or geographic, and if so if longitude range is <360, ==360, or >360 */
1320 char *dir[2] = {"input", "output"};
1321 if (gmt_M_x_is_lon (GMT, direction)) { /* Data set is geographic with x = longitudes */
1322 if (fabs (h->wesn[XHI] - h->wesn[XLO] - 360.0) < GMT_CONV4_LIMIT) {
1323 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s grid, longitudes span exactly 360\n", dir[direction]);
1324 /* If w/e is 360 and gridline reg then we have a repeat entry for 360. For pixel there are never repeat pixels */
1325 return ((h->registration == GMT_GRID_NODE_REG) ? GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT : GMT_GRID_GEOGRAPHIC_EXACT360_NOREPEAT);
1326 }
1327 else if (fabs (h->n_columns * h->inc[GMT_X] - 360.0) < GMT_CONV4_LIMIT) {
1328 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s grid, longitude cells span exactly 360\n", dir[direction]);
1329 /* If n*xinc = 360 and previous test failed then we do not have a repeat node */
1330 return (GMT_GRID_GEOGRAPHIC_EXACT360_NOREPEAT);
1331 }
1332 else if ((h->wesn[XHI] - h->wesn[XLO]) > 360.0) {
1333 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s grid, longitudes span more than 360\n", dir[direction]);
1334 return (GMT_GRID_GEOGRAPHIC_MORE360);
1335 }
1336 else {
1337 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Geographic %s grid, longitudes span less than 360\n", dir[direction]);
1338 return (GMT_GRID_GEOGRAPHIC_LESS360);
1339 }
1340 }
1341 else if (h->wesn[YLO] >= -90.0 && h->wesn[YHI] <= 90.0) { /* Here we simply advice the user if grid looks like geographic but is not set as such */
1342 if (fabs (h->wesn[XHI] - h->wesn[XLO] - 360.0) < GMT_CONV4_LIMIT) {
1343 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Cartesian %s grid, yet x spans exactly 360 and -90 <= y <= 90.\n", dir[direction]);
1344 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, " To make sure the grid is recognized as geographical and global, use the -fg option\n");
1345 return (GMT_GRID_CARTESIAN);
1346 }
1347 else if (fabs (h->n_columns * h->inc[GMT_X] - 360.0) < GMT_CONV4_LIMIT) {
1348 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Cartesian %s grid, yet x cells span exactly 360 and -90 <= y <= 90.\n", dir[direction]);
1349 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, " To make sure the grid is recognized as geographical and global, use the -fg option\n");
1350 return (GMT_GRID_CARTESIAN);
1351 }
1352 }
1353 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Cartesian %s grid\n", dir[direction]);
1354 return (GMT_GRID_CARTESIAN);
1355 }
1356
gmtgrdio_doctor_geo_increments(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)1357 GMT_LOCAL void gmtgrdio_doctor_geo_increments (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
1358 /* Check for sloppy arc min/sec increments due to divisions by 60 or 3600 */
1359 double round_inc[2], scale[2], inc, slop;
1360 unsigned int side, n_fix = 0;
1361 static char *type[2] = {"longitude", "latitude"};
1362
1363 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Call gmtgrdio_doctor_geo_increments on a geographic grid\n");
1364 for (side = GMT_X; side <= GMT_Y; side++) { /* Check both increments */
1365 scale[side] = (header->inc[side] < GMT_MIN2DEG) ? 3600.0 : 60.0; /* Check for clean multiples of minutes or seconds */
1366 inc = header->inc[side] * scale[side];
1367 round_inc[side] = rint (inc);
1368 slop = fabs (inc - round_inc[side]);
1369 if (slop > 0 && slop < GMT_CONV4_LIMIT) n_fix++;
1370 }
1371 if (n_fix == 2) {
1372 for (side = GMT_X; side <= GMT_Y; side++) { /* Check both increments */
1373 inc = header->inc[side];
1374 header->inc[side] = round_inc[side] / scale[side];
1375 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Round-off patrol changed geographic grid increment for %s from %.18g to %.18g\n",
1376 type[side], inc, header->inc[side]);
1377 }
1378 }
1379 }
1380
gmtgrdio_round_off_patrol(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)1381 GMT_LOCAL void gmtgrdio_round_off_patrol (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
1382 /* This function is called after the read info functions return. We use it to make
1383 * sure there are no tiny inconsistencies between grid increments and limits, and
1384 * also check that geographic latitudes are within bounds. For geographic data we
1385 * also examine if the increment * 60 or 3600 is very close (< 1e-4) to an integer,
1386 * in which case we reset it to the exact reciprocal value. */
1387 unsigned int k;
1388 double norm_v, round_v, d, slop;
1389 static char *type[4] = {"xmin", "xmax", "ymin", "ymax"};
1390
1391 if (gmt_M_x_is_lon (GMT, GMT_IN) && (header->wesn[XHI] - header->wesn[XLO] - header->inc[GMT_X]) <= 360.0) { /* Correct any slop in geographic increments */
1392 gmtgrdio_doctor_geo_increments (GMT, header);
1393 if (gmt_M_y_is_lat (GMT, GMT_IN)) {
1394 if ((header->wesn[YLO]+90.0) < (-GMT_CONV4_LIMIT*header->inc[GMT_Y]))
1395 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Round-off patrol found south latitude outside valid range (%.16g)!\n", header->wesn[YLO]);
1396 if ((header->wesn[YHI]-90.0) > (GMT_CONV4_LIMIT*header->inc[GMT_Y]))
1397 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Round-off patrol found north latitude outside valid range (%.16g)!\n", header->wesn[YHI]);
1398 }
1399 }
1400
1401 /* If boundaries are close to multiple of inc/2 fix them */
1402 for (k = XLO; k <= YHI; k++) { /* Check all limits for closeness to 0.5*increments */
1403 d = 0.5 * ((k < YLO) ? header->inc[GMT_X] : header->inc[GMT_Y]);
1404 norm_v = header->wesn[k] / d;
1405 round_v = rint (norm_v);
1406 slop = fabs (norm_v - round_v);
1407 if (slop > GMT_CONV12_LIMIT && slop < GMT_CONV4_LIMIT) { /* Ignore super tiny-slop and larger mismatches */
1408 header->wesn[k] = round_v * d;
1409 norm_v = header->wesn[k];
1410 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Round-off patrol changed grid limit for %s from %.16g to %.16g\n",
1411 type[k], norm_v, header->wesn[k]);
1412 }
1413 }
1414 }
1415
gmtlib_read_grd_info(struct GMT_CTRL * GMT,char * file,struct GMT_GRID_HEADER * header)1416 int gmtlib_read_grd_info (struct GMT_CTRL *GMT, char *file, struct GMT_GRID_HEADER *header) {
1417 /* file: File name
1418 * header: grid structure header
1419 * Note: The header reflects what is actually in the file, and all the dimensions
1420 * reflect the number of rows, cols, size, pads etc. However, if gmtlib_read_grd is
1421 * called requesting a subset then these will be reset accordingly.
1422 */
1423
1424 int err; /* Implied by gmt_M_err_trap */
1425 unsigned int n_columns, n_rows;
1426 double scale, offset;
1427 gmt_grdfloat invalid;
1428 struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
1429
1430 gmt_M_err_trap (gmt_grd_get_format (GMT, file, header, true)); /* Get format and also parse any +s<scl> +o<offset> +n<nan> modifiers */
1431 HH = gmt_get_H_hidden (header);
1432
1433 /* remember scale, offset, and invalid: */
1434 scale = header->z_scale_factor;
1435 offset = header->z_add_offset;
1436 invalid = header->nan_value;
1437
1438 gmt_M_err_trap ((*GMT->session.readinfo[header->type]) (GMT, header));
1439 GMT_Set_Index (GMT->parent, header, GMT_GRID_LAYOUT);
1440
1441 gmtgrdio_grd_xy_scale (GMT, header, GMT_IN); /* Possibly scale wesn,inc */
1442
1443 /* restore non-default scale, offset, and invalid: */
1444 if (HH->z_scale_given) /* User used +s<scl> */
1445 header->z_scale_factor = scale;
1446 if (HH->z_offset_given) /* User used +s<off> */
1447 header->z_add_offset = offset;
1448 if (isfinite(invalid)) /* Means user used +n<invalid> */
1449 header->nan_value = invalid;
1450
1451 gmtlib_grd_get_units (GMT, header);
1452 if (strncmp (GMT->init.module_name, "grdedit", 7U))
1453 gmtgrdio_round_off_patrol (GMT, header); /* Ensure limit/inc consistency */
1454 //gmtlib_clean_global_headers (GMT, header);
1455
1456 if (header->ProjRefPROJ4 && strstr (header->ProjRefPROJ4, "longlat")) /* A geographic image perhaps */
1457 gmt_set_geographic (GMT, GMT_IN);
1458
1459 HH->grdtype = gmtlib_get_grdtype (GMT, GMT_IN, header);
1460
1461 gmt_M_err_pass (GMT, gmt_grd_RI_verify (GMT, header, 0), file);
1462 n_columns = header->n_columns; n_rows = header->n_rows; /* Save copy */
1463 gmt_set_grddim (GMT, header); /* Set all integer dimensions and xy_off */
1464
1465 /* Sanity check for grid that may have been created oddly. Inspired by
1466 * Geomapapp output where -R was set to outside of pixel boundaries instead
1467 * of standard -R settings, yet with node_offset = gridline... */
1468
1469 if (abs((int)(header->n_columns - n_columns)) == 1 && abs((int)(header->n_rows - n_rows)) == 1) {
1470 header->n_columns = n_columns; header->n_rows = n_rows;
1471 if (header->registration == GMT_GRID_PIXEL_REG) {
1472 header->registration = GMT_GRID_NODE_REG;
1473 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Grid has wrong registration type. Switching from pixel to gridline registration\n");
1474 }
1475 else {
1476 header->registration = GMT_GRID_PIXEL_REG;
1477 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Grid has wrong registration type. Switching from gridline to pixel registration\n");
1478 }
1479 }
1480
1481 /* unpack z-range: */
1482 header->z_min = header->z_min * header->z_scale_factor + header->z_add_offset;
1483 header->z_max = header->z_max * header->z_scale_factor + header->z_add_offset;
1484
1485 return (GMT_NOERROR);
1486 }
1487
gmtlib_write_grd_info(struct GMT_CTRL * GMT,char * file,struct GMT_GRID_HEADER * header)1488 int gmtlib_write_grd_info (struct GMT_CTRL *GMT, char *file, struct GMT_GRID_HEADER *header) {
1489 /* file: File name
1490 * header: grid structure header
1491 */
1492
1493 int err; /* Implied by gmt_M_err_trap */
1494
1495 gmt_M_err_trap (gmt_grd_get_format (GMT, file, header, false));
1496
1497 gmtgrdio_grd_xy_scale (GMT, header, GMT_OUT); /* Possibly scale wesn,inc */
1498 /* pack z-range: */
1499 header->z_min = (header->z_min - header->z_add_offset) / header->z_scale_factor;
1500 header->z_max = (header->z_max - header->z_add_offset) / header->z_scale_factor;
1501 return ((*GMT->session.writeinfo[header->type]) (GMT, header));
1502 }
1503
gmt_update_grd_info(struct GMT_CTRL * GMT,char * file,struct GMT_GRID_HEADER * header)1504 int gmt_update_grd_info (struct GMT_CTRL *GMT, char *file, struct GMT_GRID_HEADER *header) {
1505 /* file: - IGNORED -
1506 * header: grid structure header
1507 */
1508
1509 /* pack z-range: */
1510 gmt_M_unused(file);
1511 header->z_min = (header->z_min - header->z_add_offset) / header->z_scale_factor;
1512 header->z_max = (header->z_max - header->z_add_offset) / header->z_scale_factor;
1513 return ((*GMT->session.updateinfo[header->type]) (GMT, header));
1514 }
1515
gmtlib_read_grd(struct GMT_CTRL * GMT,char * file,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,double * wesn,unsigned int * pad,int complex_mode)1516 int gmtlib_read_grd (struct GMT_CTRL *GMT, char *file, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, double *wesn, unsigned int *pad, int complex_mode) {
1517 /* file: - IGNORED -
1518 * header: grid structure header
1519 * grid: array with final grid
1520 * wesn: Sub-region to extract [Use entire file if NULL or contains 0,0,0,0]
1521 * padding: # of empty rows/columns to add on w, e, s, n of grid, respectively
1522 * complex_mode: &1 | &2 if complex array is to hold real (1) and imaginary (2) parts (otherwise read as real only)
1523 * Note: The file has only real values, we simply allow space in the array
1524 * for imaginary parts when processed by grdfft etc.
1525 */
1526
1527 bool expand; /* true or false */
1528 int err = GMT_OK; /* Implied by gmt_M_err_trap */
1529 struct GRD_PAD P;
1530 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1531
1532 complex_mode &= GMT_GRID_IS_COMPLEX_MASK; /* Remove any non-complex flags */
1533 /* If we are reading a 2nd grid (e.g., real, then imag) we must update info about the file since it will be a different file */
1534 if (header->complex_mode && (header->complex_mode & complex_mode) == 0) gmt_M_err_trap (gmt_grd_get_format (GMT, file, header, true));
1535
1536 expand = gmtgrdio_padspace (GMT, header, wesn, false, pad, &P); /* true if we can extend the region by the pad-size to obtain real data for BC */
1537
1538 if ((err = gmtgrdio_grd_layout (GMT, header, grid, complex_mode & GMT_GRID_IS_COMPLEX_MASK, GMT_IN))) /* Deal with complex layout */
1539 return err;
1540
1541 gmt_M_err_trap ((*GMT->session.readgrd[header->type]) (GMT, header, grid, P.wesn, P.pad, complex_mode));
1542
1543 if (header->z_min == DBL_MAX && header->z_max == -DBL_MAX) /* No valid data values in the grid */
1544 header->z_min = header->z_max = NAN;
1545 if (expand) /* Must undo the region extension and reset n_columns, n_rows using original pad */
1546 gmt_M_memcpy (header->wesn, wesn, 4, double);
1547 gmt_M_grd_setpad (GMT, header, pad); /* Copy the pad to the header */
1548 gmt_set_grddim (GMT, header); /* Update all dimensions */
1549 HH->grdtype = gmtlib_get_grdtype (GMT, GMT_IN, header); /* Since may change if a subset */
1550 if (expand) gmt_grd_zminmax (GMT, header, grid); /* Reset min/max since current extrema includes the padded region */
1551 gmtgrdio_pack_grid (GMT, header, grid, k_grd_unpack); /* revert scale and offset */
1552 gmt_BC_init (GMT, header); /* Initialize grid interpolation and boundary condition parameters */
1553
1554 return (GMT_NOERROR);
1555 }
1556
gmtlib_write_grd(struct GMT_CTRL * GMT,char * file,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,double * wesn,unsigned int * pad,int complex_mode)1557 int gmtlib_write_grd (struct GMT_CTRL *GMT, char *file, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, double *wesn, unsigned int *pad, int complex_mode) {
1558 /* file: File name
1559 * header: grid structure header
1560 * grid: array with final grid
1561 * wesn: Sub-region to write out [Use entire file if NULL or contains 0,0,0,0]
1562 * padding: # of empty rows/columns to add on w, e, s, n of grid, respectively
1563 * complex_mode: &1 | &2 if complex array is to hold real (1) and imaginary (2) parts (otherwise read as real only)
1564 * Note: The file has only real values, we simply allow space in the array
1565 * for imaginary parts when processed by grdfft etc.
1566 */
1567
1568 int err = GMT_OK; /* Implied by gmt_M_err_trap */
1569
1570 gmt_M_err_trap (gmt_grd_get_format (GMT, file, header, false));
1571 gmtgrdio_pack_grid (GMT, header, grid, k_grd_pack); /* scale and offset */
1572 gmtgrdio_grd_xy_scale (GMT, header, GMT_OUT); /* Possibly scale wesn,inc */
1573
1574 if ((err = gmtgrdio_grd_layout (GMT, header, grid, complex_mode, GMT_OUT))) /* Deal with complex layout */
1575 return (err);
1576 gmtgrdio_grd_check_consistency (GMT, header, grid); /* Fix east repeating columns and polar values */
1577 err = (*GMT->session.writegrd[header->type]) (GMT, header, grid, wesn, pad, complex_mode);
1578 if (GMT->parent->leave_grid_scaled == 0) gmtgrdio_pack_grid (GMT, header, grid, k_grd_unpack); /* revert scale and offset to leave grid as it was before writing unless session originated from gm*/
1579 return (err);
1580 }
1581
gmtlib_grd_data_size(struct GMT_CTRL * GMT,unsigned int format,gmt_grdfloat * nan_value)1582 size_t gmtlib_grd_data_size (struct GMT_CTRL *GMT, unsigned int format, gmt_grdfloat *nan_value) {
1583 /* Determine size of data type and set NaN value, if not yet done so (integers only) */
1584
1585 switch (GMT->session.grdformat[format][1]) {
1586 case 'b':
1587 if (isnan (*nan_value)) *nan_value = CHAR_MIN;
1588 return (sizeof (char));
1589 break;
1590 case 's':
1591 if (isnan (*nan_value)) *nan_value = SHRT_MIN;
1592 return (sizeof (int16_t));
1593 break;
1594 case 'i':
1595 if (isnan (*nan_value)) *nan_value = INT_MIN;
1596 /* Intentionally fall through */
1597 case 'm':
1598 return (sizeof (int32_t));
1599 break;
1600 case 'f':
1601 return (sizeof (float));
1602 break;
1603 case 'd':
1604 return (sizeof (double));
1605 break;
1606 default:
1607 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unknown grid data type: %c\n", GMT->session.grdformat[format][1]);
1608 return (GMT_GRDIO_UNKNOWN_TYPE);
1609 }
1610 }
1611
gmt_grd_set_ij_inc(struct GMT_CTRL * GMT,unsigned int n_columns,int * ij_inc)1612 void gmt_grd_set_ij_inc (struct GMT_CTRL *GMT, unsigned int n_columns, int *ij_inc) {
1613 /* Set increments to the 4 nodes with ij as lower-left node, from a node at (i,j).
1614 * n_columns may be header->n_columns or header->mx depending on pad */
1615 int s_nx = n_columns; /* A signed version */
1616 gmt_M_unused(GMT);
1617 ij_inc[0] = 0; /* No offset relative to itself */
1618 ij_inc[1] = 1; /* The node one column to the right relative to ij */
1619 ij_inc[2] = 1 - s_nx; /* The node one column to the right and one row up relative to ij */
1620 ij_inc[3] = -s_nx; /* The node one row up relative to ij */
1621 }
1622
gmt_grd_format_decoder(struct GMT_CTRL * GMT,const char * code,unsigned int * type_id)1623 int gmt_grd_format_decoder (struct GMT_CTRL *GMT, const char *code, unsigned int *type_id) {
1624 /* Returns the integer grid format ID that goes with the specified 2-character code */
1625 if (isdigit ((int)code[0])) {
1626 /* File format number given, look for old code */
1627 unsigned id_candidate = (unsigned) abs (atoi (code));
1628 if (id_candidate > 0 && id_candidate < GMT_N_GRD_FORMATS) {
1629 *type_id = id_candidate;
1630 return GMT_NOERROR;
1631 }
1632 }
1633 else {
1634 /* Character code given */
1635 unsigned i;
1636 for (i = 1; i < GMT_N_GRD_FORMATS; i++) {
1637 if (strncmp (GMT->session.grdformat[i], code, 2) == 0) {
1638 *type_id = i;
1639 return GMT_NOERROR;
1640 }
1641 }
1642 }
1643
1644 return GMT_GRDIO_UNKNOWN_ID;
1645 }
1646
gmt_grd_RI_verify(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,unsigned int mode)1647 int gmt_grd_RI_verify (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, unsigned int mode) {
1648 /* mode - 0 means we are checking an existing grid, mode = 1 means we test a new -R -I combination */
1649 /* gmt_grd_RI_verify -- routine to check grd R and I compatibility
1650 *
1651 * Author: W H F Smith
1652 * Date: 20 April 1998
1653 */
1654
1655 unsigned int error = 0, level = (mode == 0) ? GMT_MSG_ERROR : GMT_MSG_WARNING;
1656 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
1657
1658 if (!strcmp (GMT->init.module_name, "grdedit")) return (GMT_NOERROR); /* Separate handling in grdedit to allow grdedit -A */
1659
1660 switch (gmt_minmaxinc_verify (GMT, h->wesn[XLO], h->wesn[XHI], h->inc[GMT_X], GMT_CONV4_LIMIT)) {
1661 case 3:
1662 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Grid x increment <= 0.0\n");
1663 error++;
1664 break;
1665 case 2:
1666 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Grid x range <= 0.0\n");
1667 if (gmt_M_is_geographic (GMT, GMT_IN)) GMT_Report (GMT->parent, GMT_MSG_ERROR,
1668 "Make sure west < east for geographic coordinates\n");
1669 error++;
1670 break;
1671 case 1:
1672 if (HH->var_spacing[GMT_X] == 0) {
1673 GMT_Report (GMT->parent, level,
1674 "(x_max-x_min) must equal (NX + eps) * x_inc), where NX is an integer and |eps| <= %g.\n", GMT_CONV4_LIMIT);
1675 error++;
1676 }
1677 default:
1678 /* Everything is OK */
1679 break;
1680 }
1681
1682 switch (gmt_minmaxinc_verify (GMT, h->wesn[YLO], h->wesn[YHI], h->inc[GMT_Y], GMT_CONV4_LIMIT)) {
1683 case 3:
1684 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Grid y increment <= 0.0\n");
1685 error++;
1686 break;
1687 case 2:
1688 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Grid y range <= 0.0\n");
1689 error++;
1690 break;
1691 case 1:
1692 if (HH->var_spacing[GMT_Y] == 0) {
1693 GMT_Report (GMT->parent, level,
1694 "(y_max-y_min) must equal (NY + eps) * y_inc), where NY is an integer and |eps| <= %g.\n", GMT_CONV4_LIMIT);
1695 error++;
1696 }
1697 default:
1698 /* Everything is OK */
1699 break;
1700 }
1701 if (error) return ((mode == 0) ? GMT_GRDIO_RI_OLDBAD : GMT_GRDIO_RI_NEWBAD);
1702
1703 /* Final polish for geo grids that are global to ensure clean -R settings for the cases -Rg and -Rd */
1704
1705 if (gmt_M_x_is_lon (GMT, GMT_IN)) {
1706 if (fabs (h->wesn[XLO]) < GMT_CONV12_LIMIT) h->wesn[XLO] = 0.0;
1707 else if (fabs (180.0+h->wesn[XLO]) < GMT_CONV12_LIMIT) h->wesn[XLO] = -180.0;
1708 else if (fabs (h->wesn[XLO]-180.0) < GMT_CONV12_LIMIT) h->wesn[XLO] = +180.0;
1709 else if (fabs (h->wesn[XLO]-360.0) < GMT_CONV12_LIMIT) h->wesn[XLO] = +360.0;
1710 if (fabs (h->wesn[XHI]) < GMT_CONV12_LIMIT) h->wesn[XHI] = 0.0;
1711 else if (fabs (180.0+h->wesn[XHI]) < GMT_CONV12_LIMIT) h->wesn[XHI] = -180.0;
1712 else if (fabs (h->wesn[XHI]-180.0) < GMT_CONV12_LIMIT) h->wesn[XHI] = +180.0;
1713 else if (fabs (h->wesn[XHI]-360.0) < GMT_CONV12_LIMIT) h->wesn[XHI] = +360.0;
1714 }
1715 if (gmt_M_y_is_lat (GMT, GMT_IN)) {
1716 if (fabs (90.0+h->wesn[YLO]) < GMT_CONV12_LIMIT) h->wesn[YLO] = -90.0;
1717 if (fabs (h->wesn[YLO]-90.0) < GMT_CONV12_LIMIT) h->wesn[YLO] = +90.0;
1718 }
1719 return (GMT_NOERROR);
1720 }
1721
gmt_grd_prep_io(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,double wesn[],unsigned int * width,unsigned int * height,int * first_col,int * last_col,int * first_row,int * last_row,unsigned int ** index)1722 int gmt_grd_prep_io (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, double wesn[], unsigned int *width, unsigned int *height, int *first_col, int *last_col, int *first_row, int *last_row, unsigned int **index) {
1723 /* Determines which rows and columns to extract to extract from a grid, based on w,e,s,n.
1724 * This routine first rounds the w,e,s,n boundaries to the nearest gridlines or pixels,
1725 * then determines the first and last columns and rows, and the width and height of the subset (in cells).
1726 * The routine also returns and array of the x-indices in the source grid to be used in the target (subset) grid.
1727 * All integers represented positive definite items hence unsigned variables.
1728 */
1729
1730 bool geo = false;
1731 unsigned int one_or_zero, col, *actual_col = NULL; /* Column numbers */
1732 double small = 0.1, half_or_zero, x;
1733 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1734
1735 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "region: %g %g, grid: %g %g\n", wesn[XLO], wesn[XHI], header->wesn[XLO], header->wesn[XHI]);
1736
1737 half_or_zero = (header->registration == GMT_GRID_PIXEL_REG) ? 0.5 : 0.0;
1738
1739 if (!gmt_M_is_subset (GMT, header, wesn)) { /* Get entire file */
1740 *width = header->n_columns;
1741 *height = header->n_rows;
1742 *first_col = *first_row = 0;
1743 *last_col = header->n_columns - 1;
1744 *last_row = header->n_rows - 1;
1745 gmt_M_memcpy (wesn, header->wesn, 4, double);
1746 }
1747 else { /* Must deal with a subregion */
1748 if (gmt_M_x_is_lon (GMT, GMT_IN))
1749 geo = true; /* Geographic data for sure */
1750 else if (wesn[XLO] < header->wesn[XLO] || wesn[XHI] > header->wesn[XHI])
1751 geo = true; /* Probably dealing with periodic grid */
1752
1753 x = fabs (header->wesn[YLO] - wesn[YLO]); /* if |x| < GMT_CONV4_LIMIT * header->inc[GMT_Y] we set wesn to the grid limit */
1754 if (x > 0.0 && x < GMT_CONV4_LIMIT * header->inc[GMT_Y]) wesn[YLO] = header->wesn[YLO]; /* Avoid snafu */
1755 x = fabs (header->wesn[YHI] - wesn[YHI]); /* if |x| < GMT_CONV4_LIMIT * header->inc[GMT_Y] we set wesn to the grid limit */
1756 if (x > 0.0 && x < GMT_CONV4_LIMIT * header->inc[GMT_Y]) wesn[YHI] = header->wesn[YHI]; /* Avoid snafu */
1757
1758 if (wesn[YLO] < header->wesn[YLO] || wesn[YHI] > header->wesn[YHI]) return (GMT_GRDIO_DOMAIN_VIOLATION); /* Calling program goofed... */
1759
1760 one_or_zero = (header->registration == GMT_GRID_PIXEL_REG) ? 0 : 1;
1761
1762 /* Make sure w,e,s,n are proper multiples of x_inc,y_inc away from x_min,y_min */
1763
1764 gmt_M_err_pass (GMT, gmt_adjust_loose_wesn (GMT, wesn, header), HH->name);
1765
1766 /* Get dimension of subregion */
1767
1768 *width = urint ((wesn[XHI] - wesn[XLO]) * HH->r_inc[GMT_X]) + one_or_zero;
1769 *height = urint ((wesn[YHI] - wesn[YLO]) * HH->r_inc[GMT_Y]) + one_or_zero;
1770
1771 /* Get first and last row and column numbers */
1772
1773 *first_col = irint (floor ((wesn[XLO] - header->wesn[XLO]) * HH->r_inc[GMT_X] + small));
1774 *last_col = irint (ceil ((wesn[XHI] - header->wesn[XLO]) * HH->r_inc[GMT_X] - small)) - 1 + one_or_zero;
1775 *first_row = irint (floor ((header->wesn[YHI] - wesn[YHI]) * HH->r_inc[GMT_Y] + small));
1776 *last_row = irint (ceil ((header->wesn[YHI] - wesn[YLO]) * HH->r_inc[GMT_Y] - small)) - 1 + one_or_zero;
1777 }
1778
1779 actual_col = gmt_M_memory (GMT, NULL, *width, unsigned int);
1780 if (geo) {
1781 small = 0.1 * header->inc[GMT_X];
1782 for (col = 0; col < (*width); col++) {
1783 x = gmt_M_col_to_x (GMT, col, wesn[XLO], wesn[XHI], header->inc[GMT_X], half_or_zero, *width);
1784 if (header->wesn[XLO] - x > small)
1785 x += 360.0;
1786 else if (x - header->wesn[XHI] > small)
1787 x -= 360.0;
1788 actual_col[col] = (unsigned int)gmt_M_grd_x_to_col (GMT, x, header);
1789 }
1790 }
1791 else { /* Normal ordering */
1792 for (col = 0; col < (*width); col++) actual_col[col] = (*first_col) + col;
1793 }
1794
1795 *index = actual_col;
1796 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "-> region: %g %g, grid: %g %g\n", wesn[XLO], wesn[XHI], header->wesn[XLO], header->wesn[XHI]);
1797 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "row: %d %d, col: %d %d\n", *first_row, *last_row, *first_col, *last_col);
1798
1799 return (GMT_NOERROR);
1800 }
1801
gmtgrdio_decode_grd_h_info_old(struct GMT_CTRL * GMT,char * input,struct GMT_GRID_HEADER * h)1802 GMT_LOCAL void gmtgrdio_decode_grd_h_info_old (struct GMT_CTRL *GMT, char *input, struct GMT_GRID_HEADER *h) {
1803 /* Given input string, copy elements into string portions of h.
1804 By default use "/" as the field separator. However, if the first and
1805 last character of the input string is the same AND that character
1806 is non-alpha-numeric, use the first character as a separator. This
1807 is to allow "/" as part of the fields.
1808 If a field is blank [or has an equals sign - backwards compatibility], skip it.
1809 This routine is usually called if -D<input> was given by user,
1810 and after gmt_grd_init() has been called. */
1811
1812 char *ptr, *stringp = input, sep[] = "/";
1813 bool copy;
1814 unsigned int entry = 0;
1815 size_t len = 0;
1816 double d;
1817
1818 if (input[0] != input[strlen(input)-1]) {}
1819 else if (input[0] == '=') {}
1820 else if (input[0] >= 'A' && input[0] <= 'Z') {}
1821 else if (input[0] >= 'a' && input[0] <= 'z') {}
1822 else if (input[0] >= '0' && input[0] <= '9') {}
1823 else {
1824 sep[0] = input[0];
1825 ++stringp; /* advance past first field separator */
1826 }
1827
1828 while ((ptr = strsep (&stringp, sep)) != NULL) { /* using strsep because of possible empty fields */
1829 if (*ptr != '\0' || strcmp (ptr, "=") == 0 || strcmp (ptr, " ") == 0) { /* entry is not blank or "=" or " " */
1830 len = strlen (ptr);
1831 copy = (len > 1 || ptr[0] != '-'); /* Copy unless a "-" was given */
1832 switch (entry) {
1833 case 0:
1834 gmt_M_memset (h->x_units, GMT_GRID_UNIT_LEN80, char);
1835 if (len >= GMT_GRID_UNIT_LEN80)
1836 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1837 "X unit string exceeds upper length of %d characters (truncated)\n",
1838 GMT_GRID_UNIT_LEN80);
1839 if (copy) strncpy (h->x_units, ptr, GMT_GRID_UNIT_LEN80-1);
1840 break;
1841 case 1:
1842 gmt_M_memset (h->y_units, GMT_GRID_UNIT_LEN80, char);
1843 if (len >= GMT_GRID_UNIT_LEN80)
1844 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1845 "Y unit string exceeds upper length of %d characters (truncated)\n",
1846 GMT_GRID_UNIT_LEN80);
1847 if (copy) strncpy (h->y_units, ptr, GMT_GRID_UNIT_LEN80-1);
1848 break;
1849 case 2:
1850 gmt_M_memset (h->z_units, GMT_GRID_UNIT_LEN80, char);
1851 if (len >= GMT_GRID_UNIT_LEN80)
1852 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1853 "Z unit string exceeds upper length of %d characters (truncated)\n",
1854 GMT_GRID_UNIT_LEN80);
1855 if (copy) strncpy (h->z_units, ptr, GMT_GRID_UNIT_LEN80-1);
1856 break;
1857 case 3:
1858 d = strtod (ptr, NULL);
1859 if (d != 0.0) h->z_scale_factor = d; /* Don't want scale factor to become zero */
1860 break;
1861 case 4:
1862 h->z_add_offset = strtod (ptr, NULL);
1863 break;
1864 case 5:
1865 h->nan_value = strtof (ptr, NULL);
1866 break;
1867 case 6:
1868 gmt_M_memset (h->title, GMT_GRID_TITLE_LEN80, char);
1869 if (len >= GMT_GRID_TITLE_LEN80)
1870 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1871 "Title string exceeds upper length of %d characters (truncated)\n",
1872 GMT_GRID_TITLE_LEN80);
1873 if (copy) strncpy (h->title, ptr, GMT_GRID_TITLE_LEN80-1);
1874 break;
1875 case 7:
1876 gmt_M_memset (h->remark, GMT_GRID_REMARK_LEN160, char);
1877 if (len >= GMT_GRID_REMARK_LEN160)
1878 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1879 "Remark string exceeds upper length of %d characters (truncated)\n",
1880 GMT_GRID_REMARK_LEN160);
1881 if (copy) strncpy (h->remark, ptr, GMT_GRID_REMARK_LEN160-1);
1882 break;
1883 default:
1884 break;
1885 }
1886 }
1887 entry++;
1888 }
1889 }
1890
gmtgrdio_decode_grdcube_info(struct GMT_CTRL * GMT,char * input,unsigned int dim,struct GMT_GRID_HEADER * h,struct GMT_CUBE * C)1891 GMT_LOCAL int gmtgrdio_decode_grdcube_info (struct GMT_CTRL *GMT, char *input, unsigned int dim, struct GMT_GRID_HEADER *h, struct GMT_CUBE *C) {
1892 size_t k, n_slash = 0;
1893 unsigned int uerr = 0;
1894 bool old = false;
1895 char *modifiers = "xyzdsontrv", code;
1896 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
1897
1898 for (k = 0; k < strlen (input); k++) if (input[k] == '/') n_slash++;
1899 if (!(input[0] == '+' && gmt_found_modifier (GMT, input, "norstxyz"))) { /* Cannot be new syntax */
1900 old = (n_slash > 4); /* Pretty sure this is the old syntax of that many slashes */
1901 }
1902 if (dim == 3 && old) return GMT_PARSE_ERROR; /* Old syntax does not exist for cubes */
1903 if (dim < 2 || dim > 3) return GMT_PARSE_ERROR; /* Not a valid dimension */
1904 if (old) /* Old grid syntax: -D<xname>/<yname>/<zname>/<scale>/<offset>/<invalid>/<title>/<remark> */
1905 gmtgrdio_decode_grd_h_info_old (GMT, input, h);
1906 else { /* New syntax: -D[+x<xname>][+yyname>][+z<zname>][+d<dname>][+s<scale>][+o<offset>][+n<invalid>][+t<title>][+r<remark>] plus [+v<name>] for 3D */
1907 char word[GMT_LEN256] = {""};
1908 unsigned int pos = 0;
1909 double d;
1910 while (gmt_getmodopt (GMT, 'D', input, modifiers, &pos, word, &uerr) && uerr == 0) {
1911 code = word[0]; /* This is the modifier. Check if 2D and 'z' and change to 'v' */
1912 if (code == 'z' && dim == 2) code = 'd'; /* Backwards compatibility for using +z with grids */
1913 switch (code) {
1914 case 'x': /* Revise x-unit name */
1915 gmt_M_memset (h->x_units, GMT_GRID_UNIT_LEN80, char);
1916 if (strlen(word) > GMT_GRID_UNIT_LEN80)
1917 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1918 "x_unit string exceeds upper length of %d characters (truncated)\n",
1919 GMT_GRID_UNIT_LEN80);
1920 if (word[1]) strncpy (h->x_units, &word[1], GMT_GRID_UNIT_LEN80-1);
1921 break;
1922 case 'y': /* Revise y-unit name */
1923 gmt_M_memset (h->y_units, GMT_GRID_UNIT_LEN80, char);
1924 if (strlen(word) > GMT_GRID_UNIT_LEN80)
1925 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1926 "y_unit string exceeds upper length of %d characters (truncated)\n",
1927 GMT_GRID_UNIT_LEN80);
1928 if (word[1]) strncpy (h->y_units, &word[1], GMT_GRID_UNIT_LEN80-1);
1929 break;
1930 case 'z': /* Revise 3rd dim z-unit name for 3-D cubes */
1931 gmt_M_memset (C->units, GMT_GRID_UNIT_LEN80, char);
1932 if (strlen(word) > GMT_GRID_UNIT_LEN80)
1933 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1934 "z_unit string exceeds upper length of %d characters (truncated)\n",
1935 GMT_GRID_UNIT_LEN80);
1936 if (word[1]) strncpy (C->units, &word[1], GMT_GRID_UNIT_LEN80-1);
1937 break;
1938 case 'd': /* Revise 2-D or 3-D data-unit name */
1939 gmt_M_memset (h->z_units, GMT_GRID_UNIT_LEN80, char);
1940 if (strlen(word) > GMT_GRID_UNIT_LEN80)
1941 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1942 "z_unit string exceeds upper length of %d characters (truncated)\n",
1943 GMT_GRID_UNIT_LEN80);
1944 if (word[1]) strncpy (h->z_units, &word[1], GMT_GRID_UNIT_LEN80-1);
1945 break;
1946 case 's': /* Revise the scale */
1947 d = strtod (&word[1], NULL);
1948 if (d != 0.0) h->z_scale_factor = d; /* Don't want scale factor to become zero */
1949 break;
1950 case 'o': /* Revise the offset */
1951 h->z_add_offset = strtod (&word[1], NULL);
1952 break;
1953 case 'n': /* Revise the nan-value */
1954 h->nan_value = strtof (&word[1], NULL);
1955 break;
1956 case 't': /* Revise the title */
1957 gmt_M_memset (h->title, GMT_GRID_TITLE_LEN80, char);
1958 if (strlen(word) > GMT_GRID_TITLE_LEN80)
1959 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1960 "WTitle string exceeds upper length of %d characters (truncated)\n",
1961 GMT_GRID_TITLE_LEN80);
1962 if (word[1]) strncpy (h->title, &word[1], GMT_GRID_TITLE_LEN80-1);
1963 break;
1964 case 'r': /* Revise the title */
1965 gmt_M_memset (h->remark, GMT_GRID_REMARK_LEN160, char);
1966 if (strlen(word) > GMT_GRID_REMARK_LEN160)
1967 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1968 "Remark string exceeds upper length of %d characters (truncated)\n",
1969 GMT_GRID_REMARK_LEN160);
1970 if (word[1]) strncpy (h->remark, &word[1], GMT_GRID_REMARK_LEN160-1);
1971 break;
1972 case 'v': /* Set the data netCDF variable name */
1973 gmt_M_memset (HH->varname, GMT_GRID_VARNAME_LEN80, char);
1974 if (strlen(word) > GMT_GRID_VARNAME_LEN80)
1975 GMT_Report (GMT->parent, GMT_MSG_WARNING,
1976 "data variable name exceeds upper length of %d characters (truncated)\n",
1977 GMT_GRID_VARNAME_LEN80);
1978 if (word[1]) strncpy (HH->varname, &word[1], GMT_GRID_VARNAME_LEN80-1);
1979 break;
1980 default: /* These are caught in gmt_getmodopt so break is just for Coverity */
1981 break;
1982 }
1983 }
1984 }
1985 return (int)uerr;
1986 }
1987
gmt_decode_grd_h_info(struct GMT_CTRL * GMT,char * input,struct GMT_GRID_HEADER * h)1988 int gmt_decode_grd_h_info (struct GMT_CTRL *GMT, char *input, struct GMT_GRID_HEADER *h) {
1989 /* Backwards compatible when just dealing with grids */
1990 return gmtgrdio_decode_grdcube_info (GMT, input, 2U, h, NULL);
1991 }
1992
gmt_decode_cube_h_info(struct GMT_CTRL * GMT,char * input,struct GMT_CUBE * C)1993 int gmt_decode_cube_h_info (struct GMT_CTRL *GMT, char *input, struct GMT_CUBE *C) {
1994 /* Backwards compatible when just dealing with grids */
1995 return gmtgrdio_decode_grdcube_info (GMT, input, 3U, C->header, C);
1996 }
1997
gmt_grd_set_cartesian(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,unsigned int direction)1998 void gmt_grd_set_cartesian (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, unsigned int direction) {
1999 /* When we need to turn a geographic grid into a Cartesian grid type.
2000 * direction is 0 for input, 1 for output, and 2 for both */
2001 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2002 if (direction == GMT_IO) { /* Set Cartesian for both directions */
2003 gmt_set_cartesian (GMT, GMT_IN);
2004 gmt_set_cartesian (GMT, GMT_OUT);
2005 }
2006 else
2007 gmt_set_cartesian (GMT, direction);
2008 /* Reset the units from degree_longitude/latitude to plain x/y */
2009 strcpy (h->x_units, "x");
2010 strcpy (h->y_units, "y");
2011 HH->grdtype = GMT_GRID_CARTESIAN; /* Set hidden type to Cartesian */
2012 }
2013
gmtgrdio_grdcube_info_syntax(struct GMT_CTRL * GMT,char option,unsigned int dim)2014 GMT_LOCAL void gmtgrdio_grdcube_info_syntax (struct GMT_CTRL *GMT, char option, unsigned int dim) {
2015 /* Display the option for setting grid, cube, or both metadata in grdedit etc. */
2016 static char *type[3] = {"grid", "cube", "grid or cube"};
2017 static char *vname[3] = {"z", "cube", "z or cube"};
2018 unsigned int k = dim - 2;
2019 struct GMTAPI_CTRL *API = GMT->parent;
2020 GMT_Usage (API, 1, "\n-%c<information>", option);
2021 GMT_Usage (API, -2, "Append %s header information as one string composed of one or "
2022 "more modifiers; items not listed will remain unchanged:", type[k]);
2023 GMT_Usage (API, 3, "+x Append x-dimension unit <name>, or leave blank to reset.");
2024 GMT_Usage (API, 3, "+y Append y-dimension unit <name>, or leave blank to reset.");
2025 if (dim > 2) GMT_Usage (API, 3, "+z Append z-dimension unit <name>, or leave blank to reset.");
2026 GMT_Usage (API, 3, "+d Append %s data unit <name>, or leave blank to reset.", type[k]);
2027 GMT_Usage (API, 3, "+n Append a value to represent missing data.", type[k]);
2028 GMT_Usage (API, 3, "+t Append %s <title>, or leave blank to reset.", type[k]);
2029 GMT_Usage (API, 3, "+r Append %s <remark>, or leave blank to reset.", type[k]);
2030 GMT_Usage (API, 3, "+s Append data <scale>.");
2031 GMT_Usage (API, 3, "+o Append data <offset>.");
2032 GMT_Usage (API, 3, "+v Append netCDF data variable <name> (if netCDF format) [%s].", vname[k]);
2033 }
2034
gmt_grd_info_syntax(struct GMT_CTRL * GMT,char option)2035 void gmt_grd_info_syntax (struct GMT_CTRL *GMT, char option) {
2036 gmtgrdio_grdcube_info_syntax (GMT, option, 2U);
2037 }
2038
gmt_cube_info_syntax(struct GMT_CTRL * GMT,char option)2039 void gmt_cube_info_syntax (struct GMT_CTRL *GMT, char option) {
2040 gmtgrdio_grdcube_info_syntax (GMT, option, 3U);
2041 }
gmt_grdcube_info_syntax(struct GMT_CTRL * GMT,char option)2042 void gmt_grdcube_info_syntax (struct GMT_CTRL *GMT, char option) {
2043 gmtgrdio_grdcube_info_syntax (GMT, option, 4U);
2044 }
2045
gmt_set_grdinc(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)2046 void gmt_set_grdinc (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
2047 /* Update grid increments based on w/e/s/n, n_columns/n_rows, and registration */
2048 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2049 gmt_M_unused(GMT);
2050 h->inc[GMT_X] = gmt_M_get_inc (GMT, h->wesn[XLO], h->wesn[XHI], h->n_columns, h->registration);
2051 h->inc[GMT_Y] = gmt_M_get_inc (GMT, h->wesn[YLO], h->wesn[YHI], h->n_rows, h->registration);
2052 HH->r_inc[GMT_X] = 1.0 / h->inc[GMT_X]; /* Get inverse increments to avoid divisions later */
2053 HH->r_inc[GMT_Y] = 1.0 / h->inc[GMT_Y];
2054 }
2055
gmt_set_grddim(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)2056 void gmt_set_grddim (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
2057 /* Assumes pad is set and then computes n_columns, n_rows, mx, my, nm, size, xy_off based on w/e/s/n. */
2058 h->n_columns = gmt_M_grd_get_nx (GMT, h); /* Set n_columns, n_rows based on w/e/s/n and offset */
2059 h->n_rows = gmt_M_grd_get_ny (GMT, h);
2060 h->mx = gmt_M_grd_get_nxpad (h, h->pad); /* Set mx, my based on h->{n_columns,n_rows} and the current pad */
2061 h->my = gmt_M_grd_get_nypad (h, h->pad);
2062 h->nm = gmt_M_grd_get_nm (h); /* Sets the number of actual data items */
2063 h->size = gmtgrdio_grd_get_size (h); /* Sets the number of items (not bytes!) needed to hold this array, which includes the padding (size >= nm) */
2064 h->xy_off = 0.5 * h->registration;
2065 gmt_set_grdinc (GMT, h);
2066 }
2067
gmt_grd_init(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,struct GMT_OPTION * options,bool update)2068 void gmt_grd_init (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, struct GMT_OPTION *options, bool update) {
2069 /* gmt_grd_init initializes a grd header to default values and copies the
2070 * options to the header variable command.
2071 * update = true if we only want to update command line */
2072
2073 int i;
2074 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
2075
2076 if (update) /* Only clean the command history */
2077 gmt_M_memset (header->command, GMT_GRID_COMMAND_LEN320, char);
2078 else { /* Wipe the slate clean */
2079 void *ptr = HH->index_function; /* Keep these two */
2080 char mem[4];
2081 gmt_M_memcpy (mem, header->mem_layout, 4, char);
2082 gmt_M_memset (header, 1, struct GMT_GRID_HEADER);
2083 HH->index_function = ptr;
2084 header->hidden = HH;
2085 gmt_M_memcpy (header->mem_layout, mem, 4, char);
2086
2087 /* Set the variables that are not initialized to 0/false/NULL */
2088 header->z_scale_factor = 1.0;
2089 HH->row_order = k_nc_start_south; /* S->N */
2090 HH->z_id = GMT_NOTSET;
2091 header->n_bands = 1; /* Grids have at least one band but images may have 3 (RGB) or 4 (RGBA) */
2092 header->z_min = GMT->session.d_NaN;
2093 header->z_max = GMT->session.d_NaN;
2094 header->nan_value = GMT->session.f_NaN;
2095 if (gmt_M_is_geographic (GMT, GMT_OUT)) {
2096 strcpy (header->x_units, "longitude [degrees_east]");
2097 strcpy (header->y_units, "latitude [degrees_north]");
2098 }
2099 else {
2100 strcpy (header->x_units, "x");
2101 strcpy (header->y_units, "y");
2102 }
2103 strcpy (header->z_units, "z");
2104 gmt_M_grd_setpad (GMT, header, GMT->current.io.pad); /* Assign default pad */
2105 }
2106
2107 /* Always update command line history, if given */
2108
2109 if (options) {
2110 size_t len;
2111 struct GMTAPI_CTRL *API = GMT->parent;
2112 int argc = 0, k_data;
2113 char **argv = NULL, *c = NULL;
2114 char file[GMT_LEN64] = {""}, *txt = NULL;
2115
2116 if ((argv = GMT_Create_Args (API, &argc, options)) == NULL) {
2117 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Could not create argc, argv from linked structure options!\n");
2118 return;
2119 }
2120 strncpy (header->command, GMT->init.module_name, GMT_GRID_COMMAND_LEN320-1);
2121 len = strlen (header->command);
2122 for (i = 0; len < GMT_GRID_COMMAND_LEN320 && i < argc; i++) {
2123 if (gmt_file_is_tiled_list (API, argv[i], &k_data, NULL, NULL)) { /* Want to replace the tiled list with the original @earth_relief_xxx name instead */
2124 snprintf (file, GMT_LEN64, "@%s", API->remote_info[k_data].file);
2125 txt = file;
2126 }
2127 else if ((k_data = gmt_remote_dataset_id (API, argv[i])) != GMT_NOTSET && API->remote_info[k_data].ext[0] && (c = strstr (argv[i], API->remote_info[k_data].ext))) {
2128 c[0] = '\0';
2129 snprintf (file, GMT_LEN64, "%s", argv[i]);
2130 c[0] = '.';
2131 txt = file;
2132 }
2133 else
2134 txt = argv[i];
2135 len += strlen (txt) + 1;
2136 if (len >= GMT_GRID_COMMAND_LEN320) continue;
2137 strcat (header->command, " ");
2138 strcat (header->command, txt);
2139 }
2140 if (len < GMT_GRID_COMMAND_LEN320)
2141 header->command[len] = 0;
2142 else /* Must truncate */
2143 header->command[GMT_GRID_COMMAND_LEN320-1] = 0;
2144 snprintf (header->title, GMT_GRID_TITLE_LEN80, "Produced by %s", GMT->init.module_name);
2145 GMT_Destroy_Args (API, argc, &argv);
2146 }
2147 }
2148
gmt_grd_shift(struct GMT_CTRL * GMT,struct GMT_GRID * G,double shift)2149 void gmt_grd_shift (struct GMT_CTRL *GMT, struct GMT_GRID *G, double shift) {
2150 /* Rotate geographical, global grid in e-w direction
2151 * This function will shift a grid by shift degrees.
2152 * It is only called when we know the grid is geographic. */
2153
2154 unsigned int row, n_warn = 0;
2155 int col, n_shift, width, actual_col;
2156 bool gridline;
2157 uint64_t ij;
2158 gmt_grdfloat *tmp = NULL;
2159 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
2160
2161 n_shift = irint (shift * HH->r_inc[GMT_X]);
2162 width = irint (360.0 * HH->r_inc[GMT_X]);
2163 if (width > (int)G->header->n_columns) {
2164 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot rotate grid, width is too small\n");
2165 return;
2166 }
2167
2168 /* Shift boundaries */
2169
2170 tmp = gmt_M_memory (GMT, NULL, G->header->n_columns, gmt_grdfloat);
2171
2172 G->header->wesn[XLO] += shift;
2173 G->header->wesn[XHI] += shift;
2174 if (G->header->wesn[XHI] < 0.0) {
2175 G->header->wesn[XLO] += 360.0;
2176 G->header->wesn[XHI] += 360.0;
2177 }
2178 else if (G->header->wesn[XHI] > 360.0) {
2179 G->header->wesn[XLO] -= 360.0;
2180 G->header->wesn[XHI] -= 360.0;
2181 }
2182
2183 gridline = (width < (int)G->header->n_columns); /* Gridline-registrered grids will have width = n_columns-1, pixel grids have width = n_columns */
2184
2185 if (gridline) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Repeating column now at %g/%g\n", G->header->wesn[XLO], G->header->wesn[XHI]);
2186
2187
2188 for (row = 0; row < G->header->n_rows; row++) {
2189 ij = gmt_M_ijp (G->header, row, 0);
2190 if (gridline && G->data[ij] != G->data[ij+width]) n_warn++;
2191 for (col = 0; col < (int)G->header->n_columns; col++) {
2192 actual_col = (col - n_shift) % width;
2193 if (actual_col < 0) actual_col += width;
2194 tmp[actual_col] = G->data[ij+col];
2195 }
2196 if (gridline) tmp[width] = tmp[0]; /* Set new repeating column */
2197 gmt_M_memcpy (&G->data[ij], tmp, G->header->n_columns, gmt_grdfloat);
2198 }
2199 gmt_M_free (GMT, tmp);
2200
2201 if (n_warn)
2202 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Inconsistent values at repeated longitude nodes (%g and %g) for %d rows\n",
2203 G->header->wesn[XLO], G->header->wesn[XHI], n_warn);
2204 }
2205
gmt_grd_setregion(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,double * wesn,unsigned int interpolant)2206 int gmt_grd_setregion (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, double *wesn, unsigned int interpolant) {
2207 /* gmt_grd_setregion determines what w,e,s,n should be passed to gmtlib_read_grd.
2208 * It does so by using GMT->common.R.wesn which have been set correctly by map_setup.
2209 * Use interpolant to indicate if (and how) the grid is interpolated after this call.
2210 * This determines possible extension of the grid to allow interpolation (without padding).
2211 *
2212 * Here are some considerations about the boundary we need to match, assuming the grid is gridline oriented:
2213 * - When the output is to become pixels, the outermost point has to be beyond 0.5 cells inside the region
2214 * - When linear interpolation is needed afterwards, the outermost point needs to be on the region edge or beyond
2215 * - When the grid is pixel oriented, the limits need to go outward by another 0.5 cells
2216 * - When the region is global, do not extend the longitudes outward (otherwise you create wrap-around issues)
2217 * So to determine the boundary, we go inward from there.
2218 *
2219 * Return values are as follows:
2220 * 0 = Grid is entirely outside Region
2221 * 1 = Region is equal to or entirely inside Grid
2222 * 2 = All other
2223 */
2224
2225 bool grid_global;
2226 double shift_x, x_range, off;
2227 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
2228
2229 /* First make an educated guess whether the grid and region are geographical and global */
2230 grid_global = gmt_grd_is_global (GMT, h);
2231
2232 switch (interpolant) {
2233 case BCR_BILINEAR:
2234 off = 0.0;
2235 break;
2236 case BCR_BSPLINE:
2237 case BCR_BICUBIC:
2238 off = 1.5;
2239 break;
2240 default:
2241 off = -0.5;
2242 break;
2243 }
2244 if (h->registration == GMT_GRID_PIXEL_REG) off += 0.5;
2245
2246 /* Initial assignment of wesn */
2247 wesn[YLO] = GMT->common.R.wesn[YLO] - off * h->inc[GMT_Y], wesn[YHI] = GMT->common.R.wesn[YHI] + off * h->inc[GMT_Y];
2248 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]) && gmt_M_x_is_lon (GMT, GMT_IN)) off = 0.0;
2249 wesn[XLO] = GMT->common.R.wesn[XLO] - off * h->inc[GMT_X], wesn[XHI] = GMT->common.R.wesn[XHI] + off * h->inc[GMT_X];
2250
2251 if (GMT->common.R.oblique && !gmt_M_is_rect_graticule (GMT)) { /* Used -R... with oblique boundaries - return entire grid */
2252 if (wesn[XHI] < h->wesn[XLO]) /* Make adjustments so GMT->current.proj.[w,e] jives with h->wesn */
2253 shift_x = 360.0;
2254 else if (wesn[XLO] > h->wesn[XHI])
2255 shift_x = -360.0;
2256 else
2257 shift_x = 0.0;
2258
2259 wesn[XLO] = h->wesn[XLO] + lrint ((wesn[XLO] - h->wesn[XLO] + shift_x) * HH->r_inc[GMT_X]) * h->inc[GMT_X];
2260 wesn[XHI] = h->wesn[XHI] + lrint ((wesn[XHI] - h->wesn[XHI] + shift_x) * HH->r_inc[GMT_X]) * h->inc[GMT_X];
2261 wesn[YLO] = h->wesn[YLO] + lrint ((wesn[YLO] - h->wesn[YLO]) * HH->r_inc[GMT_Y]) * h->inc[GMT_Y];
2262 wesn[YHI] = h->wesn[YHI] + lrint ((wesn[YHI] - h->wesn[YHI]) * HH->r_inc[GMT_Y]) * h->inc[GMT_Y];
2263
2264 /* Make sure we do not exceed grid domain (which can happen if GMT->common.R.wesn exceeds the grid limits) */
2265 if (wesn[XLO] < h->wesn[XLO] && !grid_global) wesn[XLO] = h->wesn[XLO];
2266 if (wesn[XHI] > h->wesn[XHI] && !grid_global) wesn[XHI] = h->wesn[XHI];
2267 if (wesn[YLO] < h->wesn[YLO]) wesn[YLO] = h->wesn[YLO];
2268 if (wesn[YHI] > h->wesn[YHI]) wesn[YHI] = h->wesn[YHI];
2269
2270 /* If North or South pole are within the map boundary, we need all longitudes but restrict latitudes */
2271 if (!GMT->current.map.outside (GMT, 0.0, +90.0)) wesn[XLO] = h->wesn[XLO], wesn[XHI] = h->wesn[XHI], wesn[YHI] = h->wesn[YHI];
2272 if (!GMT->current.map.outside (GMT, 0.0, -90.0)) wesn[XLO] = h->wesn[XLO], wesn[XHI] = h->wesn[XHI], wesn[YLO] = h->wesn[YLO];
2273 return (grid_global ? 1 : 2);
2274 }
2275
2276 /* First set and check latitudes since they have no complications */
2277 wesn[YLO] = MAX (h->wesn[YLO], h->wesn[YLO] + floor ((wesn[YLO] - h->wesn[YLO]) * HH->r_inc[GMT_Y] + GMT_CONV4_LIMIT) * h->inc[GMT_Y]);
2278 wesn[YHI] = MIN (h->wesn[YHI], h->wesn[YLO] + ceil ((wesn[YHI] - h->wesn[YLO]) * HH->r_inc[GMT_Y] - GMT_CONV4_LIMIT) * h->inc[GMT_Y]);
2279
2280 if (wesn[YHI] <= wesn[YLO]) { /* Grid must be outside chosen -R */
2281 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your grid y's or latitudes appear to be outside the map region and will be skipped.\n");
2282 return (0);
2283 }
2284
2285 /* Periodic grid with 360 degree range is easy */
2286
2287 if (grid_global) {
2288 bool true_global_region = (gmt_M_360_range (h->wesn[XLO], h->wesn[XHI]) && gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI]));
2289 double x_noise = GMT_CONV4_LIMIT * h->inc[GMT_X];
2290 wesn[XLO] = h->wesn[XLO] + floor ((wesn[XLO] - h->wesn[XLO]) * HH->r_inc[GMT_X] + GMT_CONV4_LIMIT) * h->inc[GMT_X];
2291 wesn[XHI] = h->wesn[XLO] + ceil ((wesn[XHI] - h->wesn[XLO]) * HH->r_inc[GMT_X] - GMT_CONV4_LIMIT) * h->inc[GMT_X];
2292 /* For the odd chance that xmin or xmax are outside the region: bring them in */
2293 if ((wesn[XHI] - wesn[XLO]) >= 360.0) {
2294 while ((wesn[XLO] + x_noise) < GMT->common.R.wesn[XLO]) wesn[XLO] += h->inc[GMT_X];
2295 while ((wesn[XHI]- x_noise) > GMT->common.R.wesn[XHI]) wesn[XHI] -= h->inc[GMT_X];
2296 }
2297 if (true_global_region && (wesn[XHI] - wesn[XLO]) < 360.0) /* Need to enforce 360 range since that is what it should be */
2298 wesn[XHI] = wesn[XLO] + 360.0;
2299 return (1);
2300 }
2301 if (GMT->current.map.is_world) {
2302 wesn[XLO] = h->wesn[XLO], wesn[XHI] = h->wesn[XHI];
2303 return (1);
2304 }
2305
2306 /* Shift a geographic grid 360 degrees up or down to maximize the amount of longitude range */
2307
2308 if (gmt_M_x_is_lon (GMT, GMT_IN)) {
2309 if (gmt_M_360_range (GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI])) {
2310 wesn[XLO] = h->wesn[XLO], wesn[XHI] = h->wesn[XHI];
2311 return (1);
2312 }
2313 x_range = MIN (wesn[XLO], h->wesn[XHI]) - MAX (wesn[XHI], h->wesn[XLO]);
2314 if (MIN (wesn[XLO], h->wesn[XHI] + 360.0) - MAX (wesn[XHI], h->wesn[XLO] + 360.0) > x_range)
2315 shift_x = 360.0;
2316 else if (MIN (wesn[XLO], h->wesn[XHI] - 360.0) - MAX (wesn[XHI], h->wesn[XLO] - 360.0) > x_range)
2317 shift_x = -360.0;
2318 else
2319 shift_x = 0.0;
2320 h->wesn[XLO] += shift_x;
2321 h->wesn[XHI] += shift_x;
2322 }
2323
2324 wesn[XLO] = MAX (h->wesn[XLO], h->wesn[XLO] + floor ((wesn[XLO] - h->wesn[XLO]) * HH->r_inc[GMT_X] + GMT_CONV4_LIMIT) * h->inc[GMT_X]);
2325 wesn[XHI] = MIN (h->wesn[XHI], h->wesn[XLO] + ceil ((wesn[XHI] - h->wesn[XLO]) * HH->r_inc[GMT_X] - GMT_CONV4_LIMIT) * h->inc[GMT_X]);
2326
2327 if (wesn[XHI] <= wesn[XLO]) { /* Grid may is outside chosen -R in longitude */
2328 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Your grid x's or longitudes appear to be outside the map region and will be skipped.\n");
2329 return (0);
2330 }
2331 return (2);
2332 }
2333
gmtlib_adjust_loose_wesn(struct GMT_CTRL * GMT,double wesn[],struct GMT_GRID_HEADER * header,unsigned int verbose_level)2334 GMT_LOCAL int gmtlib_adjust_loose_wesn (struct GMT_CTRL *GMT, double wesn[], struct GMT_GRID_HEADER *header, unsigned int verbose_level) {
2335 /* Used to ensure that sloppy w,e,s,n values are rounded to the gridlines or pixels in the referenced grid.
2336 * Upon entry, the boundaries w,e,s,n are given as a rough approximation of the actual subset needed.
2337 * The routine will limit the boundaries to the grids region and round w,e,s,n to the nearest gridline or
2338 * pixel boundaries (depending on the grid orientation).
2339 * Warnings are produced when the w,e,s,n boundaries are adjusted, so this routine is currently not
2340 * intended to throw just any values at it (although one could).
2341 */
2342
2343 bool global, error = false;
2344 double val, dx, small, was;
2345 char format[GMT_LEN256] = {""};
2346 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
2347
2348 switch (gmt_minmaxinc_verify (GMT, wesn[XLO], wesn[XHI], header->inc[GMT_X], GMT_CONV4_LIMIT)) { /* Check if range is compatible with x_inc */
2349 case 3:
2350 return (GMT_GRDIO_BAD_XINC);
2351 break;
2352 case 2:
2353 return (GMT_GRDIO_BAD_XRANGE);
2354 break;
2355 default:
2356 /* Everything is seemingly OK */
2357 break;
2358 }
2359 switch (gmt_minmaxinc_verify (GMT, wesn[YLO], wesn[YHI], header->inc[GMT_Y], GMT_CONV4_LIMIT)) { /* Check if range is compatible with y_inc */
2360 case 3:
2361 return (GMT_GRDIO_BAD_YINC);
2362 break;
2363 case 2:
2364 return (GMT_GRDIO_BAD_YRANGE);
2365 break;
2366 default:
2367 /* Everything is OK */
2368 break;
2369 }
2370 global = gmt_grd_is_global (GMT, header);
2371
2372 if (!global) {
2373 if (gmt_M_x_is_lon (GMT, GMT_IN)) {
2374 /* If longitudes are all west of range or all east of range, try moving them by 360 degrees east or west */
2375 if (wesn[XHI] < header->wesn[XLO])
2376 wesn[XLO] += 360.0, wesn[XHI] += 360.0;
2377 else if (wesn[XLO] > header->wesn[XHI])
2378 wesn[XLO] -= 360.0, wesn[XHI] -= 360.0;
2379 }
2380 if (header->wesn[XLO] - wesn[XLO] > GMT_CONV4_LIMIT) wesn[XLO] = header->wesn[XLO], error = true;
2381 if (wesn[XHI] - header->wesn[XHI] > GMT_CONV4_LIMIT) wesn[XHI] = header->wesn[XHI], error = true;
2382 }
2383 if (header->wesn[YLO] - wesn[YLO] > GMT_CONV4_LIMIT) wesn[YLO] = header->wesn[YLO], error = true;
2384 if (wesn[YHI] - header->wesn[YHI] > GMT_CONV4_LIMIT) wesn[YHI] = header->wesn[YHI], error = true;
2385 if (error)
2386 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Region exceeds grid domain. Region reduced to grid domain.\n");
2387
2388 /* If new region is not an exact match with grid increment we ensure we extend the region outwards to cover the desired
2389 * region. However, if that takes us outside the grid's region then we backtrack back in */
2390
2391 if (!(gmt_M_x_is_lon (GMT, GMT_IN) && gmt_M_360_range (wesn[XLO], wesn[XHI]) && global)) { /* Do this unless a 360 longitude wrap */
2392 small = GMT_CONV4_LIMIT * header->inc[GMT_X];
2393
2394 val = header->wesn[XLO] + lrint ((wesn[XLO] - header->wesn[XLO]) * HH->r_inc[GMT_X]) * header->inc[GMT_X];
2395 dx = fabs (wesn[XLO] - val);
2396 if (gmt_M_x_is_lon (GMT, GMT_IN)) dx = fmod (dx, 360.0);
2397 if (dx > small) {
2398 was = wesn[XLO];
2399 GMT_Report (GMT->parent, verbose_level,
2400 "(w - x_min) must equal (NX + eps) * x_inc), where NX is an integer and |eps| <= %g.\n", GMT_CONV4_LIMIT);
2401 snprintf (format, GMT_LEN256, "w reset from %s to %s\n",
2402 GMT->current.setting.format_float_out, GMT->current.setting.format_float_out);
2403 wesn[XLO] = val - header->inc[GMT_X];
2404 if (wesn[XLO] < header->wesn[XLO]) val += header->inc[GMT_X];
2405 GMT_Report (GMT->parent, verbose_level, format, was, wesn[XLO]);
2406 }
2407
2408 val = header->wesn[XLO] + lrint ((wesn[XHI] - header->wesn[XLO]) * HH->r_inc[GMT_X]) * header->inc[GMT_X];
2409 dx = fabs (wesn[XHI] - val);
2410 if (gmt_M_x_is_lon (GMT, GMT_IN)) dx = fmod (dx, 360.0);
2411 if (dx > small) {
2412 was = wesn[XHI];
2413 GMT_Report (GMT->parent, verbose_level,
2414 "(e - x_min) must equal (NX + eps) * x_inc), where NX is an integer and |eps| <= %g.\n", GMT_CONV4_LIMIT);
2415 snprintf (format, GMT_LEN256, "e reset from %s to %s\n",
2416 GMT->current.setting.format_float_out, GMT->current.setting.format_float_out);
2417 wesn[XHI] = val + header->inc[GMT_X];
2418 if (wesn[XHI] > header->wesn[XHI]) val -= header->inc[GMT_X];
2419 GMT_Report (GMT->parent, verbose_level, format, was, wesn[XHI]);
2420 }
2421 }
2422
2423 /* Check if s,n are a multiple of y_inc offset from y_min - if not adjust s, n */
2424 small = GMT_CONV4_LIMIT * header->inc[GMT_Y];
2425
2426 val = header->wesn[YLO] + lrint ((wesn[YLO] - header->wesn[YLO]) * HH->r_inc[GMT_Y]) * header->inc[GMT_Y];
2427 if (fabs (wesn[YLO] - val) > small) {
2428 was = wesn[YLO];
2429 GMT_Report (GMT->parent, verbose_level, "(s - y_min) must equal (NY + eps) * y_inc), where NY is an integer and |eps| <= %g.\n",
2430 GMT_CONV4_LIMIT);
2431 snprintf (format, GMT_LEN256, "s reset from %s to %s\n",
2432 GMT->current.setting.format_float_out, GMT->current.setting.format_float_out);
2433 wesn[YLO] = val - header->inc[GMT_Y];
2434 if (wesn[YLO] < header->wesn[YLO]) val += header->inc[GMT_Y];
2435 GMT_Report (GMT->parent, verbose_level, format, was, wesn[YLO]);
2436 }
2437
2438 val = header->wesn[YLO] + lrint ((wesn[YHI] - header->wesn[YLO]) * HH->r_inc[GMT_Y]) * header->inc[GMT_Y];
2439 if (fabs (wesn[YHI] - val) > small) {
2440 was = wesn[YHI];
2441 GMT_Report (GMT->parent, verbose_level, "(n - y_min) must equal (NY + eps) * y_inc), where NY is an integer and |eps| <= %g.\n",
2442 GMT_CONV4_LIMIT);
2443 snprintf (format, GMT_LEN256, "n reset from %s to %s\n",
2444 GMT->current.setting.format_float_out, GMT->current.setting.format_float_out);
2445 wesn[YHI] = val + header->inc[GMT_Y];
2446 if (wesn[YHI] > header->wesn[YHI]) val -= header->inc[GMT_Y];
2447 GMT_Report (GMT->parent, verbose_level, format, was, wesn[YHI]);
2448 }
2449 return (GMT_NOERROR);
2450 }
2451
gmt_adjust_loose_wesn(struct GMT_CTRL * GMT,double wesn[],struct GMT_GRID_HEADER * header)2452 int gmt_adjust_loose_wesn (struct GMT_CTRL *GMT, double wesn[], struct GMT_GRID_HEADER *header) {
2453 /* Most places we wish to warn if w/e/s/n is sloppy except when via DCW that must be rounded */
2454 return gmtlib_adjust_loose_wesn (GMT, wesn, header, GMT->common.R.via_polygon ? GMT_MSG_INFORMATION : GMT_MSG_WARNING);
2455 }
2456
gmt_scale_and_offset_f(struct GMT_CTRL * GMT,gmt_grdfloat * data,size_t length,double scale,double offset)2457 void gmt_scale_and_offset_f (struct GMT_CTRL *GMT, gmt_grdfloat *data, size_t length, double scale, double offset) {
2458 /* Routine that does the data conversion and sanity checking before
2459 * calling scale_and_offset_f() to scale and offset the data in a grid */
2460 gmt_grdfloat scale_f = (gmt_grdfloat)scale;
2461 gmt_grdfloat offset_f = (gmt_grdfloat)offset;
2462
2463 if (scale_f == 1.0 && offset_f == 0.0)
2464 return; /* No work needed */
2465
2466 /* Sanity checks */
2467 if (!isnormal (scale)) {
2468 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Scale must be a non-zero normalized number (%g).\n", scale);
2469 scale_f = 1.0f;
2470 }
2471 if (!isfinite (offset)) {
2472 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Offset must be a finite number (%g).\n", offset);
2473 offset_f = 0.0f;
2474 }
2475
2476 /* Call workhorse */
2477 scale_and_offset_f (data, length, scale_f, offset_f);
2478 }
2479
gmt_read_img(struct GMT_CTRL * GMT,char * imgfile,struct GMT_GRID * Grid,double * in_wesn,double scale,unsigned int mode,double lat,bool init)2480 int gmt_read_img (struct GMT_CTRL *GMT, char *imgfile, struct GMT_GRID *Grid, double *in_wesn, double scale, unsigned int mode, double lat, bool init) {
2481 /* Function that reads an entire Sandwell/Smith Mercator grid and stores it like a regular
2482 * GMT grid. If init is true we also initialize the Mercator projection. Lat should be 0.0
2483 * if we are dealing with standard 72 or 80 img latitude; else it must be specified.
2484 */
2485
2486 int status, first_i;
2487 unsigned int min, actual_col, n_cols, row, col, first;
2488 uint64_t ij;
2489 off_t n_skip;
2490 int16_t *i2 = NULL;
2491 uint16_t *u2 = NULL;
2492 char file[PATH_MAX];
2493 struct stat buf;
2494 FILE *fp = NULL;
2495 double wesn[4], wesn_all[4];
2496 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (Grid->header);
2497
2498 first = gmt_download_file_if_not_found (GMT, imgfile, GMT_CACHE_DIR);
2499 if (!gmt_getdatapath (GMT, &imgfile[first], file, R_OK)) return (GMT_GRDIO_FILE_NOT_FOUND);
2500 if (stat (file, &buf)) return (GMT_GRDIO_STAT_FAILED); /* Inquiry about file failed somehow */
2501
2502 switch (buf.st_size) { /* Known sizes are 1 or 2 min at lat_max = ~72, ~80, or ~85. Set exact latitude */
2503 case GMT_IMG_NLON_1M*GMT_IMG_NLAT_1M_85*GMT_IMG_ITEMSIZE:
2504 lat = GMT_IMG_MAXLAT_85;
2505 min = 1;
2506 break;
2507 case GMT_IMG_NLON_1M*GMT_IMG_NLAT_1M_80*GMT_IMG_ITEMSIZE:
2508 lat = GMT_IMG_MAXLAT_80;
2509 min = 1;
2510 break;
2511 case GMT_IMG_NLON_1M*GMT_IMG_NLAT_1M_72*GMT_IMG_ITEMSIZE:
2512 lat = GMT_IMG_MAXLAT_72;
2513 min = 1;
2514 break;
2515 case GMT_IMG_NLON_2M*GMT_IMG_NLAT_2M_85*GMT_IMG_ITEMSIZE:
2516 lat = GMT_IMG_MAXLAT_85;
2517 min = 2;
2518 break;
2519 case GMT_IMG_NLON_2M*GMT_IMG_NLAT_2M_80*GMT_IMG_ITEMSIZE:
2520 lat = GMT_IMG_MAXLAT_80;
2521 min = 2;
2522 break;
2523 case GMT_IMG_NLON_2M*GMT_IMG_NLAT_2M_72*GMT_IMG_ITEMSIZE:
2524 lat = GMT_IMG_MAXLAT_72;
2525 min = 2;
2526 break;
2527 case GMT_IMG_NLON_4M*GMT_IMG_NLAT_4M_72*GMT_IMG_ITEMSIZE: /* Test grids only */
2528 lat = GMT_IMG_MAXLAT_72;
2529 min = 4;
2530 break;
2531 default:
2532 if (lat == 0.0) return (GMT_GRDIO_BAD_IMG_LAT);
2533 min = (buf.st_size > GMT_IMG_NLON_2M*GMT_IMG_NLAT_2M_80*GMT_IMG_ITEMSIZE) ? 1 : 2;
2534 GMT_Report (GMT->parent, GMT_MSG_WARNING, "img file %s has unusual size - grid increment defaults to %d min\n", file, min);
2535 break;
2536 }
2537
2538 wesn_all[XLO] = GMT_IMG_MINLON; wesn_all[XHI] = GMT_IMG_MAXLON;
2539 wesn_all[YLO] = -lat; wesn_all[YHI] = lat;
2540 if (!in_wesn || (in_wesn[XLO] == in_wesn[XHI] && in_wesn[YLO] == in_wesn[YHI])) { /* Default is entire grid */
2541 gmt_M_memcpy (wesn, wesn_all, 4, double);
2542 }
2543 else /* Use specified subset */
2544 gmt_M_memcpy (wesn, in_wesn, 4, double);
2545
2546 if ((fp = gmt_fopen (GMT, file, "rb")) == NULL) return (GMT_GRDIO_OPEN_FAILED);
2547
2548 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Reading img grid from file %s (scale = %g mode = %d lat = %g)\n",
2549 &imgfile[first], scale, mode, lat);
2550 Grid->header->inc[GMT_X] = Grid->header->inc[GMT_Y] = min / 60.0;
2551
2552 if (init) {
2553 /* Select plain Mercator on a sphere with -Jm1 -R0/360/-lat/+lat */
2554 GMT->current.setting.proj_ellipsoid = gmt_get_ellipsoid (GMT, "Sphere");
2555 GMT->current.proj.units_pr_degree = true;
2556 GMT->current.proj.pars[0] = 180.0;
2557 GMT->current.proj.pars[1] = 0.0;
2558 GMT->current.proj.pars[2] = 1.0;
2559 GMT->current.proj.projection = GMT->current.proj.projection_GMT = GMT_MERCATOR;
2560 gmt_set_geographic (GMT, GMT_IN);
2561 GMT->common.J.active = true;
2562
2563 gmt_M_err_pass (GMT, gmt_proj_setup (GMT, wesn_all), file);
2564 }
2565
2566 if (wesn[XLO] < 0.0 && wesn[XHI] < 0.0) wesn[XLO] += 360.0, wesn[XHI] += 360.0;
2567
2568 /* Project lon/lat boundaries to Mercator units */
2569 gmt_geo_to_xy (GMT, wesn[XLO], wesn[YLO], &Grid->header->wesn[XLO], &Grid->header->wesn[YLO]);
2570 gmt_geo_to_xy (GMT, wesn[XHI], wesn[YHI], &Grid->header->wesn[XHI], &Grid->header->wesn[YHI]);
2571
2572 /* Adjust boundaries to multiples of increments, making sure we are inside bounds */
2573 Grid->header->wesn[XLO] = MAX (GMT_IMG_MINLON, floor (Grid->header->wesn[XLO] / Grid->header->inc[GMT_X]) * Grid->header->inc[GMT_X]);
2574 Grid->header->wesn[XHI] = MIN (GMT_IMG_MAXLON, ceil (Grid->header->wesn[XHI] / Grid->header->inc[GMT_X]) * Grid->header->inc[GMT_X]);
2575 if (Grid->header->wesn[XLO] > Grid->header->wesn[XHI]) Grid->header->wesn[XLO] -= 360.0;
2576 Grid->header->wesn[YLO] = MAX (0.0, floor (Grid->header->wesn[YLO] / Grid->header->inc[GMT_Y]) * Grid->header->inc[GMT_Y]);
2577 Grid->header->wesn[YHI] = MIN (GMT->current.proj.rect[YHI], ceil (Grid->header->wesn[YHI] / Grid->header->inc[GMT_Y]) * Grid->header->inc[GMT_Y]);
2578 /* Allocate grid memory */
2579
2580 Grid->header->registration = GMT_GRID_PIXEL_REG; /* These are always pixel grids */
2581 if ((status = gmt_grd_RI_verify (GMT, Grid->header, 1))) { /* Final verification of -R -I; return error if we must */
2582 gmt_fclose (GMT, fp);
2583 return (status);
2584 }
2585 gmt_M_grd_setpad (GMT, Grid->header, GMT->current.io.pad); /* Assign default pad */
2586 gmt_set_grddim (GMT, Grid->header); /* Set all dimensions before returning */
2587 Grid->data = gmt_M_memory_aligned (GMT, NULL, Grid->header->size, gmt_grdfloat);
2588
2589 n_cols = (min == 1) ? GMT_IMG_NLON_1M : GMT_IMG_NLON_2M; /* Number of columns (10800 or 21600) */
2590 first_i = irint (floor (Grid->header->wesn[XLO] * HH->r_inc[GMT_X])); /* first tile partly or fully inside region */
2591 if (first_i < 0) first_i += n_cols;
2592 n_skip = lrint (floor ((GMT->current.proj.rect[YHI] - Grid->header->wesn[YHI]) * HH->r_inc[GMT_Y])); /* Number of rows clearly above y_max */
2593 if (fseek (fp, n_skip * n_cols * GMT_IMG_ITEMSIZE, SEEK_SET)) {
2594 gmt_fclose (GMT, fp);
2595 return (GMT_GRDIO_SEEK_FAILED);
2596 }
2597
2598 i2 = gmt_M_memory (GMT, NULL, n_cols, int16_t);
2599 for (row = 0; row < Grid->header->n_rows; row++) { /* Read all the rows, offset by 2 boundary rows and cols */
2600 if (gmt_M_fread (i2, sizeof (int16_t), n_cols, fp) != n_cols) {
2601 gmt_M_free (GMT, i2);
2602 gmt_fclose (GMT, fp);
2603 return (GMT_GRDIO_READ_FAILED); /* Failed to get one row */
2604 }
2605 #ifndef WORDS_BIGENDIAN
2606 u2 = (uint16_t *)i2;
2607 for (col = 0; col < n_cols; col++)
2608 u2[col] = bswap16 (u2[col]);
2609 #endif
2610 #ifdef DEBUG
2611 if (row == 0) {
2612 uint16_t step, max_step = 0;
2613 for (col = 1; col < n_cols; col++) {
2614 step = abs (i2[col] - i2[col-1]);
2615 if (step > max_step) max_step = step;
2616 }
2617 if (max_step > 32768)
2618 GMT_Report (GMT->parent, GMT_MSG_WARNING, "File %s probably needs to byteswapped (max change = %u)\n", file, max_step);
2619 }
2620 #endif
2621 ij = gmt_M_ijp (Grid->header, row, 0);
2622 for (col = 0, actual_col = first_i; col < Grid->header->n_columns; col++) { /* Process this row's values */
2623 switch (mode) {
2624 case 0: /* No encoded track flags, do nothing */
2625 break;
2626 case 1: /* Remove the track flag on odd (constrained) points */
2627 if (i2[actual_col]%2) i2[actual_col]--;
2628 break;
2629 case 2: /* Remove the track flag on odd (constrained) points and set unconstrained to NaN */
2630 i2[actual_col] = (i2[actual_col]%2) ? i2[actual_col] - 1 : SHRT_MIN;
2631 break;
2632 case 3: /* Set odd (constrained) points to 1 and set unconstrained to 0 */
2633 i2[actual_col] %= 2;
2634 break;
2635 }
2636 Grid->data[ij+col] = (gmt_grdfloat)((mode == 3) ? i2[actual_col] : (i2[actual_col] * scale));
2637 if (++actual_col == n_cols) actual_col = 0; /* Wrapped around 360 */
2638 }
2639 }
2640 gmt_M_free (GMT, i2);
2641 gmt_fclose (GMT, fp);
2642 if (init) {
2643 gmt_M_memcpy (GMT->common.R.wesn, wesn, 4, double);
2644 GMT->common.J.active = false;
2645 }
2646 gmt_BC_init (GMT, Grid->header); /* Initialize grid interpolation and boundary condition parameters */
2647 gmt_grd_BC_set (GMT, Grid, GMT_IN); /* Set boundary conditions */
2648 HH->has_NaNs = GMT_GRID_NO_NANS; /* No nans in img grids */
2649 return (GMT_NOERROR);
2650 }
2651
gmt_cube_pad_off(struct GMT_CTRL * GMT,struct GMT_CUBE * U)2652 void gmt_cube_pad_off (struct GMT_CTRL *GMT, struct GMT_CUBE *U) {
2653 /* Shifts the cube contents so there is no pad. The remainder of
2654 * the array is not reset and should not be addressed, but
2655 * we set it to zero just in case.
2656 * If pad is zero then we do nothing.
2657 */
2658 uint64_t k, ijp, ij0, nm, here_p = 0, here_0 = 0;
2659 unsigned int row;
2660 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (U->header);
2661
2662 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
2663 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Calling gmt_cube_pad_off on interleaved complex grid! Programming error?\n");
2664 return;
2665 }
2666 if (!gmt_grd_pad_status (GMT, U->header, NULL)) return; /* No pad so nothing to do */
2667
2668 /* Here, U has a pad which we need to eliminate */
2669 nm = U->header->nm; /* Number of nodes in one component */
2670 for (k = 0; k < U->header->n_bands; k++) {
2671 for (row = 0; row < U->header->n_rows; row++) {
2672 ijp = gmt_M_ijp (U->header, row, 0) + here_p; /* Index of start of this row's first column in padded cube */
2673 ij0 = gmt_M_ij0 (U->header, row, 0) + here_0; /* Index of start of this row's first column in unpadded cube */
2674 gmt_M_memcpy (&(U->data[ij0]), &(U->data[ijp]), U->header->n_columns, gmt_grdfloat); /* Only copy the n_columns data values */
2675 }
2676 here_p += U->header->size;
2677 here_0 += nm;
2678 }
2679 if (here_p > here_0) { /* Just wipe the remainder of the array to be sure */
2680 size_t n_to_cleen = here_p - here_0;
2681 gmt_M_memset (&(U->data[nm*U->header->n_bands]), n_to_cleen, gmt_grdfloat); /* nm*U->header->n_bands is 1st position after last row in cube */
2682 }
2683 gmt_M_memset (U->header->pad, 4, int); /* Pad is no longer active */
2684 gmt_set_grddim (GMT, U->header); /* Update all dimensions to reflect the padding */
2685 }
2686
gmt_grd_pad_off(struct GMT_CTRL * GMT,struct GMT_GRID * G)2687 void gmt_grd_pad_off (struct GMT_CTRL *GMT, struct GMT_GRID *G) {
2688 /* Shifts the grid contents so there is no pad. The remainder of
2689 * the array is not reset and should not be addressed, but
2690 * we set it to zero just in case.
2691 * If pad is zero then we do nothing.
2692 */
2693 bool is_complex;
2694 uint64_t nm;
2695 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
2696
2697 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
2698 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Calling gmt_grd_pad_off on interleaved complex grid! Programming error?\n");
2699 return;
2700 }
2701 if (!gmt_grd_pad_status (GMT, G->header, NULL)) return; /* No pad so nothing to do */
2702
2703 /* Here, G has a pad which we need to eliminate */
2704 is_complex = (G->header->complex_mode & GMT_GRID_IS_COMPLEX_MASK);
2705 if (!is_complex || (G->header->complex_mode & GMT_GRID_IS_COMPLEX_REAL))
2706 gmtgrdio_pad_grd_off_sub (G, G->data); /* Remove pad around real component only or entire normal grid */
2707 if (is_complex && (G->header->complex_mode & GMT_GRID_IS_COMPLEX_IMAG))
2708 gmtgrdio_pad_grd_off_sub (G, &G->data[G->header->size/2]); /* Remove pad around imaginary component */
2709 nm = G->header->nm; /* Number of nodes in one component */
2710 if (is_complex) nm *= 2; /* But there might be two */
2711 if (G->header->size > nm) { /* Just wipe the remaineder of the array to be sure */
2712 size_t n_to_cleen = G->header->size - nm;
2713 gmt_M_memset (&(G->data[nm]), n_to_cleen, gmt_grdfloat); /* nm is 1st position after last row */
2714 }
2715 gmt_M_memset (G->header->pad, 4, int); /* Pad is no longer active */
2716 gmt_set_grddim (GMT, G->header); /* Update all dimensions to reflect the padding */
2717 }
2718
gmt_grd_pad_on(struct GMT_CTRL * GMT,struct GMT_GRID * G,unsigned int * pad)2719 void gmt_grd_pad_on (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigned int *pad) {
2720 /* Shift grid content from a non-padded (or differently padded) to a padded organization.
2721 * We check that the grid size can handle this and allocate more space if needed.
2722 * If pad matches the grid's pad then we do nothing.
2723 */
2724 bool is_complex;
2725 size_t size;
2726 struct GMT_GRID_HEADER *h = NULL;
2727 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
2728
2729 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
2730 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Calling gmt_grd_pad_off on interleaved complex grid! Programming error?\n");
2731 return;
2732 }
2733 if (gmt_grd_pad_status (GMT, G->header, pad)) return; /* Already padded as requested so nothing to do */
2734 if (pad[XLO] == 0 && pad[XHI] == 0 && pad[YLO] == 0 && pad[YHI] == 0) { /* Just remove the existing pad entirely */
2735 gmt_grd_pad_off (GMT, G);
2736 return;
2737 }
2738 /* Here the pads differ (or G has no pad at all) */
2739 is_complex = (G->header->complex_mode & GMT_GRID_IS_COMPLEX_MASK);
2740 size = ((size_t)gmt_M_grd_get_nxpad (G->header, pad)) * ((size_t)gmt_M_grd_get_nypad (G->header, pad)); /* New array size after pad is added */
2741 if (is_complex) size *= 2; /* Twice the space for complex grids */
2742 if (size > G->header->size) { /* Must allocate more space, but since no realloc for aligned memory we must do it the hard way */
2743 gmt_grdfloat *f = NULL;
2744 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Extend grid via copy onto larger memory-aligned grid\n");
2745 f = gmt_M_memory_aligned (GMT, NULL, size, gmt_grdfloat); /* New, larger grid size */
2746 gmt_M_memcpy (f, G->data, G->header->size, gmt_grdfloat); /* Copy over previous grid values */
2747 gmt_M_free_aligned (GMT, G->data); /* Free previous aligned grid memory */
2748 G->data = f; /* Attach the new, larger aligned memory */
2749 G->header->size = size; /* Update the size */
2750 }
2751 /* Because G may have a pad that is nonzero (but different from pad) we need a different header structure in the macros below */
2752 h = gmtgrdio_duplicate_gridheader (GMT, G->header);
2753
2754 gmt_M_grd_setpad (GMT, G->header, pad); /* G->header->pad is now set to specified dimensions in pad */
2755 gmt_set_grddim (GMT, G->header); /* Update all dimensions to reflect the new padding */
2756 if (is_complex && (G->header->complex_mode & GMT_GRID_IS_COMPLEX_IMAG))
2757 gmtgrdio_pad_grd_on_sub (GMT, G, h, &G->data[size/2]); /* Add pad around imaginary component first */
2758 if (!is_complex || (G->header->complex_mode & GMT_GRID_IS_COMPLEX_REAL))
2759 gmtgrdio_pad_grd_on_sub (GMT, G, h, G->data); /* Add pad around real component */
2760 gmt_M_free (GMT, h->hidden); /* Done with this header hidden struct */
2761 gmt_M_free (GMT, h); /* Done with this header */
2762 }
2763
gmt_grd_pad_zero(struct GMT_CTRL * GMT,struct GMT_GRID * G)2764 void gmt_grd_pad_zero (struct GMT_CTRL *GMT, struct GMT_GRID *G) {
2765 /* Sets all boundary row/col nodes to zero and sets
2766 * the header->BC to GMT_IS_NOTSET.
2767 */
2768 bool is_complex;
2769 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
2770
2771 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
2772 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Calling gmt_grd_pad_off on interleaved complex grid! Programming error?\n");
2773 return;
2774 }
2775 if (!gmt_grd_pad_status (GMT, G->header, NULL)) return; /* No pad so nothing to do */
2776 if (HH->BC[XLO] == GMT_BC_IS_NOTSET && HH->BC[XHI] == GMT_BC_IS_NOTSET && HH->BC[YLO] ==
2777 GMT_BC_IS_NOTSET && HH->BC[YHI] == GMT_BC_IS_NOTSET)
2778 return; /* No BCs set so nothing to do */ /* No pad so nothing to do */
2779 /* Here, G has a pad with BCs which we need to reset */
2780 is_complex = (G->header->complex_mode & GMT_GRID_IS_COMPLEX_MASK);
2781 if (!is_complex || (G->header->complex_mode & GMT_GRID_IS_COMPLEX_REAL))
2782 gmtgrdio_pad_grd_zero_sub (G, G->data);
2783 if (is_complex && (G->header->complex_mode & GMT_GRID_IS_COMPLEX_IMAG))
2784 gmtgrdio_pad_grd_zero_sub (G, &G->data[G->header->size/2]);
2785 gmt_M_memset (HH->BC, 4U, int); /* BCs no longer set for this grid */
2786 }
2787
gmt_get_grid(struct GMT_CTRL * GMT)2788 struct GMT_GRID *gmt_get_grid (struct GMT_CTRL *GMT) {
2789 struct GMT_GRID *G = NULL;
2790 G = gmt_M_memory (GMT, NULL, 1, struct GMT_GRID);
2791 G->hidden = gmt_M_memory (GMT, NULL, 1, struct GMT_GRID_HIDDEN);
2792 return (G);
2793 }
2794
gmt_create_grid(struct GMT_CTRL * GMT)2795 struct GMT_GRID *gmt_create_grid (struct GMT_CTRL *GMT) {
2796 /* Allocates space for a new grid container. No space allocated for the gmt_grdfloat grid itself */
2797 struct GMT_GRID *G = NULL;
2798 struct GMT_GRID_HIDDEN *GH = NULL;
2799
2800 G = gmt_get_grid (GMT);
2801 GH = gmt_get_G_hidden (G);
2802 G->header = gmt_get_header (GMT);
2803 gmt_grd_init (GMT, G->header, NULL, false); /* Set default values */
2804 GMT_Set_Index (GMT->parent, G->header, GMT_GRID_LAYOUT);
2805 #ifdef DOUBLE_PRECISION_GRID
2806 G->header->type = GMT_GRID_IS_ND;
2807 #else
2808 G->header->type = GMT_GRID_IS_NF;
2809 #endif
2810 GH->alloc_mode = GMT_ALLOC_INTERNALLY; /* Memory can be freed by GMT. */
2811 GH->alloc_level = GMT->hidden.func_level; /* Must be freed at this level. */
2812 GH->id = GMT->parent->unique_var_ID++; /* Give unique identifier */
2813 return (G);
2814 }
2815
gmt_duplicate_grid(struct GMT_CTRL * GMT,struct GMT_GRID * G,unsigned int mode)2816 struct GMT_GRID *gmt_duplicate_grid (struct GMT_CTRL *GMT, struct GMT_GRID *G, unsigned int mode) {
2817 /* Duplicates an entire grid, including data if requested. */
2818 struct GMT_GRID *Gnew = NULL;
2819
2820 Gnew = gmt_create_grid (GMT);
2821 gmt_copy_gridheader (GMT, Gnew->header, G->header);
2822
2823 if ((mode & GMT_DUPLICATE_DATA) || (mode & GMT_DUPLICATE_ALLOC)) { /* Also allocate and possibly duplicate data array */
2824 struct GMT_GRID_HIDDEN *GH = gmt_get_G_hidden (Gnew);
2825 if ((mode & GMT_DUPLICATE_RESET) && !gmt_grd_pad_status (GMT, G->header, GMT->current.io.pad)) {
2826 /* Pads differ and we requested resetting the pad */
2827 gmt_M_grd_setpad (GMT, Gnew->header, GMT->current.io.pad); /* Set default pad size */
2828 gmt_set_grddim (GMT, Gnew->header); /* Update size dimensions given the change of pad */
2829 if (mode & GMT_DUPLICATE_DATA) { /* Per row since grid sizes will not the same */
2830 uint64_t node_in, node_out;
2831 unsigned int row;
2832 Gnew->data = gmt_M_memory_aligned (GMT, NULL, Gnew->header->size, gmt_grdfloat);
2833 gmt_M_row_loop (GMT, G, row) {
2834 node_in = gmt_M_ijp (G->header, row, 0);
2835 node_out = gmt_M_ijp (Gnew->header, row, 0);
2836 gmt_M_memcpy (&Gnew->data[node_out], &G->data[node_in], G->header->n_columns, gmt_grdfloat);
2837 }
2838 }
2839 }
2840 else { /* Can do fast copy */
2841 Gnew->data = gmt_M_memory_aligned (GMT, NULL, G->header->size, gmt_grdfloat);
2842 if (mode & GMT_DUPLICATE_DATA) gmt_M_memcpy (Gnew->data, G->data, G->header->size, gmt_grdfloat);
2843 }
2844
2845 Gnew->x = gmt_grd_coord (GMT, Gnew->header, GMT_X); /* Get array of x coordinates */
2846 Gnew->y = gmt_grd_coord (GMT, Gnew->header, GMT_Y); /* Get array of y coordinates */
2847 GH->xy_alloc_mode[GMT_X] = GH->xy_alloc_mode[GMT_Y] = GMT_ALLOC_INTERNALLY;
2848 }
2849 return (Gnew);
2850 }
2851
gmt_free_header(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER ** header)2852 void gmt_free_header (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER **header) {
2853 struct GMT_GRID_HEADER_HIDDEN *HH = NULL;
2854 struct GMT_GRID_HEADER *h = *header;
2855 if (h == NULL) return; /* Nothing to deallocate */
2856 /* Free the header structure and anything allocated by it */
2857 HH = gmt_get_H_hidden (h);
2858 if (!GMT->parent->external) {
2859 gmt_M_str_free (h->ProjRefWKT);
2860 gmt_M_str_free (h->ProjRefPROJ4);
2861 }
2862 gmt_M_str_free (HH->pocket);
2863 gmt_M_free (GMT, h->hidden);
2864 gmt_M_free (GMT, *header);
2865 }
2866
gmtlib_free_grid_ptr(struct GMT_CTRL * GMT,struct GMT_GRID * G,bool free_grid)2867 unsigned int gmtlib_free_grid_ptr (struct GMT_CTRL *GMT, struct GMT_GRID *G, bool free_grid) {
2868 /* By taking a reference to the grid pointer we can set it to NULL when done */
2869 struct GMT_GRID_HIDDEN *GH = NULL;
2870 enum GMT_enum_alloc alloc_mode;
2871 if (!G) return 0; /* Nothing to deallocate */
2872 /* Only free G->data if allocated by GMT AND free_grid is true */
2873 GH = gmt_get_G_hidden (G);
2874 if (G->data && free_grid) {
2875 if (GH->alloc_mode == GMT_ALLOC_INTERNALLY) gmt_M_free_aligned (GMT, G->data);
2876 G->data = NULL; /* This will remove reference to external memory since gmt_M_free_aligned would not have been called */
2877 }
2878 if (G->x && GH->xy_alloc_mode[GMT_X] == GMT_ALLOC_INTERNALLY)
2879 gmt_M_free (GMT, G->x);
2880 if (G->y && GH->xy_alloc_mode[GMT_Y] == GMT_ALLOC_INTERNALLY)
2881 gmt_M_free (GMT, G->y);
2882 G->x = G->y = NULL; /* This will remove reference to external memory since gmt_M_free would not have been called */
2883 if (GH->extra) gmtlib_close_grd (GMT, G); /* Close input file used for row-by-row i/o */
2884 alloc_mode = GH->alloc_mode;
2885 gmt_M_free (GMT, G->hidden);
2886 gmt_free_header (GMT, &(G->header)); /* Free the header structure and anything allocated by it */
2887 return (alloc_mode);
2888 }
2889
gmt_free_grid(struct GMT_CTRL * GMT,struct GMT_GRID ** G,bool free_grid)2890 void gmt_free_grid (struct GMT_CTRL *GMT, struct GMT_GRID **G, bool free_grid) {
2891 /* By taking a reference to the grid pointer we can set it to NULL when done */
2892 (void)gmtlib_free_grid_ptr (GMT, *G, free_grid);
2893 gmt_M_free (GMT, *G);
2894 }
2895
gmt_set_outgrid(struct GMT_CTRL * GMT,char * file,bool separate,unsigned int min_pad,struct GMT_GRID * G,struct GMT_GRID ** Out)2896 int gmt_set_outgrid (struct GMT_CTRL *GMT, char *file, bool separate, unsigned int min_pad, struct GMT_GRID *G, struct GMT_GRID **Out) {
2897 /* In most situations we can recycle the input grid to be the output grid as well. However, there
2898 * are a few situations when we must override this situation:
2899 * 1) When OpenMP is enabled and calculations depend on nearby nodes.
2900 * 2) The input grid is a read-only memory location
2901 * 3) The intended output file is a memory location.
2902 * To avoid wasting memory we try to reuse the input array when
2903 * it is possible. We return true when new memory had to be allocated.
2904 * Note we duplicate the grid if we must so that Out always has the input
2905 * data in it (directly or via the pointer).
2906 * If the selected grid has a smaller pad than min_pad then we extend it to min_pad */
2907 bool add_pad = false;
2908 unsigned int k, pad[4] = {min_pad, min_pad, min_pad, min_pad};
2909 struct GMT_GRID_HIDDEN *GH = gmt_get_G_hidden (G);
2910
2911 for (k = 0; !add_pad && k < 4; k++)
2912 if (G->header->pad[k] < min_pad) add_pad = true;
2913 if (separate || gmt_M_file_is_memory (file) || GH->alloc_mode == GMT_ALLOC_EXTERNALLY) { /* Cannot store results in a non-GMT read-only input array */
2914 if ((*Out = GMT_Duplicate_Data (GMT->parent, GMT_IS_GRID, GMT_DUPLICATE_DATA, G)) == NULL) {
2915 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Unable to duplicate grid! - this is not a good thing and may crash this module\n");
2916 (*Out) = G;
2917 }
2918 else {
2919 struct GMT_GRID_HIDDEN *GH = gmt_get_G_hidden (*Out);
2920 GH->alloc_mode = GMT_ALLOC_INTERNALLY;
2921 if (add_pad) {
2922 gmt_grd_pad_on (GMT, *Out, pad); /* Add pad */
2923 gmt_BC_init (GMT, (*Out)->header); /* Initialize grid interpolation and boundary condition parameters */
2924 gmt_grd_BC_set (GMT, *Out, GMT_IN); /* Set boundary conditions */
2925 }
2926 }
2927 return (true);
2928 }
2929 /* Here we may overwrite the input grid and just pass the pointer back */
2930 (*Out) = G;
2931 if (add_pad) {
2932 gmt_grd_pad_on (GMT, *Out, pad); /* Add pad */
2933 gmt_BC_init (GMT, (*Out)->header); /* Initialize grid interpolation and boundary condition parameters */
2934 gmt_grd_BC_set (GMT, *Out, GMT_IN); /* Set boundary conditions */
2935 }
2936 return (false);
2937 }
2938
gmt_change_grdreg(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,unsigned int registration)2939 int gmt_change_grdreg (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, unsigned int registration) {
2940 unsigned int old_registration;
2941 double F;
2942 gmt_M_unused(GMT);
2943 /* Adjust the grid header to the selected registration, if different.
2944 * In all cases we return the original registration. */
2945
2946 old_registration = header->registration;
2947 if (old_registration == registration) return (old_registration); /* Noting to do */
2948
2949 F = (header->registration == GMT_GRID_PIXEL_REG) ? 0.5 : -0.5; /* Pixel will shrink w/e/s/n, gridline will extend */
2950 header->wesn[XLO] += F * header->inc[GMT_X];
2951 header->wesn[XHI] -= F * header->inc[GMT_X];
2952 header->wesn[YLO] += F * header->inc[GMT_Y];
2953 header->wesn[YHI] -= F * header->inc[GMT_Y];
2954
2955 header->registration = registration;
2956 header->xy_off = 0.5 * header->registration;
2957 return (old_registration);
2958 }
2959
gmt_cube_vminmax(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,gmt_grdfloat * z)2960 void gmt_cube_vminmax (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, gmt_grdfloat *z) {
2961 /* Reset the vmin/vmax values in the header based on the non-NaN values in the cube */
2962 unsigned int row, col, layer;
2963 uint64_t node, offset = 0, n = 0;
2964
2965 h->z_min = DBL_MAX; h->z_max = -DBL_MAX;
2966 for (layer = 0; layer < h->n_bands; layer++) {
2967 for (row = 0; row < h->n_rows; row++) {
2968 for (col = 0, node = gmt_M_ijp (h, row, 0) + offset; col < h->n_columns; col++, node++) {
2969 if (isnan (z[node]))
2970 continue;
2971 /* Update v_min, v_max (called z_min/max in header) */
2972 h->z_min = MIN (h->z_min, (double)z[node]);
2973 h->z_max = MAX (h->z_max, (double)z[node]);
2974 n++;
2975 }
2976 }
2977 offset += h->size; /* Go to next layer */
2978 }
2979 if (n == 0) h->z_min = h->z_max = GMT->session.d_NaN; /* No non-NaNs in the entire grid */
2980 }
2981
gmt_grd_zminmax(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h,gmt_grdfloat * z)2982 void gmt_grd_zminmax (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h, gmt_grdfloat *z) {
2983 /* Reset the zmin/zmax values in the header based on the non-NaN values in the grid */
2984 unsigned int row, col;
2985 uint64_t node, n = 0;
2986
2987 h->z_min = DBL_MAX; h->z_max = -DBL_MAX;
2988 for (row = 0; row < h->n_rows; row++) {
2989 for (col = 0, node = gmt_M_ijp (h, row, 0); col < h->n_columns; col++, node++) {
2990 if (isnan (z[node]))
2991 continue;
2992 /* Update z_min, z_max */
2993 h->z_min = MIN (h->z_min, (double)z[node]);
2994 h->z_max = MAX (h->z_max, (double)z[node]);
2995 n++;
2996 }
2997 }
2998 if (n == 0) h->z_min = h->z_max = GMT->session.d_NaN; /* No non-NaNs in the entire grid */
2999 }
3000
gmt_grd_minmax(struct GMT_CTRL * GMT,struct GMT_GRID * Grid,double xyz[2][3])3001 void gmt_grd_minmax (struct GMT_CTRL *GMT, struct GMT_GRID *Grid, double xyz[2][3]) {
3002 /* Determine a grid's global min and max locations and z values; return via xyz */
3003 unsigned int row, col, i;
3004 uint64_t ij, i_minmax[2] = {0, 0};
3005 gmt_grdfloat z_extreme[2] = {FLT_MAX, -FLT_MAX};
3006 gmt_M_unused(GMT);
3007
3008 gmt_M_grd_loop (GMT, Grid, row, col, ij) {
3009 if (gmt_M_is_fnan (Grid->data[ij])) continue;
3010 if (Grid->data[ij] < z_extreme[0]) {
3011 z_extreme[0] = Grid->data[ij];
3012 i_minmax[0] = ij;
3013 }
3014 if (Grid->data[ij] > z_extreme[1]) {
3015 z_extreme[1] = Grid->data[ij];
3016 i_minmax[1] = ij;
3017 }
3018 }
3019 for (i = 0; i < 2; i++) { /* 0 is min, 1 is max */
3020 xyz[i][GMT_X] = gmt_M_grd_col_to_x (GMT, gmt_M_col (Grid->header, i_minmax[i]), Grid->header);
3021 xyz[i][GMT_Y] = gmt_M_grd_row_to_y (GMT, gmt_M_row (Grid->header, i_minmax[i]), Grid->header);
3022 xyz[i][GMT_Z] = z_extreme[i];
3023 }
3024 }
3025
gmt_grd_detrend(struct GMT_CTRL * GMT,struct GMT_GRID * Grid,unsigned mode,double * coeff)3026 void gmt_grd_detrend (struct GMT_CTRL *GMT, struct GMT_GRID *Grid, unsigned mode, double *coeff) {
3027 /* mode = 0 (GMT_FFT_REMOVE_NOTHING): Do nothing.
3028 * mode = 1 (GMT_FFT_REMOVE_MEAN): Remove the mean value (returned via a[0])
3029 * mode = 2 (GMT_FFT_REMOVE_MID): Remove the mid value value (returned via a[0])
3030 * mode = 3 (GMT_FFT_REMOVE_TREND): Remove the best-fitting plane by least squares (returned via a[0-2])
3031 *
3032 * Note: The grid may be complex and contain real, imag, or both components. The data should
3033 * be in serial layout so we may loop over the components and do our thing. Only the real
3034 * components coefficients are returned.
3035 */
3036
3037 unsigned int col, row, one_or_zero, start_component = 0, stop_component = 0, component;
3038 uint64_t ij, offset;
3039 bool is_complex = false;
3040 double x_half_length, one_on_xhl, y_half_length, one_on_yhl;
3041 double sumx2, sumy2, data_var_orig = 0.0, data_var = 0.0, var_redux, x, y, z, a[3];
3042 char *comp[2] = {"real", "imaginary"};
3043 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (Grid->header);
3044
3045 gmt_M_memset (coeff, 3, double);
3046
3047 if (HH->trendmode != GMT_FFT_REMOVE_NOTHING) { /* Already removed the trend */
3048 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Grid has already been detrending - no action taken\n");
3049 return;
3050 }
3051 if (mode == GMT_FFT_REMOVE_NOTHING) { /* Do nothing */
3052 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No detrending selected\n");
3053 return;
3054 }
3055 HH->trendmode = mode; /* Update grid header */
3056 if (HH->arrangement == GMT_GRID_IS_INTERLEAVED) {
3057 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Demultiplexing complex grid before detrending can take place.\n");
3058 gmt_grd_mux_demux (GMT, Grid->header, Grid->data, GMT_GRID_IS_SERIAL);
3059 }
3060
3061 if (Grid->header->complex_mode & GMT_GRID_IS_COMPLEX_MASK) { /* Complex grid */
3062 is_complex = true;
3063 start_component = (Grid->header->complex_mode & GMT_GRID_IS_COMPLEX_REAL) ? 0 : 1;
3064 stop_component = (Grid->header->complex_mode & GMT_GRID_IS_COMPLEX_IMAG) ? 1 : 0;
3065 }
3066
3067 for (component = start_component; component <= stop_component; component++) { /* Loop over 1 or 2 components */
3068 offset = component * Grid->header->size / 2; /* offset to start of this component in grid */
3069 gmt_M_memset (a, 3, double);
3070 if (mode == GMT_FFT_REMOVE_MEAN) { /* Remove mean */
3071 for (row = 0; row < Grid->header->n_rows; row++) for (col = 0; col < Grid->header->n_columns; col++) {
3072 ij = gmt_M_ijp (Grid->header,row,col) + offset;
3073 z = Grid->data[ij];
3074 a[0] += z;
3075 data_var_orig += z * z;
3076 }
3077 a[0] /= Grid->header->nm;
3078 for (row = 0; row < Grid->header->n_rows; row++) for (col = 0; col < Grid->header->n_columns; col++) {
3079 ij = gmt_M_ijp (Grid->header,row,col) + offset;
3080 Grid->data[ij] -= (gmt_grdfloat)a[0];
3081 z = Grid->data[ij];
3082 data_var += z * z;
3083 }
3084 var_redux = 100.0 * (data_var_orig - data_var) / data_var_orig;
3085 if (is_complex)
3086 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Mean value removed from %s component: %.8g Variance reduction: %.2f\n",
3087 comp[component], a[0], var_redux);
3088 else
3089 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Mean value removed: %.8g Variance reduction: %.2f\n", a[0], var_redux);
3090 }
3091 else if (mode == GMT_FFT_REMOVE_MID) { /* Remove mid value */
3092 double zmin = DBL_MAX, zmax = -DBL_MAX;
3093 for (row = 0; row < Grid->header->n_rows; row++) for (col = 0; col < Grid->header->n_columns; col++) {
3094 ij = gmt_M_ijp (Grid->header,row,col) + offset;
3095 z = Grid->data[ij];
3096 data_var_orig += z * z;
3097 if (z < zmin) zmin = z;
3098 if (z > zmax) zmax = z;
3099 }
3100 a[0] = 0.5 * (zmin + zmax); /* Mid value */
3101 for (row = 0; row < Grid->header->n_rows; row++) for (col = 0; col < Grid->header->n_columns; col++) {
3102 ij = gmt_M_ijp (Grid->header,row,col) + offset;
3103 Grid->data[ij] -= (gmt_grdfloat)a[0];
3104 z = Grid->data[ij];
3105 data_var += z * z;
3106 }
3107 var_redux = 100.0 * (data_var_orig - data_var) / data_var_orig;
3108 if (is_complex)
3109 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Mid value removed from %s component: %.8g Variance reduction: %.2f\n",
3110 comp[component], a[0], var_redux);
3111 else
3112 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Mid value removed: %.8g Variance reduction: %.2f\n", a[0], var_redux);
3113 }
3114 else { /* Here we wish to remove a LS plane */
3115
3116 /* Let plane be z = a0 + a1 * x + a2 * y. Choose the
3117 center of x,y coordinate system at the center of
3118 the array. This will make the Normal equations
3119 matrix G'G diagonal, so solution is trivial. Also,
3120 spend some multiplications on normalizing the
3121 range of x,y into [-1,1], to avoid roundoff error.
3122 */
3123
3124 one_or_zero = (Grid->header->registration == GMT_GRID_PIXEL_REG) ? 0 : 1;
3125 x_half_length = 0.5 * (Grid->header->n_columns - one_or_zero);
3126 one_on_xhl = 1.0 / x_half_length;
3127 y_half_length = 0.5 * (Grid->header->n_rows - one_or_zero);
3128 one_on_yhl = 1.0 / y_half_length;
3129
3130 sumx2 = sumy2 = data_var = 0.0;
3131
3132 for (row = 0; row < Grid->header->n_rows; row++) {
3133 y = one_on_yhl * (row - y_half_length);
3134 for (col = 0; col < Grid->header->n_columns; col++) {
3135 x = one_on_xhl * (col - x_half_length);
3136 ij = gmt_M_ijp (Grid->header,row,col) + offset;
3137 z = Grid->data[ij];
3138 data_var_orig += z * z;
3139 a[0] += z;
3140 a[1] += z*x;
3141 a[2] += z*y;
3142 sumx2 += x*x;
3143 sumy2 += y*y;
3144 }
3145 }
3146 a[0] /= Grid->header->nm;
3147 a[1] /= sumx2;
3148 a[2] /= sumy2;
3149 for (row = 0; row < Grid->header->n_rows; row++) {
3150 y = one_on_yhl * (row - y_half_length);
3151 for (col = 0; col < Grid->header->n_columns; col++) {
3152 ij = gmt_M_ijp (Grid->header,row,col) + offset;
3153 x = one_on_xhl * (col - x_half_length);
3154 Grid->data[ij] -= (gmt_grdfloat)(a[0] + a[1]*x + a[2]*y);
3155 data_var += (Grid->data[ij] * Grid->data[ij]);
3156 }
3157 }
3158 var_redux = 100.0 * (data_var_orig - data_var) / data_var_orig;
3159 data_var = sqrt (data_var / (Grid->header->nm - 1));
3160 /* Rescale a1,a2 into user's units, in case useful later */
3161 a[1] *= (2.0 / (Grid->header->wesn[XHI] - Grid->header->wesn[XLO]));
3162 a[2] *= (2.0 / (Grid->header->wesn[YHI] - Grid->header->wesn[YLO]));
3163 if (is_complex)
3164 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Plane removed from %s component. Mean, S.D., Dx, Dy: %.8g\t%.8g\t%.8g\t%.8g Variance reduction: %.2f\n",
3165 comp[component], a[0], data_var, a[1], a[2], var_redux);
3166 else
3167 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Plane removed. Mean, S.D., Dx, Dy: %.8g\t%.8g\t%.8g\t%.8g Variance reduction: %.2f\n",
3168 a[0], data_var, a[1], a[2], var_redux);
3169 }
3170 if (component == 0) gmt_M_memcpy (coeff, a, 3, double); /* Return the real component results */
3171 }
3172 }
3173
gmtlib_init_complex(struct GMT_GRID_HEADER * header,unsigned int complex_mode,uint64_t * imag_offset)3174 bool gmtlib_init_complex (struct GMT_GRID_HEADER *header, unsigned int complex_mode, uint64_t *imag_offset) {
3175 /* Sets complex-related parameters based on the input complex_mode variable:
3176 * If complex_mode & GMT_GRID_NO_HEADER then we do NOT want to write a header [output only; only some formats]
3177 * If grid is the imaginary components of a complex grid then we compute the offset
3178 * from the start of the complex array where the first imaginary value goes, using the serial arrangement.
3179 */
3180 bool do_header = !(complex_mode & GMT_GRID_NO_HEADER); /* Want no header if this bit is set */
3181 /* Imaginary components are stored after the real components if complex */
3182 *imag_offset = (complex_mode & GMT_GRID_IS_COMPLEX_IMAG) ? header->size / 2ULL : 0ULL;
3183
3184 return (do_header);
3185 }
3186
3187 /* Reverses the grid vertically, that is, from north up to south up or vice versa. */
gmt_grd_flip_vertical(void * gridp,const unsigned n_cols32,const unsigned n_rows32,const unsigned n_stride32,size_t cell_size)3188 void gmt_grd_flip_vertical (void *gridp, const unsigned n_cols32, const unsigned n_rows32, const unsigned n_stride32, size_t cell_size) {
3189 /* Note: when grid is complex, pass 2x n_rows */
3190 size_t row, n_cols = n_cols32, n_rows = n_rows32;
3191 size_t rows_over_2 = (size_t) floor (n_rows / 2.0);
3192 size_t stride = n_cols; /* stride is the distance between rows. defaults to n_cols */
3193 char *grid = (char*)gridp;
3194 char *tmp = calloc (n_cols, cell_size);
3195 char *top, *bottom;
3196
3197 if (n_stride32 != 0)
3198 stride = (size_t)n_stride32;
3199
3200 for (row = 0; row < rows_over_2; ++row) {
3201 /* pointer to top row: */
3202 top = grid + row * stride * cell_size;
3203 /* pointer to bottom row: */
3204 bottom = grid + ( (n_rows - row) * stride - stride ) * cell_size;
3205 memcpy (tmp, top, n_cols * cell_size); /* save top row */
3206 memcpy (top, bottom, n_cols * cell_size); /* copy bottom to top */
3207 memcpy (bottom, tmp, n_cols * cell_size); /* copy tmp to bottom */
3208 }
3209 gmt_M_str_free (tmp);
3210 }
3211
gmtlib_found_url_for_gdal(char * fname)3212 bool gmtlib_found_url_for_gdal (char *fname) {
3213 /* File names starting as below should not be tested for existence or reading permissions as they
3214 are either meant to be read via GDAL. Regular URLs (e.g., htthp://, ftp:// are read via libcurl. */
3215 if (!strncmp(fname,"WCS:", 4) ||
3216 !strncmp(fname,"WMS:", 4) ||
3217 !strncmp(fname,"MVT:", 4) ||
3218 !strncmp(fname,"/vsi", 4)) {
3219 #ifdef WIN32
3220 /* On Windows libcurl does not care about the certificates file (see https://github.com/curl/curl/issues/1538)
3221 and would fail, so no choice but prevent certificates verification.
3222 However, a CURL_CA_BUNDLE=/path/to/curl-ca-bundle.crt will overcome this and will consult the crt file. */
3223 if (!getenv ("GDAL_HTTP_UNSAFESSL") && !getenv("CURL_CA_BUNDLE"))
3224 putenv ("GDAL_HTTP_UNSAFESSL=YES");
3225 #endif
3226 return true;
3227 }
3228 else
3229 return false;
3230 }
3231
gmtgrdio_get_extension_period(char * file)3232 GMT_LOCAL int gmtgrdio_get_extension_period (char *file) {
3233 int i, pos_ext = 0;
3234 for (i = (int)strlen(file) - 1; i > 0; i--) {
3235 if (file[i] == '.') { /* Beginning of file extension */
3236 pos_ext = i;
3237 break;
3238 }
3239 }
3240 return (pos_ext);
3241 }
3242
3243 #define GMT_VF_TYPE_POS 13 /* Character in the virtual file name that indicates data family */
3244
gmt_raster_type(struct GMT_CTRL * GMT,char * file,bool extra)3245 int gmt_raster_type (struct GMT_CTRL *GMT, char *file, bool extra) {
3246 /* Returns the type of the file (either grid or image).
3247 * We use the file extension to make these decisions:
3248 * GMT_IS_IMAGE: In this context, this means a plain image
3249 * that does not have any georeference information in it.
3250 * The image types that fall into this category are
3251 * GIF, JPG, RAS, PNG, BMP, WEBP, PBM, RGB.
3252 * These are all detected by looking for magic bytes.
3253 * GMT_IS_GRID: In this context, this means file has an extension
3254 * that could be used by images or images with geospatial
3255 * data and even floating point values, such as geotiff.
3256 * THus the list of types in this category are
3257 * TIF, JP2, IMG (ERDAS). These are detected via magic bytes.
3258 * GMT_NOTSET: This covers anything that fails to land in the
3259 * other two categories.
3260 * extra will try to open the image to count bands.
3261 */
3262 FILE *fp = NULL;
3263 unsigned char data[16] = {""};
3264 char *F = NULL, *p = NULL, path[PATH_MAX] = {""};
3265 int j, code, pos_ext;
3266
3267 if (!file) return (GMT_ARG_IS_NULL); /* Gave nothing */
3268 if (gmt_M_file_is_memory (file)) {
3269 return (file[GMT_VF_TYPE_POS] == 'G') ? GMT_NOTSET : GMT_IS_IMAGE;
3270 }
3271 if (gmt_M_file_is_remote (file) || gmt_M_file_is_url (file)) { /* Must download, then modify the name */
3272 j = gmt_download_file_if_not_found (GMT, file, 0);
3273 F = strdup (&file[j]);
3274 }
3275 else
3276 F = strdup (file);
3277
3278 if ((p = strstr(F, "=gd")) != NULL) *p = '\0'; /* Chop off any =gd<stuff> so that the opening test doesn't fail */
3279 if (!gmt_getdatapath (GMT, F, path, R_OK)) {
3280 gmt_M_str_free (F);
3281 return GMT_GRDIO_FILE_NOT_FOUND;
3282 }
3283 pos_ext = gmtgrdio_get_extension_period (path) + 1; /* Start of extension */
3284 if ((fp = fopen (path, "rb")) == NULL) {
3285 gmt_M_str_free (F);
3286 return (GMT_ERROR_ON_FOPEN);
3287 }
3288 gmt_M_str_free (F);
3289 if (gmt_M_fread (data, sizeof (unsigned char), 16, fp) < 16) {
3290 fclose (fp);
3291 return (GMT_GRDIO_READ_FAILED); /* Failed to get one row */
3292 }
3293 fclose (fp);
3294
3295 /* Different magic chars for different image formats:
3296 .jpg: FF D8 FF
3297 .png: 89 50 4E 47 0D 0A 1A 0A
3298 .gif: GIF87a
3299 GIF89a
3300 .bmp: BM
3301 .webp: RIFF ???? WEBP
3302 .ras 59 A6 6A 95
3303 .pbm P 1-6
3304 .rgb 01 da
3305 .tif 49 49 2A 00 or 4D 4D 00 2A
3306 .jp2 00 00 00 0C 6A 50 20 20 0D 0A 87 0A
3307 .img EHFA_HEADER_TAG
3308 .ige ERDAS_IMG_EXTERNAL_RASTER
3309 .fits 53 49 4d 50 4c 45
3310 .ewc 06 02 01 02
3311 */
3312
3313 switch (data[0]) {
3314 case 0x00: /* JP2 */
3315 code = ( !strncmp( (const char *)data, "\x00\x00\x00\x0C\x6A\x50\x20\x20\x0d\x0a\x87\x0a", 12 )) ? GMT_IS_GRID : GMT_NOTSET; break;
3316
3317 case 0x01: /* SGI Iris */
3318 code = (( data[1] == 0xda )) ? GMT_IS_IMAGE : GMT_NOTSET; break;
3319
3320 case 0x06: /* EWC */
3321 code = ( !strncmp( (const char *)data, "\x06\x02\x01\x02", 4 )) ? GMT_IS_GRID : GMT_NOTSET; break;
3322
3323 case 0x53: /* FITS */
3324 code = ( !strncmp( (const char *)data, "\x53\x49\x4d\x50\x4c\x45", 6 )) ? GMT_IS_GRID : GMT_NOTSET; break;
3325
3326 case 0x59: /* Sun raster */
3327 code = ( !strncmp( (const char *)data, "\x59\xA6\x6A\x95", 4 )) ? GMT_IS_IMAGE : GMT_NOTSET; break;
3328
3329 case 0x77: /* OZI */
3330 if (data[1] == 0x80 && gmt_strlcmp (&path[pos_ext], "ozfx3"))
3331 code = GMT_IS_GRID;
3332 else if (data[1] == 0x78 && gmt_strlcmp (&path[pos_ext], "ozf2"))
3333 code = GMT_IS_GRID;
3334 else
3335 code = GMT_NOTSET;
3336 break;
3337 case 0x89: /* PNG */
3338 code = ( !strncmp( (const char *)data, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8 )) ? GMT_IS_IMAGE : GMT_NOTSET; break;
3339
3340 case 0xFF: /* JPG */
3341 code = ( !strncmp( (const char *)data, "\xFF\xD8\xFF", 3 )) ? GMT_IS_IMAGE : GMT_NOTSET; break;
3342
3343 case 'B': /* BMP */
3344 code = (( data[1] == 'M' && gmt_strlcmp (&path[pos_ext], "bmp"))) ? GMT_IS_IMAGE : GMT_NOTSET; break;
3345
3346 case 'E': /* IMG or IGE */
3347 code = ( !strncmp( (const char *)data, "EHFA_HEADER_TAG", 15 ) || !strncmp( (const char *)data, "ERDAS_IMG_EXTER", 15 ) ) ? GMT_IS_GRID : GMT_NOTSET; break;
3348
3349 case 'G': /* GIF */
3350 code = ( !strncmp( (const char *)data, "GIF87a", 6 ) || !strncmp( (const char *)data, "GIF89a", 6 ) ) ? GMT_IS_IMAGE : GMT_NOTSET; break;
3351
3352 case 'I': /* TIF */
3353 if (strstr (file, "earth_day") || strstr (file, "earth_night")) /* We know these are images, not data */
3354 code = GMT_IS_IMAGE;
3355 else
3356 code = ( !strncmp( (const char*)data, "\x49\x49\x2A\x00", 4 )) ? GMT_IS_GRID : GMT_NOTSET;
3357 break;
3358 case 'M': /* TIF */
3359 if (strstr (file, "earth_day") || strstr (file, "earth_night")) /* We know these are images, not data */
3360 code = GMT_IS_IMAGE;
3361 else
3362 code = ( !strncmp( (const char*)data, "\x4D\x4D\x00\x2A", 4 )) ? GMT_IS_GRID : GMT_NOTSET;
3363 break;
3364 case 'P': /* PPM (also check that extension starts with p since the magic bytes are a bit weak ) */
3365 code = (data[1] >= '1' && data[1] <= '6' && tolower (path[pos_ext]) == 'p') ? GMT_IS_IMAGE : GMT_NOTSET; break;
3366
3367 case 'R': /* Google WEBP */
3368 if ( !strncmp ((const char *)data, "RIFF", 4) || !strncmp ((const char *)(data+8), "WEBP", 4))
3369 code = GMT_IS_IMAGE;
3370 else
3371 code = GMT_NOTSET;
3372 break;
3373
3374 default: /* Just consider file extensions at this point */
3375 /* .sid (MrSID), .ecw (ECW), .kap (BSB), .gen (ADRG), .map (OZI) */
3376 if (gmt_strlcmp (&path[pos_ext], "sid") || gmt_strlcmp (&path[pos_ext], "ecw") ||
3377 gmt_strlcmp (&path[pos_ext], "kap") || gmt_strlcmp (&path[pos_ext], "gen") ||
3378 gmt_strlcmp (&path[pos_ext], "map"))
3379 code = GMT_IS_GRID;
3380 else
3381 code = GMT_NOTSET;
3382 break;
3383 }
3384
3385 if (extra && code != GMT_IS_IMAGE && (data[0] == 'I' || data[0] == 'M')) { /* Need to check more to determine if TIFF is grid or image */
3386 /* See if input could be an image of a kind that could also be a grid and we don't yet know what it is. Pass GMT_GRID_IS_IMAGE mode */
3387 struct GMT_IMAGE *I = NULL;
3388 if ((I = GMT_Read_Data (GMT->parent, GMT_IS_IMAGE, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_ONLY | GMT_GRID_IS_IMAGE, NULL, file, NULL)) != NULL) {
3389 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header); /* Get pointer to hidden structure */
3390 if (HH->pocket && strchr (HH->pocket, ',') == NULL) /* Got a single band request which we return as a grid */
3391 code = GMT_IS_GRID;
3392 else if (HH->orig_datatype == GMT_UCHAR || HH->orig_datatype == GMT_CHAR) /* Got a gray or RGB image with or without transparency */
3393 code = GMT_IS_IMAGE;
3394 else if (I->header->n_bands > 1) /* Whatever it is we must return multiband as an image */
3395 code = GMT_IS_IMAGE;
3396 else /* Here we only have one band so it is a grid */
3397 code = GMT_IS_GRID;
3398 GMT_Destroy_Data (GMT->parent, &I);
3399 }
3400 }
3401 if (code == GMT_IS_IMAGE)
3402 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s considered a valid image instead of grid. Open via GDAL\n", file);
3403 else if (code == GMT_IS_GRID)
3404 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s may be image or grid. Open via GDAL for checking\n", file);
3405 else
3406 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "%s is most likely a grid. Open in GMT as grid\n", file);
3407
3408 return code;
3409 }
3410
gmt_img_sanitycheck(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * h)3411 int gmt_img_sanitycheck (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *h) {
3412 /* Make sure that IMG Mercator grids are not used with map projections for plotting */
3413
3414 if (strncmp (h->remark, GMT_IMG_REMARK, strlen(GMT_IMG_REMARK))) return GMT_NOERROR; /* Not a Mercator img grid since missing the critical remark format */
3415 if (h->registration == GMT_GRID_NODE_REG) return GMT_NOERROR; /* Cannot be a Mercator img grid since they are all pixel registered */
3416 if (GMT->current.proj.projection == GMT_LINEAR) return GMT_NOERROR; /* Only linear projection is allowed with this projected grid */
3417 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot use a map projection with an already projected grid (spherical Mercator img grid). Use -Jx or -JX.\n");
3418 return GMT_PROJECTION_ERROR;
3419 }
3420
3421 /* 3-D GMT_CUBE handling is here */
3422
gmtgrdio_get_cube(struct GMT_CTRL * GMT)3423 GMT_LOCAL struct GMT_CUBE *gmtgrdio_get_cube (struct GMT_CTRL *GMT) {
3424 struct GMT_CUBE *C = NULL;
3425 C = gmt_M_memory (GMT, NULL, 1, struct GMT_CUBE);
3426 C->hidden = gmt_M_memory (GMT, NULL, 1, struct GMT_CUBE_HIDDEN);
3427 return (C);
3428 }
3429
gmtlib_create_cube(struct GMT_CTRL * GMT)3430 struct GMT_CUBE *gmtlib_create_cube (struct GMT_CTRL *GMT) {
3431 /* Allocates space for a new cube container. No space allocated for the gmt_grdfloat cube itself */
3432 struct GMT_CUBE *C = NULL;
3433 struct GMT_CUBE_HIDDEN *GU = NULL;
3434
3435 C = gmtgrdio_get_cube (GMT);
3436 GU = gmt_get_U_hidden (C);
3437 C->header = gmt_get_header (GMT);
3438 gmt_grd_init (GMT, C->header, NULL, false); /* Set default values */
3439 #ifdef DOUBLE_PRECISION_GRID
3440 C->header->type = GMT_GRID_IS_ND;
3441 #else
3442 C->header->type = GMT_GRID_IS_NF;
3443 #endif
3444 GMT_Set_Index (GMT->parent, C->header, GMT_GRID_LAYOUT);
3445 GU->alloc_mode = GMT_ALLOC_INTERNALLY; /* Memory can be freed by GMT. */
3446 GU->alloc_level = GMT->hidden.func_level; /* Must be freed at this level. */
3447 GU->id = GMT->parent->unique_var_ID++; /* Give unique identifier */
3448 return (C);
3449 }
3450
gmtlib_duplicate_cube(struct GMT_CTRL * GMT,struct GMT_CUBE * U,unsigned int mode)3451 struct GMT_CUBE *gmtlib_duplicate_cube (struct GMT_CTRL *GMT, struct GMT_CUBE *U, unsigned int mode) {
3452 /* Duplicates an entire grid, including data if requested. */
3453 struct GMT_CUBE *Unew = NULL;
3454
3455 Unew = gmtlib_create_cube (GMT);
3456 gmt_copy_gridheader (GMT, Unew->header, U->header);
3457 gmt_M_memcpy (Unew->z_range, U->z_range, 2U, double);
3458 Unew->z_inc = U->z_inc;
3459 Unew->mode = U->mode;
3460 strncpy (Unew->name, U->name, GMT_GRID_UNIT_LEN80-1);
3461 strncpy (Unew->units, U->units, GMT_GRID_UNIT_LEN80-1);
3462
3463 if ((mode & GMT_DUPLICATE_DATA) || (mode & GMT_DUPLICATE_ALLOC)) { /* Also allocate and possibly duplicate data array */
3464 struct GMT_CUBE_HIDDEN *UH = gmt_get_U_hidden (Unew);
3465 if ((mode & GMT_DUPLICATE_RESET) && !gmt_grd_pad_status (GMT, U->header, GMT->current.io.pad)) {
3466 /* Pads differ and we requested resetting the pad */
3467 gmt_M_grd_setpad (GMT, Unew->header, GMT->current.io.pad); /* Set default pad size */
3468 gmt_set_grddim (GMT, Unew->header); /* Update size dimensions given the change of pad */
3469 if (mode & GMT_DUPLICATE_DATA) { /* Per row since grid sizes will not the same */
3470 uint64_t node_in, node_out, k, off_in = 0, off_out = 0;
3471 unsigned int row;
3472 Unew->data = gmt_M_memory_aligned (GMT, NULL, Unew->header->size * Unew->header->n_bands, gmt_grdfloat);
3473 for (k = 0; k < U->header->n_bands; k++, off_in += U->header->size, off_out += Unew->header->size) {
3474 for (row = 0; row < U->header->n_rows; row++) {
3475 node_in = gmt_M_ijp (U->header, row, 0) + off_in;
3476 node_out = gmt_M_ijp (Unew->header, row, 0) + off_out;
3477 gmt_M_memcpy (&Unew->data[node_out], &U->data[node_in], U->header->n_columns, gmt_grdfloat);
3478 }
3479 }
3480 }
3481 }
3482 else { /* Can do fast copy of the whole cube */
3483 Unew->data = gmt_M_memory_aligned (GMT, NULL, U->header->size * U->header->n_bands, gmt_grdfloat);
3484 if (mode & GMT_DUPLICATE_DATA) gmt_M_memcpy (Unew->data, U->data, U->header->size * U->header->n_bands, gmt_grdfloat);
3485 }
3486
3487 Unew->x = gmt_grd_coord (GMT, Unew->header, GMT_X); /* Get array of x coordinates */
3488 Unew->y = gmt_grd_coord (GMT, Unew->header, GMT_Y); /* Get array of y coordinates */
3489 UH->xyz_alloc_mode[GMT_X] = UH->xyz_alloc_mode[GMT_Y] = GMT_ALLOC_INTERNALLY;
3490 if (U->z) {
3491 Unew->z = gmt_duplicate_array (GMT, U->z, U->header->n_bands);
3492 UH->xyz_alloc_mode[GMT_Z] = GMT_ALLOC_INTERNALLY;
3493 }
3494 }
3495 return (Unew);
3496 }
3497
gmtlib_free_cube_ptr(struct GMT_CTRL * GMT,struct GMT_CUBE * U,bool free_cube)3498 unsigned int gmtlib_free_cube_ptr (struct GMT_CTRL *GMT, struct GMT_CUBE *U, bool free_cube) {
3499 /* By taking a reference to the grid pointer we can set it to NULL when done */
3500 struct GMT_CUBE_HIDDEN *UH = NULL;
3501 enum GMT_enum_alloc alloc_mode;
3502 if (!U) return 0; /* Nothing to deallocate */
3503 /* Only free G->data if allocated by GMT AND free_grid is true */
3504 UH = gmt_get_U_hidden (U);
3505 if (U->data && free_cube) {
3506 if (UH->alloc_mode == GMT_ALLOC_INTERNALLY) gmt_M_free_aligned (GMT, U->data);
3507 U->data = NULL; /* This will remove reference to external memory since gmt_M_free_aligned would not have been called */
3508 }
3509 if (U->x && UH->xyz_alloc_mode[GMT_X] == GMT_ALLOC_INTERNALLY)
3510 gmt_M_free (GMT, U->x);
3511 if (U->y && UH->xyz_alloc_mode[GMT_Y] == GMT_ALLOC_INTERNALLY)
3512 gmt_M_free (GMT, U->y);
3513 if (U->z && UH->xyz_alloc_mode[GMT_Z] == GMT_ALLOC_INTERNALLY)
3514 gmt_M_free (GMT, U->z);
3515 U->x = U->y = U->z = NULL; /* This will remove reference to external memory since gmt_M_free would not have been called */
3516 alloc_mode = UH->alloc_mode;
3517 gmt_M_free (GMT, U->hidden);
3518 gmt_free_header (GMT, &(U->header)); /* Free the header structure and anything allocated by it */
3519 return (alloc_mode);
3520 }
3521
gmtlib_free_cube(struct GMT_CTRL * GMT,struct GMT_CUBE ** U,bool free_cube)3522 void gmtlib_free_cube (struct GMT_CTRL *GMT, struct GMT_CUBE **U, bool free_cube) {
3523 /* By taking a reference to the grid pointer we can set it to NULL when done */
3524 (void)gmtlib_free_cube_ptr (GMT, *U, free_cube);
3525 gmt_M_free (GMT, *U);
3526 }
3527
gmt_get_active_layers(struct GMT_CTRL * GMT,struct GMT_CUBE * U,double * range,uint64_t * start_k,uint64_t * stop_k)3528 uint64_t gmt_get_active_layers (struct GMT_CTRL *GMT, struct GMT_CUBE *U, double *range, uint64_t *start_k, uint64_t *stop_k) {
3529 /* Return via start_k, stop_k the first and last index of the selected layers */
3530 uint64_t n_layers = U->header->n_bands, n_layers_used;
3531 if (range[0] > U->z[n_layers-1] || range[1] < U->z[0]) {
3532 GMT_Report (GMT->parent, GMT_MSG_ERROR, "gmt_get_active_layers: Requested range is outside the valid cube range.\n");
3533 return 0;
3534 }
3535 *start_k = 0; *stop_k = n_layers - 1; /* We first assume all layers are needed */
3536 while (*start_k < n_layers && range[0] > U->z[*start_k]) /* Find the first layer that is inside the selected range */
3537 (*start_k)++;
3538 if (*start_k && range[0] < U->z[*start_k]) (*start_k)--; /* Go back one if start is less than first layer */
3539 while (*stop_k && range[1] < U->z[*stop_k]) /* Find the last layer that is inside the output time range */
3540 (*stop_k)--;
3541 if (*stop_k < (n_layers - 1) && range[1] > U->z[*stop_k]) (*stop_k)++; /* Go forward one if stop is larger than last layer */
3542 n_layers_used = *stop_k - *start_k + 1; /* Total number of input layers needed */
3543 return (n_layers_used);
3544 }
3545
3546 #ifdef HAVE_GDAL
gmtgrdio_gdal_free_from(struct GMT_CTRL * GMT,struct GMT_GDALREAD_OUT_CTRL * from_gdalread)3547 GMT_LOCAL void gmtgrdio_gdal_free_from (struct GMT_CTRL *GMT, struct GMT_GDALREAD_OUT_CTRL *from_gdalread) {
3548 int i;
3549 if (from_gdalread->band_field_names) {
3550 for (i = 0; i < from_gdalread->RasterCount; i++ )
3551 if (from_gdalread->band_field_names[i].DataType)
3552 gmt_M_str_free (from_gdalread->band_field_names[i].DataType); /* Those were allocated with strdup */
3553 gmt_M_free (GMT, from_gdalread->band_field_names);
3554 }
3555 if (from_gdalread->ColorMap) gmt_M_free (GMT, from_gdalread->ColorMap); /* Maybe we will have a use for this in future, but not yet */
3556 }
3557
gmtlib_read_image_info(struct GMT_CTRL * GMT,char * file,bool must_be_image,struct GMT_IMAGE * I)3558 int gmtlib_read_image_info (struct GMT_CTRL *GMT, char *file, bool must_be_image, struct GMT_IMAGE *I) {
3559 size_t k;
3560 char *p;
3561 double dumb;
3562 struct GMT_GDALREAD_IN_CTRL *to_gdalread = NULL;
3563 struct GMT_GDALREAD_OUT_CTRL *from_gdalread = NULL;
3564 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
3565
3566 /* Allocate new control structures */
3567 to_gdalread = gmt_M_memory (GMT, NULL, 1, struct GMT_GDALREAD_IN_CTRL);
3568 from_gdalread = gmt_M_memory (GMT, NULL, 1, struct GMT_GDALREAD_OUT_CTRL);
3569
3570 to_gdalread->M.active = true; /* Get metadata only */
3571
3572 k = strlen (file) - 1;
3573 while (k && file[k] && file[k] != '+') k--; /* See if we have a band request */
3574 if (k && file[k+1] == 'b') {
3575 /* Yes we do. Put the band string into the 'pocket' where gmtlib_read_image will look and finish the request */
3576 HH->pocket = strdup (&file[k+2]);
3577 file[k] = '\0';
3578 }
3579 if ((p = strstr (file, "=gd")) != NULL) *p = '\0'; /* Chop off any =<stuff> */
3580
3581 if (gmt_gdalread (GMT, file, to_gdalread, from_gdalread)) {
3582 GMT_Report (GMT->parent, GMT_MSG_ERROR, "ERROR reading image with gdalread.\n");
3583 gmt_M_free (GMT, to_gdalread);
3584 gmtgrdio_gdal_free_from (GMT, from_gdalread);
3585 gmt_M_free (GMT, from_gdalread);
3586 return (GMT_GRDIO_READ_FAILED);
3587 }
3588 if (from_gdalread->band_field_names != NULL) { /* Know the data type */
3589 if (!strcmp(from_gdalread->band_field_names[0].DataType, "Byte"))
3590 I->type = GMT_UCHAR;
3591 else if (!strcmp(from_gdalread->band_field_names[0].DataType, "Int16"))
3592 I->type = GMT_SHORT;
3593 else if (!strcmp(from_gdalread->band_field_names[0].DataType, "UInt16"))
3594 I->type = GMT_USHORT;
3595 else if (!strcmp(from_gdalread->band_field_names[0].DataType, "Int32"))
3596 I->type = GMT_INT;
3597 else if (!strcmp(from_gdalread->band_field_names[0].DataType, "UInt32"))
3598 I->type = GMT_UINT;
3599 else if (!must_be_image && !strcmp(from_gdalread->band_field_names[0].DataType, "Float32"))
3600 I->type = GMT_FLOAT;
3601 else if (!must_be_image && !strcmp(from_gdalread->band_field_names[0].DataType, "Float64"))
3602 I->type = GMT_FLOAT; /* No doubles for grids or images */
3603 else {
3604 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Using this data type (%s) is not implemented\n",
3605 from_gdalread->band_field_names[0].DataType);
3606 gmt_M_free (GMT, to_gdalread);
3607 gmtgrdio_gdal_free_from (GMT, from_gdalread);
3608 gmt_M_free (GMT, from_gdalread);
3609 return (GMT_NOT_A_VALID_TYPE);
3610 }
3611 }
3612
3613 I->color_interp = from_gdalread->color_interp; /* Must find out how to release this mem */
3614 I->n_indexed_colors = from_gdalread->nIndexedColors;
3615 gmt_M_str_free (I->header->ProjRefPROJ4); /* Make sure we don't leak due to a previous copy */
3616 gmt_M_str_free (I->header->ProjRefWKT);
3617 I->header->ProjRefPROJ4 = from_gdalread->ProjRefPROJ4;
3618 I->header->ProjRefWKT = from_gdalread->ProjRefWKT;
3619 I->header->ProjRefEPSG = from_gdalread->ProjRefEPSG;
3620 I->header->inc[GMT_X] = from_gdalread->hdr[7];
3621 I->header->inc[GMT_Y] = from_gdalread->hdr[8];
3622 I->header->n_columns = from_gdalread->RasterXsize;
3623 I->header->n_rows = from_gdalread->RasterYsize;
3624 I->header->n_bands = from_gdalread->RasterCount;
3625 I->header->registration = (int)from_gdalread->hdr[6];
3626 I->header->z_min = from_gdalread->hdr[4]; /* Whatever that means for an image */
3627 I->header->z_max = from_gdalread->hdr[5];
3628 /* These are always pixel-reg, so they would be wrong when we are returning grid-reg
3629 I->header->wesn[XLO] = from_gdalread->Corners.LL[0];
3630 I->header->wesn[XHI] = from_gdalread->Corners.UR[0];
3631 I->header->wesn[YLO] = from_gdalread->Corners.LL[1];
3632 I->header->wesn[YHI] = from_gdalread->Corners.UR[1];
3633 */
3634 I->header->wesn[XLO] = from_gdalread->hdr[0];
3635 I->header->wesn[XHI] = from_gdalread->hdr[1];
3636 I->header->wesn[YLO] = from_gdalread->hdr[2];
3637 I->header->wesn[YHI] = from_gdalread->hdr[3];
3638 if (I->header->wesn[YHI] < I->header->wesn[YLO]) { /* Simple images have that annoying origin at UL and y positive down */
3639 dumb = I->header->wesn[YHI];
3640 I->header->wesn[YHI] = I->header->wesn[YLO];
3641 I->header->wesn[YLO] = dumb;
3642 }
3643 if (I->header->ProjRefPROJ4 && (strstr (I->header->ProjRefPROJ4, "longlat") || strstr (I->header->ProjRefPROJ4, "latlong")))
3644 gmt_set_geographic (GMT, GMT_IN); /* A geographic image */
3645
3646 HH->grdtype = gmtlib_get_grdtype (GMT, GMT_IN, I->header);
3647 gmt_set_grddim (GMT, I->header); /* This recomputes n_columns|n_rows. Dangerous if -R is not compatible with inc */
3648 GMT_Set_Index (GMT->parent, I->header, GMT_IMAGE_LAYOUT);
3649
3650 gmt_M_free (GMT, to_gdalread);
3651 gmtgrdio_gdal_free_from (GMT, from_gdalread);
3652 gmt_M_free (GMT, from_gdalread);
3653
3654 return (GMT_NOERROR);
3655 }
3656
gmtlib_read_image(struct GMT_CTRL * GMT,char * file,struct GMT_IMAGE * I,double * wesn,unsigned int * pad,unsigned int complex_mode)3657 int gmtlib_read_image (struct GMT_CTRL *GMT, char *file, struct GMT_IMAGE *I, double *wesn, unsigned int *pad, unsigned int complex_mode) {
3658 /* file: - IGNORED -
3659 * image: array with final image
3660 * wesn: Sub-region to extract [Use entire file if NULL or contains 0,0,0,0]
3661 * padding: # of empty rows/columns to add on w, e, s, n of image, respectively
3662 * complex_mode: &1 | &2 if complex array is to hold real (1) and imaginary (2) parts (otherwise read as real only)
3663 * Note: The file has only real values, we simply allow space in the array
3664 * for imaginary parts when processed by grdfft etc.
3665 */
3666
3667 int i;
3668 bool expand;
3669 char strR[GMT_LEN128];
3670 struct GRD_PAD P;
3671 struct GMT_GDALREAD_IN_CTRL *to_gdalread = NULL;
3672 struct GMT_GDALREAD_OUT_CTRL *from_gdalread = NULL;
3673 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (I->header);
3674 gmt_M_unused(complex_mode);
3675
3676 expand = gmtgrdio_padspace (GMT, I->header, wesn, true, pad, &P); /* true if we can extend the region by the pad-size to obtain real data for BC */
3677
3678 /* Allocate new control structures */
3679 to_gdalread = gmt_M_memory (GMT, NULL, 1, struct GMT_GDALREAD_IN_CTRL);
3680 from_gdalread = gmt_M_memory (GMT, NULL, 1, struct GMT_GDALREAD_OUT_CTRL);
3681
3682 if (GMT->common.R.active[RSET]) {
3683 if (gmt_M_360_range (I->header->wesn[XLO], I->header->wesn[XHI])) { /* Ensure we handle -180/180 and 0/360 right */
3684 if (P.wesn[XLO] > I->header->wesn[XHI]) P.wesn[XLO] -= 360.0, P.wesn[XHI] -= 360.0;
3685 else if (P.wesn[XHI] < I->header->wesn[XLO]) P.wesn[XLO] += 360.0, P.wesn[XHI] += 360.0;
3686 }
3687 snprintf (strR, GMT_LEN128, "%.10f/%.10f/%.10f/%.10f", P.wesn[XLO], P.wesn[XHI], P.wesn[YLO], P.wesn[YHI]);
3688 to_gdalread->R.region = strR;
3689 to_gdalread->registration.val = I->header->registration; /* Due to pix-reg only by GDAL we need to inform it about our reg type */
3690 to_gdalread->registration.x_inc = I->header->inc[GMT_X];
3691 to_gdalread->registration.y_inc = I->header->inc[GMT_Y];
3692 to_gdalread->R.active = true; /* Wait until we really know how to use it */
3693 }
3694
3695 if (HH->pocket) { /* See if we have a band request */
3696 to_gdalread->B.active = true;
3697 to_gdalread->B.bands = HH->pocket; /* Band parsing and error testing is done in gmt_gdalread */
3698 }
3699
3700 if (pad) {
3701 gmt_M_memcpy (to_gdalread->p.pad, P.pad, 4U, unsigned int);
3702 to_gdalread->p.active = gmtgrdio_pad_status (GMT, P.pad);
3703 }
3704 to_gdalread->I.active = true; /* Means that image in I->data will be BIP interleaved */
3705
3706 /* Tell gmt_gdalread that we already have the memory allocated and send in the *data pointer */
3707 if (I->data) { /* Otherwise (subregion) memory is allocated in gdalread */
3708 if (I->type == GMT_FLOAT || I->type == GMT_DOUBLE) {
3709 to_gdalread->f_ptr.active = true;
3710 to_gdalread->f_ptr.grd = (gmt_grdfloat *)I->data;
3711 }
3712 else {
3713 to_gdalread->c_ptr.active = true;
3714 to_gdalread->c_ptr.grd = I->data;
3715 }
3716 }
3717 if (HH->grdtype > GMT_GRID_GEOGRAPHIC_LESS360) to_gdalread->R.periodic = true; /* Let gmt_gdalread know we have a periodic image */
3718 if (gmt_gdalread (GMT, file, to_gdalread, from_gdalread)) {
3719 GMT_Report (GMT->parent, GMT_MSG_ERROR, "ERROR reading image with gdalread.\n");
3720 gmt_M_free (GMT, to_gdalread);
3721 for (i = 0; i < from_gdalread->RasterCount; i++)
3722 gmt_M_str_free (from_gdalread->band_field_names[i].DataType); /* Those were allocated with strdup */
3723 gmt_M_free (GMT, from_gdalread->band_field_names);
3724 gmt_M_free (GMT, from_gdalread);
3725 return (GMT_GRDIO_READ_FAILED);
3726 }
3727
3728 if (to_gdalread->O.mem_layout[0]) /* If a different mem_layout request was applied in gmt_gdalread than we must update */
3729 gmt_strncpy(I->header->mem_layout, to_gdalread->O.mem_layout, 4);
3730
3731 if (to_gdalread->B.active) gmt_M_str_free (HH->pocket); /* It was allocated by strdup. Free it for an eventual reuse. */
3732
3733 I->colormap = from_gdalread->ColorMap;
3734 I->n_indexed_colors = from_gdalread->nIndexedColors;
3735 I->header->n_bands = from_gdalread->nActualBands; /* What matters here on is the number of bands actually read */
3736
3737 gmt_M_memcpy (I->header->wesn, from_gdalread->hdr, 4, double); /* Set the actual w/e/s/n bounds */
3738
3739 if (expand) /* Must undo the region extension and reset n_columns, n_rows */
3740 gmt_M_memcpy (I->header->wesn, wesn, 4, double);
3741 gmt_M_grd_setpad (GMT, I->header, pad); /* Copy the pad to the header */
3742 gmt_set_grddim (GMT, I->header); /* Update header items */
3743 HH->grdtype = gmtlib_get_grdtype (GMT, GMT_IN, I->header);
3744
3745 gmt_M_free (GMT, to_gdalread);
3746 for (i = 0; i < from_gdalread->RasterCount; i++)
3747 gmt_M_str_free (from_gdalread->band_field_names[i].DataType); /* Those were allocated with strdup */
3748 gmt_M_free (GMT, from_gdalread->band_field_names);
3749 gmt_M_free (GMT, from_gdalread);
3750 gmt_BC_init (GMT, I->header); /* Initialize image interpolation and boundary condition parameters */
3751
3752 return (GMT_NOERROR);
3753 }
3754 #endif
3755