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 _ N C . C R O U T I N E S
20 *
21 * Takes care of all grd input/output built on NCAR's NetCDF routines.
22 * This version is intended to provide more general support for reading
23 * NetCDF files that were not generated by GMT. At the same time, the grids
24 * written by these routines are intended to be more conform COARDS conventions.
25 * These routines are to eventually replace the older gmt_cdf_ routines.
26 *
27 * Most functions will return with error message if an internal error is returned.
28 * There functions are only called indirectly via the GMT_* grdio functions.
29 *
30 * Author: Remko Scharroo
31 * Date: 04-AUG-2005
32 * Version: 1
33 *
34 * Added support for chunked I/O, Florian Wobbe, June 2012.
35 *
36 * Functions include:
37 *
38 * gmt_nc_read_grd_info: Read header from file
39 * gmt_nc_read_grd: Read data set from file
40 * gmt_nc_update_grd_info: Update header in existing file
41 * gmt_nc_write_grd_info: Write header to new file
42 * gmt_nc_write_grd: Write header and data set to new file
43 * gmt_nc_read_cube_info: Read information from cube file
44 * gmt_nc_write_cube: rite header and cube to new file(s)
45 * gmtlib_is_nc_grid: Determine if we have a nc grid
46 *
47 * Private functions:
48 * gmtnc_setup_chunk_cache: Change the default HDF5 chunk cache settings
49 * gmtnc_pad_grid: Add padding to a grid
50 * gmtnc_unpad_grid: Remove padding from a grid
51 * gmtnc_padding_copy: Fill padding by replicating the border cells
52 * gmtnc_padding_zero: Fill padding with zeros
53 * gmtnc_n_chunked_rows_in_cache Determines how many chunks to read at once
54 * gmtnc_io_nc_grid Does the actual netcdf I/O
55 * gmtnc_netcdf_libvers returns the netCDF library version
56 * gmtnc_right_shift_grid
57 * gmtnc_set_optimal_chunksize Determines the optimal chunksize
58 * gmtnc_get_units
59 * gmtnc_put_units
60 * gmtnc_check_step
61 * gmtnc_grd_info
62 * gmtnc_grid_fix_repeat_col
63 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
64
65 #include "gmt_dev.h"
66 #include "gmt_internals.h"
67 #include <netcdf.h>
68
69 /* Declaration modifier for netcdf DLL support
70 * annoying: why can't netcdf.h do this on its own? */
71 #if defined WIN32 && ! defined NETCDF_STATIC
72 #define DLL_NETCDF
73 #endif
74
75 /* HDF5 chunk cache: reasonable defaults assuming min. chunk size of 128x128 and type byte */
76 #define NC_CACHE_SIZE 33554432 /* 32MiB */
77 #define NC_CACHE_NELEMS 2053 /* prime > NC_CACHE_SIZE / (128*128*1byte) */
78 #define NC_CACHE_PREEMPTION 0.75f
79
80 int gmt_cdf_grd_info (struct GMT_CTRL *GMT, int ncid, struct GMT_GRID_HEADER *header, char job);
81 int gmt_cdf_read_grd (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, double wesn[], unsigned int *pad, unsigned int complex_mode);
82
83 static int nc_libvers[] = {-1, -1, -1, -1}; /* holds the version of the netCDF library */
84
85 static char *regtype[2] = {"gridline", "pixel"};
86
gmtnc_netcdf_libvers(void)87 GMT_LOCAL const int * gmtnc_netcdf_libvers (void) {
88 static bool inquired = false;
89
90 if (!inquired) {
91 const char *vers_string = nc_inq_libvers();
92 sscanf (vers_string, "%d.%d.%d.%d",
93 nc_libvers, nc_libvers+1, nc_libvers+2, nc_libvers+3);
94 inquired = true;
95 }
96
97 return nc_libvers; /* return pointer to version array */
98 }
99
100 /* netcdf I/O mode */
101 enum Netcdf_io_mode {
102 k_put_netcdf = 0,
103 k_get_netcdf
104 };
105
106 /* Wrapper around gmt_nc_put_vara_grdfloat and gmt_nc_get_vara_grdfloat */
gmtnc_vara_grdfloat(int ncid,int varid,const size_t * startp,const size_t * countp,gmt_grdfloat * fp,unsigned io_mode)107 static inline int gmtnc_vara_grdfloat (int ncid, int varid, const size_t *startp, const size_t *countp, gmt_grdfloat *fp, unsigned io_mode) {
108 if (io_mode == k_put_netcdf) /* write netcdf */
109 return gmt_nc_put_vara_grdfloat (ncid, varid, startp, countp, fp);
110 /* read netcdf */
111 return gmt_nc_get_vara_grdfloat (ncid, varid, startp, countp, fp);
112 }
113
114 /* Wrapper around gmt_nc_put_varm_grdfloat and gmt_nc_get_varm_grdfloat */
gmtnc_varm_grdfloat(int ncid,int varid,const size_t * startp,const size_t * countp,const ptrdiff_t * stridep,const ptrdiff_t * imapp,gmt_grdfloat * fp,unsigned io_mode)115 static inline int gmtnc_varm_grdfloat (int ncid, int varid, const size_t *startp, const size_t *countp, const ptrdiff_t *stridep, const ptrdiff_t *imapp, gmt_grdfloat *fp, unsigned io_mode) {
116 if (io_mode == k_put_netcdf) /* write netcdf */
117 return gmt_nc_put_varm_grdfloat (ncid, varid, startp, countp, stridep, imapp, fp);
118 /* read netcdf */
119 return gmt_nc_get_varm_grdfloat (ncid, varid, startp, countp, stridep, imapp, fp);
120 }
121
122 /* Get number of chunked rows that fit into cache (32MiB) */
gmtnc_n_chunked_rows_in_cache(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,unsigned width,unsigned height,size_t * n_contiguous_chunk_rows,size_t * chunksize)123 GMT_LOCAL int gmtnc_n_chunked_rows_in_cache (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, unsigned width, unsigned height, size_t *n_contiguous_chunk_rows, size_t *chunksize) {
124 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
125 nc_type z_type; /* type of z variable */
126 size_t z_size; /* size of z variable */
127 size_t z_bytes; /* Number of bytes */
128 size_t width_t = (size_t)width;
129 size_t height_t = (size_t)height;
130 unsigned yx_dim[2] = {HH->xy_dim[1], HH->xy_dim[0]}; /* because xy_dim not row major */
131 int err, storage_in;
132
133 gmt_M_err_trap (nc_inq_vartype(HH->ncid, HH->z_id, &z_type)); /* type of z */
134 gmt_M_err_trap (nc_inq_type(HH->ncid, z_type, NULL, &z_size)); /* size of z elements in bytes */
135 gmt_M_err_trap (nc_inq_var_chunking (HH->ncid, HH->z_id, &storage_in, chunksize));
136 if (storage_in != NC_CHUNKED) {
137 /* default if NC_CONTIGUOUS */
138 chunksize[yx_dim[0]] = 128; /* 128 rows */
139 chunksize[yx_dim[1]] = width_t; /* all columns */
140 }
141
142 z_bytes = height_t * height_t * z_size;
143 if (z_bytes > NC_CACHE_SIZE) {
144 /* memory needed for subset exceeds the cache size */
145 unsigned int level;
146 size_t chunks_per_row = (size_t) ceil ((double)width / chunksize[yx_dim[1]]);
147 *n_contiguous_chunk_rows = NC_CACHE_SIZE / (width_t * z_size) / chunksize[yx_dim[0]];
148 #ifdef NC4_DEBUG
149 level = GMT_MSG_WARNING;
150 #else
151 level = GMT_MSG_DEBUG;
152 #endif
153 GMT_Report (GMT->parent, level,
154 "processing at most %" PRIuS " (%" PRIuS "x%" PRIuS ") chunks at a time (%.1lf MiB)...\n",
155 *n_contiguous_chunk_rows * chunks_per_row,
156 *n_contiguous_chunk_rows, chunks_per_row,
157 *n_contiguous_chunk_rows * z_size * width * chunksize[yx_dim[0]] / 1048576.0);
158 }
159 else
160 *n_contiguous_chunk_rows = 0; /* all chunks fit into cache */
161
162 return GMT_NOERROR;
163 }
164
165 /* Read and write classic or chunked netcdf files */
gmtnc_io_nc_grid(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,unsigned dim[],unsigned origin[],size_t stride,unsigned io_mode,gmt_grdfloat * grid,bool cube)166 GMT_LOCAL int gmtnc_io_nc_grid (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, unsigned dim[], unsigned origin[], size_t stride, unsigned io_mode, gmt_grdfloat* grid, bool cube) {
167 /* io_mode = k_get_netcdf: read a netcdf file to grid
168 * io_mode = k_put_netcdf: write a grid to netcdf */
169 int status = NC_NOERR;
170 unsigned int d_off = (cube) ? 1 : 0;
171 unsigned width = dim[1+d_off], height = dim[0+d_off];
172 unsigned yx_dim[2]; /* because xy_dim is not row major! */
173 size_t width_t = (size_t)width;
174 size_t height_t = (size_t)height;
175 size_t chunksize[5]; /* chunksize of z */
176 size_t start[5] = {0,0,0,0,0}, count[5] = {1,1,1,1,1};
177 size_t n_contiguous_chunk_rows = 0; /* that are processed at once, 0 = all */
178 ptrdiff_t imap[5] = {1,1,1,1,1}; /* mapping between dims of netCDF and in-memory grid */
179 const ptrdiff_t onestride[5] = {1,1,1,1,1}; /* Passing this instead of NULL bypasses netCDF bug in 4.6.2 */
180 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
181
182 /* catch illegal io_mode in debug */
183 assert (io_mode == k_put_netcdf || io_mode == k_get_netcdf);
184
185 #ifdef NC4_DEBUG
186 GMT_Report (GMT->parent, GMT_MSG_WARNING, "%s n_columns:%u n_rows:%u x0:%u y0:%u y-order:%s\n",
187 io_mode == k_put_netcdf ? "writing," : "reading,",
188 dim[1+d_off], dim[0+d_off], origin[1], origin[0],
189 HH->row_order == k_nc_start_south ? "S->N" : "N->S");
190 #endif
191
192 /* set index of input origin */
193 yx_dim[0] = HH->xy_dim[1], yx_dim[1] = HH->xy_dim[0]; /* xy_dim not row major */
194 memcpy (start, HH->t_index, 3 * sizeof(size_t)); /* set lower dimensions first (e.g. layer) */
195 start[yx_dim[0]] = origin[0]; /* first row */
196 start[yx_dim[1]] = origin[1]; /* first col */
197
198 /* set mapping of complex grids or if reading a part of a grid */
199 imap[yx_dim[0]] = (stride == 0 ? width : stride); /* distance between each row */
200 imap[yx_dim[1]] = 1; /* distance between values in a row */
201 if (cube) imap[0] = header->nm; /* distance between values in a layer (?) */
202
203 /* determine how many chunks to process at once */
204 gmtnc_n_chunked_rows_in_cache (GMT, header, width, height, &n_contiguous_chunk_rows, chunksize);
205
206 if (n_contiguous_chunk_rows) {
207 /* read/write grid in chunks to keep memory footprint low */
208 size_t remainder;
209 #ifdef NC4_DEBUG
210 unsigned row_num = 0;
211 GMT_Report (GMT->parent, GMT_MSG_WARNING, "stride: %u width: %u\n",
212 stride, width);
213 #endif
214
215 /* adjust row count, so that it ends on the bottom of a chunk */
216 count[yx_dim[0]] = chunksize[yx_dim[0]] * n_contiguous_chunk_rows;
217 remainder = start[yx_dim[0]] % chunksize[yx_dim[0]];
218 count[yx_dim[0]] -= remainder;
219
220 count[yx_dim[1]] = width_t;
221 while ( (start[yx_dim[0]] + count[yx_dim[0]]) <= height_t && status == NC_NOERR) {
222 #ifdef NC4_DEBUG
223 GMT_Report (GMT->parent, GMT_MSG_WARNING, "chunked row #%u start-y:%" PRIuS " height:%" PRIuS "\n",
224 ++row_num, start[yx_dim[0]], count[yx_dim[0]]);
225 #endif
226 /* get/put chunked rows */
227 if (stride)
228 status = gmtnc_varm_grdfloat (HH->ncid, HH->z_id, start, count, onestride, imap, grid, io_mode);
229 else
230 status = gmtnc_vara_grdfloat (HH->ncid, HH->z_id, start, count, grid, io_mode);
231
232 /* advance grid location and set new origin */
233 grid += count[yx_dim[0]] * ((stride == 0 ? width_t : stride));
234 start[yx_dim[0]] += count[yx_dim[0]];
235 if (remainder) {
236 /* reset count to full chunk height */
237 count[yx_dim[0]] += remainder;
238 remainder = 0;
239 }
240 }
241 if ( start[yx_dim[0]] != height_t && status == NC_NOERR ) {
242 /* get/put last chunked row */
243 count[yx_dim[0]] = height_t - start[yx_dim[0]] + origin[0];
244 #ifdef NC4_DEBUG
245 GMT_Report (GMT->parent, GMT_MSG_WARNING, "chunked row #%u start-y:%" PRIuS " height:%" PRIuS "\n",
246 ++row_num, start[yx_dim[0]], count[yx_dim[0]]);
247 #endif
248 if (stride)
249 status = gmtnc_varm_grdfloat (HH->ncid, HH->z_id, start, count, onestride, imap, grid, io_mode);
250 else
251 status = gmtnc_vara_grdfloat (HH->ncid, HH->z_id, start, count, grid, io_mode);
252 }
253 }
254 else {
255 /* get/put whole grid contiguous */
256 count[yx_dim[0]] = height_t;
257 count[yx_dim[1]] = width_t;
258 if (stride)
259 status = gmtnc_varm_grdfloat (HH->ncid, HH->z_id, start, count, onestride, imap, grid, io_mode);
260 else
261 status = gmtnc_vara_grdfloat (HH->ncid, HH->z_id, start, count, grid, io_mode);
262 }
263 return status;
264 }
265
gmtnc_get_units(struct GMT_CTRL * GMT,int ncid,int varid,char * name_units)266 GMT_LOCAL void gmtnc_get_units (struct GMT_CTRL *GMT, int ncid, int varid, char *name_units) {
267 /* Get attributes long_name and units for given variable ID
268 * and assign variable name if attributes are not available.
269 * ncid, varid : as in nc_get_att_text
270 * nameunit : long_name and units in form "long_name [units]"
271 */
272 char name[GMT_GRID_UNIT_LEN80], units[GMT_GRID_UNIT_LEN80];
273 if (gmtlib_nc_get_att_text (GMT, ncid, varid, "long_name", name, GMT_GRID_UNIT_LEN80))
274 nc_inq_varname (ncid, varid, name);
275 if (!gmtlib_nc_get_att_text (GMT, ncid, varid, "units", units, GMT_GRID_UNIT_LEN80) && units[0])
276 snprintf (name_units, GMT_GRID_UNIT_LEN80, "%s [%s]", name, units);
277 else
278 strncpy (name_units, name, GMT_GRID_UNIT_LEN80-1);
279 }
280
gmtnc_put_units(int ncid,int varid,char * name_units)281 GMT_LOCAL void gmtnc_put_units (int ncid, int varid, char *name_units) {
282 /* Put attributes long_name and units for given variable ID based on
283 * string name_unit in the form "long_name [units]".
284 * ncid, varid : as is nc_put_att_text
285 * name_units : string in form "long_name [units]"
286 */
287 bool copy = false;
288 int i = 0, j = 0;
289 char name[GMT_GRID_UNIT_LEN80], units[GMT_GRID_UNIT_LEN80];
290
291 strncpy (name, name_units, GMT_GRID_UNIT_LEN80-1);
292 units[0] = '\0';
293 for (i = 0; i < GMT_GRID_UNIT_LEN80 && name[i]; i++) {
294 if (name[i] == ']') copy = false, units[j] = '\0';
295 if (copy) units[j++] = name[i];
296 if (name[i] == '[') {
297 name[i] = '\0';
298 if (i > 0 && name[i-1] == ' ') name[i-1] = '\0';
299 copy = true;
300 }
301 }
302 if (name[0]) nc_put_att_text (ncid, varid, "long_name", strlen(name), name);
303 if (units[0]) nc_put_att_text (ncid, varid, "units", strlen(units), units);
304 if (strstr(units, "degrees_east")) {
305 nc_put_att_text (ncid, varid, "standard_name", 9, "longitude");
306 nc_put_att_text (ncid, varid, "axis", 1, "X");
307 }
308 else if (strstr(units, "degrees_north")) {
309 nc_put_att_text (ncid, varid, "standard_name", 8, "latitude");
310 nc_put_att_text (ncid, varid, "axis", 1, "Y");
311 }
312 }
313
gmtnc_check_step(struct GMT_CTRL * GMT,uint32_t n,double * x,char * varname,char * file,bool save_xy_array)314 GMT_LOCAL int gmtnc_check_step (struct GMT_CTRL *GMT, uint32_t n, double *x, char *varname, char *file, bool save_xy_array) {
315 /* Check if all steps in range are the same (within 0.1%). Returns 0 if OK and 1 if variable spacing */
316 double step, step_min, step_max;
317 uint32_t i;
318 unsigned int wlevel = (save_xy_array) ? GMT_MSG_INFORMATION : GMT_MSG_WARNING;
319 if (n < 2) return 0;
320 step_min = step_max = x[1]-x[0];
321 for (i = 2; i < n; i++) {
322 step = x[i]-x[i-1];
323 if (step < step_min) step_min = step;
324 if (step > step_max) step_max = step;
325 }
326 if (fabs (step_min-step_max)/(fabs (step_min)*0.5 + fabs (step_max)*0.5) > 0.001) {
327 GMT_Report (GMT->parent, wlevel,
328 "The step size of coordinate (%s) in grid %s is not constant.\n", varname, file);
329 GMT_Report (GMT->parent, wlevel,
330 "GMT will use a constant step size of %g; the original ranges from %g to %g.\n",
331 (x[n-1]-x[0])/(n-1), step_min, step_max);
332 return 1;
333 }
334 return 0;
335 }
336
gmtnc_set_optimal_chunksize(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)337 GMT_LOCAL void gmtnc_set_optimal_chunksize (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
338 /* For optimal performance, set the number of elements in a given chunk
339 * dimension (n) to be the ceiling of the number of elements in that
340 * dimension of the array variable (d) divided by a natural number N>1.
341 * That is, set n = ceil (d / N). Using a chunk size slightly larger than
342 * this value is also acceptable. For example: 129 = ceil (257 / 2).
343 * Do NOT set n = floor (d / N), for example 128 = floor (257 / 2). */
344
345 double chunksize[2] = {128, 128}; /* default min chunksize */
346 const size_t min_chunk_pixels = (size_t)(chunksize[0] * chunksize[1]); /* min pixel count per chunk */
347
348 if (GMT->current.setting.io_nc4_chunksize[0] == k_netcdf_io_classic)
349 /* no chunking with classic model */
350 return;
351
352 if (GMT->current.setting.io_nc4_chunksize[0] != k_netcdf_io_chunked_auto && (
353 header->n_rows >= GMT->current.setting.io_nc4_chunksize[0] ||
354 header->n_columns >= GMT->current.setting.io_nc4_chunksize[1])) {
355 /* if chunk size is smaller than grid size */
356 return;
357 }
358
359 /* here, chunk size is either k_netcdf_io_chunked_auto or the chunk size is
360 * larger than grid size */
361
362 if ( header->nm < min_chunk_pixels ) {
363 /* the grid dimension is too small for chunking to make sense. switch to
364 * classic model */
365 GMT->current.setting.io_nc4_chunksize[0] = k_netcdf_io_classic;
366 return;
367 }
368
369 /* adjust default chunk sizes for grids that have more than min_chunk_pixels
370 * cells but less than chunksize (default 128) cells in one dimension */
371 if (header->n_rows < chunksize[0]) {
372 chunksize[0] = header->n_rows;
373 chunksize[1] = floor (min_chunk_pixels / chunksize[0]);
374 }
375 else if (header->n_columns < chunksize[1]) {
376 chunksize[1] = header->n_columns;
377 chunksize[0] = floor (min_chunk_pixels / chunksize[1]);
378 }
379
380 /* determine optimal chunk size in the range [chunksize,2*chunksize) */
381 GMT->current.setting.io_nc4_chunksize[0] = (size_t) ceil (header->n_rows / floor (header->n_rows / chunksize[0]));
382 GMT->current.setting.io_nc4_chunksize[1] = (size_t) ceil (header->n_columns / floor (header->n_columns / chunksize[1]));
383 }
384
gmtnc_not_obviously_global(double * we)385 GMT_LOCAL bool gmtnc_not_obviously_global (double *we) {
386 /* If range is not 360 and boundaries are not 0, 180, 360 then we pass true */
387 if (!gmt_M_360_range (we[0], we[1])) return true;
388 if (!(doubleAlmostEqualZero (we[0], 0.0) || doubleAlmostEqual (we[0], -180.0))) return true;
389 return false;
390 }
391
gmtnc_not_obviously_polar(double * se)392 GMT_LOCAL bool gmtnc_not_obviously_polar (double *se) {
393 /* If range is not 180 and boundaries are not -90/90 then we pass true */
394 if (!gmt_M_180_range (se[0], se[1])) return true;
395 if (!(doubleAlmostEqual (se[0], -90.0) && doubleAlmostEqual (se[1], 90.0))) return true;
396 return false;
397 }
398
gmtnc_put_xy_vectors(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)399 GMT_LOCAL int gmtnc_put_xy_vectors (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
400 /* Write the x and y vectors to the netCDF file */
401 int err = GMT_NOERROR;
402 /* Create enough memory to store the x- and y-coordinate values */
403 double *xy = gmt_M_memory (GMT, NULL, MAX (header->n_columns,header->n_rows), double);
404 unsigned int col, row;
405 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
406
407 for (col = 0; col < header->n_columns; col++) xy[col] = gmt_M_grd_col_to_x (GMT, col, header);
408 gmt_M_err_trap (nc_put_var_double (HH->ncid, HH->xyz_id[GMT_X], xy));
409
410 /* Depending on row_order, write y-coordinate array bottom-to-top or top-to-bottom */
411 if (HH->row_order == k_nc_start_south) {
412 for (row = 0; row < header->n_rows; row++)
413 xy[row] = (double) gmt_M_col_to_x (GMT, row, header->wesn[YLO], header->wesn[YHI], header->inc[GMT_Y], 0.5 * header->registration, header->n_rows);
414 }
415 else {
416 for (row = 0; row < header->n_rows; row++)
417 xy[row] = (double) gmt_M_row_to_y (GMT, row, header->wesn[YLO], header->wesn[YHI], header->inc[GMT_Y], 0.5 * header->registration, header->n_rows);
418 }
419 gmt_M_err_trap (nc_put_var_double (HH->ncid, HH->xyz_id[GMT_Y], xy));
420 gmt_M_free (GMT, xy);
421 return GMT_NOERROR;
422 }
423
gmtnc_grd_info(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,char job,bool cube,uint64_t n_layers)424 GMT_LOCAL int gmtnc_grd_info (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, char job, bool cube, uint64_t n_layers) {
425 /* Used when reading and writing 2-D grids and when writing 3-D data cubes */
426 int j, err, has_vector, has_range, registration, var_spacing = 0;
427 int old_fill_mode, status;
428 unsigned int d_off = (cube) ? 1 : 0;
429 size_t len;
430 double dummy[2], min, max;
431 char dimname[GMT_GRID_UNIT_LEN80], coord[GMT_LEN8];
432 nc_type z_type;
433 bool save_xy_array = !strncmp (GMT->init.module_name, "grd2xyz", 7U);
434 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
435
436 /* Dimension ids, variable ids, etc.. */
437 int i, ncid, z_id = GMT_NOTSET, ids[5] = {-1,-1,-1,-1,-1}, gm_id = GMT_NOTSET, dims[5], nvars, ndims = 0;
438 size_t lens[5], item[2];
439
440 /* If not yet determined, attempt to get the layer IDs from the variable name */
441 int n_t_index_by_value = 0;
442 double t_value[3];
443
444 if (HH->varname[0]) {
445 char *c = strpbrk (HH->varname, "(["); /* find first occurrence of ( or [ */
446 if (c != NULL && *c == '(') {
447 n_t_index_by_value = sscanf (c+1, "%lf,%lf,%lf", &t_value[0], &t_value[1], &t_value[2]);
448 *c = '\0';
449 /* t_index will be determined later from t_value when the nc-file is opened */
450 }
451 else if (c != NULL && *c == '[') {
452 sscanf (c+1, "%" SCNuS ",%" SCNuS ",%" SCNuS "", &HH->t_index[0], &HH->t_index[1], &HH->t_index[2]);
453 *c = '\0';
454 }
455 }
456
457 /* Open NetCDF file */
458 if (!strcmp (HH->name,"=")) return (GMT_GRDIO_NC_NO_PIPE);
459 switch (job) {
460 case 'r':
461 gmt_M_err_trap (gmt_nc_open (GMT, HH->name, NC_NOWRITE, &ncid));
462 break;
463 case 'u':
464 gmt_M_err_trap (gmt_nc_open (GMT, HH->name, NC_WRITE, &ncid));
465 gmt_M_err_trap (nc_set_fill (ncid, NC_NOFILL, &old_fill_mode));
466 break;
467 default:
468 /* create new nc-file */
469 gmtnc_set_optimal_chunksize (GMT, header);
470 if (GMT->current.setting.io_nc4_chunksize[0] != k_netcdf_io_classic) {
471 /* create chunked nc4 file */
472 gmt_M_err_trap (gmt_nc_create (GMT, HH->name, NC_NETCDF4, &ncid));
473 HH->is_netcdf4 = true;
474 }
475 else {
476 /* create nc using classic data model */
477 gmt_M_err_trap (gmt_nc_create (GMT, HH->name, NC_CLOBBER, &ncid));
478 HH->is_netcdf4 = false;
479 }
480 gmt_M_err_trap (nc_set_fill (ncid, NC_NOFILL, &old_fill_mode));
481 break;
482 }
483
484 /* Retrieve or define dimensions and variables */
485
486 if (job == 'r' || job == 'u') {
487 int kind;
488 /* determine netCDF data model */
489 gmt_M_err_trap (nc_inq_format(ncid, &kind));
490 HH->is_netcdf4 = (kind == NC_FORMAT_NETCDF4 || kind == NC_FORMAT_NETCDF4_CLASSIC);
491
492 /* First see if this is an old NetCDF formatted file */
493 if (!nc_inq_dimid (ncid, "xysize", &i)) return (gmt_cdf_grd_info (GMT, ncid, header, job));
494
495 /* Find first 2-dimensional (z) variable or specified variable */
496 if (!HH->varname[0]) { /* No specific named variable was requested; search for suitable matrix */
497 int ID = GMT_NOTSET, dim = 0;
498 gmt_M_err_trap (nc_inq_nvars (ncid, &nvars));
499 i = 0;
500 while (i < nvars && z_id < 0) { /* Look for first 2D grid, with fallback to first higher-dimension (3-4D) grid if 2D not found */
501 gmt_M_err_trap (nc_inq_varndims (ncid, i, &ndims));
502 if (ndims == 2) /* Found the first 2-D grid */
503 z_id = i;
504 else if (ID == GMT_NOTSET && ndims > 2 && ndims < 5) { /* Also look for higher-dim grid in case no 2-D */
505 ID = i;
506 dim = ndims;
507 }
508 i++;
509 }
510 if (z_id < 0) { /* No 2-D grid found, check if we found a higher dimension cube */
511 if (ID == GMT_NOTSET) return (GMT_GRDIO_NO_2DVAR); /* No we didn't */
512 z_id = ID; /* Pick the higher dimensioned cube instead, get its name, and warn */
513 nc_inq_varname (ncid, z_id, HH->varname);
514 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No 2-D array in file %s. Selecting first 2-D slice in the %d-D array %s\n", HH->name, dim, HH->varname);
515 }
516 }
517 else if (nc_inq_varid (ncid, HH->varname, &z_id) == NC_NOERR) { /* Gave a named variable that exist, is it 2-4 D? */
518 gmt_M_err_trap (nc_inq_varndims (ncid, z_id, &ndims));
519 if (ndims < 2 || ndims > 5) return (GMT_GRDIO_BAD_DIM);
520 }
521 else /* No such named variable in the grid */
522 return (GMT_GRDIO_NO_VAR);
523
524 if (z_id < 0) return (GMT_GRDIO_NO_2DVAR);
525
526 /* Get the z data type and determine its dimensions */
527 gmt_M_err_trap (nc_inq_vartype (ncid, z_id, &z_type));
528 gmt_M_err_trap (nc_inq_vardimid (ncid, z_id, dims));
529 switch (z_type) {
530 case NC_UBYTE: header->type = GMT_GRID_IS_NB; HH->orig_datatype = GMT_UCHAR; break;
531 case NC_BYTE: header->type = GMT_GRID_IS_NB; HH->orig_datatype = GMT_CHAR; break;
532 case NC_SHORT: header->type = GMT_GRID_IS_NS; HH->orig_datatype = GMT_SHORT; break;
533 case NC_INT: header->type = GMT_GRID_IS_NI; HH->orig_datatype = GMT_INT; break;
534 case NC_FLOAT: header->type = GMT_GRID_IS_NF; HH->orig_datatype = GMT_FLOAT; break;
535 case NC_DOUBLE: header->type = GMT_GRID_IS_ND; HH->orig_datatype = GMT_DOUBLE; break;
536 default: header->type = k_grd_unknown_fmt; break;
537 }
538
539 /* Get the ids of the x and y (and depth and time) coordinate variables */
540 for (i = 0; i < ndims; i++) {
541 gmt_M_err_trap (nc_inq_dim (ncid, dims[i], dimname, &lens[i]));
542 //if (nc_inq_varid (ncid, dimname, &ids[i])) return (GMT_GRDIO_NC_NOT_COARDS);
543 if ((status = nc_inq_varid (ncid, dimname, &ids[i])) != NC_NOERR)
544 GMT_Report (GMT->parent, GMT_MSG_WARNING, "\"%s\", %s\n\tIf something bad happens later, try importing via GDAL.\n",
545 dimname, nc_strerror(status));
546 }
547 HH->xy_dim[0] = ndims-1;
548 HH->xy_dim[1] = ndims-2;
549 HH->xyz_id[GMT_X] = ids[1];
550 HH->xyz_id[GMT_Y] = ids[0];
551
552 /* Check if LatLon variable exists, then we may need to flip x and y */
553 if (nc_inq_varid (ncid, "LatLon", &i) == NC_NOERR) nc_get_var_int (ncid, i, HH->xy_dim);
554 header->n_columns = (uint32_t) lens[HH->xy_dim[0]];
555 header->n_rows = (uint32_t) lens[HH->xy_dim[1]];
556
557 /* Check if the spatial_ref attribute exists, first via the z_id variable, and if not then via a global attribute */
558 if (nc_inq_attlen (ncid, z_id, "grid_mapping", &len) == NC_NOERR) { /* The data layer has an attribute with the name of the variable that has the spatial_ref */
559 char *v_name = gmt_M_memory (GMT, NULL, len+1, char); /* Allocate the needed space for the variable name */
560 if (nc_get_att_text (ncid, z_id, "grid_mapping", v_name) == NC_NOERR) { /* Get the name of the variable */
561 if (nc_inq_varid (ncid, v_name, &i) == NC_NOERR) /* Got the id of this variable */
562 gm_id = i;
563 }
564 gmt_M_free (GMT, v_name);
565 }
566 else if (nc_inq_varid (ncid, "grid_mapping", &i) == NC_NOERR) /* A global variable has it */
567 gm_id = i;
568 } /* if (job == 'r' || job == 'u') */
569 else {
570 /* Define dimensions of z variable */
571 ndims = (cube) ? 3 : 2;
572 HH->xy_dim[0] = 1 + d_off;
573 HH->xy_dim[1] = 0 + d_off;
574
575 strcpy (coord, (gmt_M_x_is_lon (GMT, GMT_OUT)) ? "lon" : (gmt_M_type (GMT, GMT_OUT, GMT_X) & GMT_IS_RATIME) ? "time" : "x");
576 gmt_M_err_trap (nc_def_dim (ncid, coord, (size_t) header->n_columns, &dims[1+d_off]));
577 gmt_M_err_trap (nc_def_var (ncid, coord, NC_DOUBLE, 1, &dims[1+d_off], &ids[1+d_off]));
578 HH->xyz_id[GMT_X] = ids[1+d_off];
579
580 strcpy (coord, (gmt_M_y_is_lat (GMT, GMT_OUT)) ? "lat" : (gmt_M_type (GMT, GMT_OUT, GMT_Y) & GMT_IS_RATIME) ? "time" : "y");
581 gmt_M_err_trap (nc_def_dim (ncid, coord, (size_t) header->n_rows, &dims[0+d_off]));
582 gmt_M_err_trap (nc_def_var (ncid, coord, NC_DOUBLE, 1, &dims[0+d_off], &ids[0+d_off]));
583 HH->xyz_id[GMT_Y] = ids[0+d_off];
584
585 if (cube) { /* Allow for cube expansion by setting layer dimension to 0 (NC_UNLIMITED) */
586 strcpy (coord, (gmt_M_type (GMT, GMT_OUT, GMT_Z) & GMT_IS_RATIME) ? "time" : "z");
587 gmt_M_err_trap (nc_def_dim (ncid, coord, (size_t) n_layers, &dims[0]));
588 gmt_M_err_trap (nc_def_var (ncid, coord, NC_DOUBLE, 1, &dims[0], &HH->xyz_id[GMT_Z]));
589 }
590
591 switch (header->type) {
592 case GMT_GRID_IS_NB: z_type = NC_BYTE; break;
593 case GMT_GRID_IS_NS: z_type = NC_SHORT; break;
594 case GMT_GRID_IS_NI: z_type = NC_INT; break;
595 case GMT_GRID_IS_NF: z_type = NC_FLOAT; break;
596 case GMT_GRID_IS_ND: z_type = NC_DOUBLE; break;
597 default: z_type = NC_NAT;
598 }
599
600 /* Variable name is given, or defaults to "z" (or "cube" under cube) */
601 if (!HH->varname[0])
602 strcpy (HH->varname, (cube) ? "cube" : "z");
603 gmt_M_err_trap (nc_def_var (ncid, HH->varname, z_type, (cube) ? 3 : 2, dims, &z_id));
604
605 /* set deflation and chunking */
606 if (GMT->current.setting.io_nc4_chunksize[0] != k_netcdf_io_classic) {
607 /* set chunk size */
608 size_t nc4_chunksize[3] = {0, 0, 0};
609 gmt_M_memcpy (&nc4_chunksize[cube], GMT->current.setting.io_nc4_chunksize, 2U, size_t);
610 gmt_M_err_trap (nc_def_var_chunking (ncid, z_id, NC_CHUNKED, nc4_chunksize));
611 /* set deflation level and shuffle for x, y[, z], and z[|w] variable */
612 if (GMT->current.setting.io_nc4_deflation_level) {
613 gmt_M_err_trap (nc_def_var_deflate (ncid, HH->xyz_id[GMT_X], true, true, GMT->current.setting.io_nc4_deflation_level));
614 gmt_M_err_trap (nc_def_var_deflate (ncid, HH->xyz_id[GMT_Y], true, true, GMT->current.setting.io_nc4_deflation_level));
615 if (cube && n_layers == 0) gmt_M_err_trap (nc_def_var_deflate (ncid, HH->xyz_id[GMT_Z], true, true, GMT->current.setting.io_nc4_deflation_level));
616 gmt_M_err_trap (nc_def_var_deflate (ncid, z_id, true, true, GMT->current.setting.io_nc4_deflation_level));
617 }
618 } /* GMT->current.setting.io_nc4_chunksize[0] != k_netcdf_io_classic */
619 } /* if (job == 'r' || job == 'u') */
620 HH->z_id = z_id;
621 HH->ncid = ncid;
622
623 /* Query or assign attributes */
624
625 if (job == 'u') gmt_M_err_trap (nc_redef (ncid));
626
627 if (job == 'r') {
628 bool set_reg = true;
629 double dx = 0, dy = 0, threshold = 0.0;
630 /* Create enough memory to store the x- and y-coordinate values */
631 double *xy = gmt_M_memory (GMT, NULL, MAX (header->n_columns,header->n_rows), double);
632 /* Get global information */
633 if (gmtlib_nc_get_att_text (GMT, ncid, NC_GLOBAL, "title", header->title, GMT_GRID_TITLE_LEN80))
634 gmtlib_nc_get_att_text (GMT, ncid, z_id, "long_name", header->title, GMT_GRID_TITLE_LEN80);
635 if (gmtlib_nc_get_att_text (GMT, ncid, NC_GLOBAL, "history", header->command, GMT_GRID_COMMAND_LEN320))
636 gmtlib_nc_get_att_text (GMT, ncid, NC_GLOBAL, "source", header->command, GMT_GRID_COMMAND_LEN320);
637 gmtlib_nc_get_att_text (GMT, ncid, NC_GLOBAL, "description", header->remark, GMT_GRID_REMARK_LEN160);
638 header->registration = GMT_GRID_NODE_REG;
639 if (!nc_get_att_int (ncid, NC_GLOBAL, "node_offset", &i)) { /* GMT wrote the registration in the grid */
640 header->registration = i;
641 set_reg = false; /* Do not update it below since we know the registration */
642 }
643
644 if (gm_id != GMT_NOTSET && nc_inq_attlen (ncid, gm_id, "spatial_ref", &len) == NC_NOERR) { /* Good to go */
645 char *attrib = NULL;
646 gmt_M_str_free (header->ProjRefWKT); /* Make sure we didn't have a previously allocated one */
647 attrib = gmt_M_memory (GMT, NULL, len+1, char); /* and allocate the needed space */
648 gmt_M_err_trap (nc_get_att_text (ncid, gm_id, "spatial_ref", attrib));
649 header->ProjRefWKT = strdup (attrib); /* Turn it into a strdup allocation to be compatible with other instances elsewhere */
650 gmt_M_free (GMT, attrib);
651 }
652
653 /* Explanation for the logic below: Not all netCDF grids are proper COARDS grids and hence we sometime must guess
654 * regarding the settings. The x and y coordinates may be written as arrays, which reflect the positions of the
655 * nodes. There may also be the attributes actual_range which specifies the x range. These will differ if the
656 * grid is pixel registered, hence when both are present we use this difference to detect a pixel grid. However,
657 * some external tools such as xarray may slice a grid but not update the attributes. In this case the actual_range
658 * may have an initial range that is no longer the case. We have added a check if these differ by more than a
659 * half grid increment. If not then we can trust it. If actual_range is missing then we have to guess the registration
660 * which we do by checking if the start coordinate is an integer multiple of the increment. If not, we guess pixel registration
661 * but cannot know if this is the case unless the adjusted coordinates in x has a range of 360 and in y a range of 180.
662 * Finally, if there is no array just the actual_range, then we cannot tell the registration from the range but try
663 * and leave it as gridline registration. */
664
665 /* Get information about x variable */
666 gmtnc_get_units (GMT, ncid, ids[HH->xy_dim[0]], header->x_units);
667 /* Set default range to number of nodes in case nothing is found further down */
668 dummy[0] = 0.0, dummy[1] = (double) header->n_columns-1; /* Default */
669 registration = GMT_GRID_NODE_REG;
670
671 /* Look for the x-coordinate vector */
672 if ((has_vector = !nc_get_var_double (ncid, ids[HH->xy_dim[0]], xy)) && header->n_columns > 1) {
673 var_spacing = gmtnc_check_step (GMT, header->n_columns, xy, header->x_units, HH->name, save_xy_array);
674 if (save_xy_array && var_spacing) {
675 if (GMT->current.io.nc_xarray) gmt_M_free (GMT, GMT->current.io.nc_xarray);
676 GMT->current.io.nc_xarray = gmt_M_memory (GMT, NULL, header->n_columns, double);
677 gmt_M_memcpy (GMT->current.io.nc_xarray, xy, header->n_columns, double);
678 HH->var_spacing[GMT_X] = var_spacing;
679 }
680 dx = fabs (xy[1] - xy[0]); /* Grid spacing in x */
681 }
682 if (has_vector && header->n_columns == 1) has_vector = false; /* One coordinate does not a vector make */
683
684 /* Look for the x-coordinate range attributes */
685 has_range = (!nc_get_att_double (ncid, ids[HH->xy_dim[0]], "actual_range", dummy) ||
686 !nc_get_att_double (ncid, ids[HH->xy_dim[0]], "valid_range", dummy) ||
687 !(nc_get_att_double (ncid, ids[HH->xy_dim[0]], "valid_min", &dummy[0]) +
688 nc_get_att_double (ncid, ids[HH->xy_dim[0]], "valid_max", &dummy[1])));
689
690 if (has_vector && has_range) { /* Has both so we can do a basic sanity check */
691 threshold = (0.5+GMT_CONV5_LIMIT) * dx;
692 min = xy[0]; max = xy[header->n_columns-1];
693 if (min > max) gmt_M_double_swap (min, max); /* Got it backwards in the array */
694 if (fabs (dummy[0] - min) > threshold || fabs (dummy[1] - max) > threshold) {
695 if (gmtnc_not_obviously_global (dummy)) {
696 GMT_Report (GMT->parent, GMT_MSG_WARNING, "The x-coordinates and range attribute are in conflict; must rely on coordinates only\n");
697 dummy[0] = xy[0], dummy[1] = xy[header->n_columns-1];
698 has_range = false; /* Since useless information */
699 /* For registration, we have to assume that the actual range is an integer multiple of increment.
700 * If so, then if the coordinates are off by 0.5*dx then we assume we have pixel registration */
701 threshold = (0.5-GMT_CONV5_LIMIT) * dx;
702 if (set_reg && fabs (fmod (dummy[0], dx)) > threshold) { /* Pixel registration */
703 registration = header->registration = GMT_GRID_PIXEL_REG;
704 dummy[0] -= 0.5 * dx; dummy[1] += 0.5 * dx;
705 if (gmt_M_360_range (dummy[0], dummy[1]))
706 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid x-coordinates after pixel registration adjustment have exactly 360 range\n");
707 }
708 }
709 else { /* Got clean global longitude range, stick with that information and ignore xy */
710 GMT_Report (GMT->parent, GMT_MSG_WARNING, "The x-coordinates and range attribute are in conflict but range is exactly 360; we rely on this range\n");
711 if (set_reg && (header->n_columns%2) == 0) { /* Pixel registration */
712 registration = header->registration = GMT_GRID_PIXEL_REG;
713 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Global longitudes, guessing registration to be %s since nx is odd\n", regtype[header->registration]);
714 }
715 }
716 }
717 else { /* Data seems OK; determine registration */
718 dummy[0] = xy[0], dummy[1] = xy[header->n_columns-1];
719 if (set_reg && (fabs(dummy[1] - dummy[0]) / fabs(xy[header->n_columns-1] - xy[0]) - 1.0 > 0.5 / (header->n_columns - 1)))
720 registration = header->registration = GMT_GRID_PIXEL_REG;
721 }
722 }
723 else if (has_vector) { /* No attribute for range, use coordinates */
724 dummy[0] = xy[0], dummy[1] = xy[header->n_columns-1];
725 threshold = (0.5-GMT_CONV5_LIMIT) * dx;
726 if (set_reg && fabs (fmod (dummy[0], dx)) > threshold) { /* Most likely pixel registration since off by dx/2 */
727 registration = header->registration = GMT_GRID_PIXEL_REG;
728 dummy[0] -= 0.5 * dx; dummy[1] += 0.5 * dx;
729 if (gmt_M_360_range (dummy[0], dummy[1]))
730 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid x-coordinates after pixel registration adjustment have exactly 360 range\n");
731 }
732 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No range attribute, guessing registration to be %s\n", regtype[header->registration]);
733 }
734 else { /* Only has the valid_range settings. If no registration set, and no dx available, guess based on nx */
735 if (set_reg && (header->n_columns%2) == 0) { /* Pixel registration */
736 registration = header->registration = GMT_GRID_PIXEL_REG;
737 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No x-coordinates, guessing registration to be %s since nx is odd\n", regtype[header->registration]);
738 }
739 }
740
741 /* Determine grid step */
742 header->inc[GMT_X] = gmt_M_get_inc (GMT, dummy[0], dummy[1], header->n_columns, registration);
743 if (gmt_M_is_dnan(header->inc[GMT_X]) || gmt_M_is_zero (header->inc[GMT_X])) header->inc[GMT_X] = 1.0;
744 if (header->n_columns == 1) registration = GMT_GRID_PIXEL_REG; /* The only way to have a grid like that */
745
746 #ifdef NC4_DEBUG
747 GMT_Report (GMT->parent, GMT_MSG_WARNING, "x registration: %u\n", header->registration);
748 GMT_Report (GMT->parent, GMT_MSG_WARNING, "x dummy: %g %g\n", dummy[0], dummy[1]);
749 GMT_Report (GMT->parent, GMT_MSG_WARNING, "x[0] x[nx-1]: %g %g\n", xy[0], xy[header->n_columns-1]);
750 GMT_Report (GMT->parent, GMT_MSG_WARNING, "xinc: %g %g\n", header->inc[GMT_X]);
751 #endif
752
753 /* Extend x boundaries by half if we found pixel registration */
754 if (registration == GMT_GRID_NODE_REG && header->registration == GMT_GRID_PIXEL_REG)
755 header->wesn[XLO] = dummy[0] - header->inc[GMT_X] / 2.0,
756 header->wesn[XHI] = dummy[1] + header->inc[GMT_X] / 2.0;
757 else /* Use as is */
758 header->wesn[XLO] = dummy[0], header->wesn[XHI] = dummy[1];
759
760 /* Get information about y variable */
761 gmtnc_get_units (GMT, ncid, ids[HH->xy_dim[1]], header->y_units);
762 /* Set default range to number of nodes in case nothing is found further down */
763 dummy[0] = 0.0, dummy[1] = (double) header->n_rows-1; /* Default */
764 registration = GMT_GRID_NODE_REG;
765
766 /* Read the y-coordinate vector (if available), otherwise just look for range attributes */
767 if ((has_vector = !nc_get_var_double (ncid, ids[HH->xy_dim[1]], xy)) && header->n_rows > 1) {
768 var_spacing = gmtnc_check_step (GMT, header->n_rows, xy, header->y_units, HH->name, save_xy_array);
769 if (save_xy_array && var_spacing) {
770 if (GMT->current.io.nc_yarray) gmt_M_free (GMT, GMT->current.io.nc_yarray);
771 GMT->current.io.nc_yarray = gmt_M_memory (GMT, NULL, header->n_rows, double);
772 gmt_M_memcpy (GMT->current.io.nc_yarray, xy, header->n_rows, double);
773 HH->var_spacing[GMT_Y] = var_spacing;
774 /* Flip-ud y-array since row = 0 is the last value */
775 gmt_grd_flip_vertical (GMT->current.io.nc_yarray, 1, header->n_rows, 0, sizeof(double));
776 }
777 dummy[0] = xy[0], dummy[1] = xy[header->n_rows-1];
778 dy = fabs (xy[1] - xy[0]); /* Grid spacing in y */
779 }
780 if (has_vector && header->n_rows == 1) has_vector = false; /* One coordinate does not a vector make */
781 has_range = (!nc_get_att_double (ncid, ids[HH->xy_dim[1]], "actual_range", dummy) ||
782 !nc_get_att_double (ncid, ids[HH->xy_dim[1]], "valid_range", dummy) ||
783 !(nc_get_att_double (ncid, ids[HH->xy_dim[1]], "valid_min", &dummy[0]) +
784 nc_get_att_double (ncid, ids[HH->xy_dim[1]], "valid_max", &dummy[1])));
785
786 if (has_vector && has_range) { /* Has both so we can do a basic sanity check */
787 threshold = (0.5+GMT_CONV5_LIMIT) * dy;
788 min = xy[0]; max = xy[header->n_rows-1];
789 if (min > max) gmt_M_double_swap (min, max); /* Got it backwards in the array */
790 if (fabs (dummy[0] - min) > threshold || fabs (dummy[1] - max) > threshold) {
791 if (gmtnc_not_obviously_polar (dummy)) {
792 GMT_Report (GMT->parent, GMT_MSG_WARNING, "The y-coordinates and range attribute are in conflict; must rely on coordinates only\n");
793 dummy[0] = xy[0], dummy[1] = xy[header->n_rows-1];
794 has_range = false; /* Since useless information */
795 /* Registration was set using x values, so here we just check that we get the same result.
796 * If the coordinates are off by 0.5*dy then we assume we have pixel registration */
797 threshold = (0.5-GMT_CONV5_LIMIT) * dy;
798 if (fabs (fmod (dummy[0], dy)) > threshold) { /* Pixel registration? */
799 if (header->registration == GMT_GRID_NODE_REG) /* No, somehow messed up now */
800 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Guessing of registration in conflict between x and y, using %s\n", regtype[header->registration]);
801 else { /* Pixel registration confirmed */
802 dummy[0] -= 0.5 * dy; dummy[1] += 0.5 * dy;
803 registration = GMT_GRID_PIXEL_REG;
804 if (gmt_M_180_range (dummy[0], dummy[1]))
805 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid y-coordinates after pixel registration adjustment have exactly 180 range\n");
806 }
807 }
808 }
809 else {
810 GMT_Report (GMT->parent, GMT_MSG_WARNING, "The y-coordinates and range attribute are in conflict but range is exactly 180; we rely on this range\n");
811 if ((header->n_rows%2) == 1 && header->registration == GMT_GRID_NODE_REG) /* Pixel registration? */
812 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Guessing of registration in conflict between x and y, using %s\n", regtype[header->registration]);
813 else
814 registration = header->registration;
815 }
816 }
817 else { /* Data seems OK; determine registration and set dummy from data coordinates */
818 dummy[0] = xy[0], dummy[1] = xy[header->n_rows-1];
819 if ((fabs(dummy[1] - dummy[0]) / fabs(xy[header->n_rows-1] - xy[0]) - 1.0 > 0.5 / (header->n_rows - 1)) && header->registration == GMT_GRID_NODE_REG)
820 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Guessing of registration in conflict between x and y, using %s\n", regtype[header->registration]);
821 }
822 }
823 else if (has_vector) { /* No attribute for range, use coordinates */
824 threshold = (0.5-GMT_CONV5_LIMIT) * dy;
825 dummy[0] = xy[0], dummy[1] = xy[header->n_rows-1];
826 if (fabs (fmod (dummy[0], dy)) > threshold) { /* Most likely pixel registration since off by dy/2 */
827 if (header->registration == GMT_GRID_NODE_REG) /* No, somehow messed up now */
828 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Guessing of registration in conflict between x and y, using %s\n", regtype[header->registration]);
829 else { /* Pixel registration confirmed */
830 if (dummy[0] > dummy[1]) { /* Check for reverse order of y-coordinate */
831 gmt_M_double_swap (dummy[0], dummy[1]);
832 HH->row_order = k_nc_start_north;
833 }
834 dummy[0] -= 0.5 * dy; dummy[1] += 0.5 * dy;
835 registration = GMT_GRID_PIXEL_REG;
836 if (gmt_M_180_range (dummy[0], dummy[1]))
837 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Grid y-coordinates after pixel registration adjustment have exactly 180 range\n");
838 }
839 }
840 }
841 else { /* Only has the valid_range settings. If no registration set, and no dy available, guess based on ny */
842 if ((header->n_rows%2) == 1 && header->registration == GMT_GRID_NODE_REG) /* Pixel registration? */
843 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Guessing of registration in conflict between x and y, using %s\n", regtype[header->registration]);
844 }
845
846 /* Check for reverse order of y-coordinate */
847 if (dummy[0] > dummy[1]) {
848 HH->row_order = k_nc_start_north;
849 gmt_M_double_swap (dummy[0], dummy[1]);
850 }
851 else if (has_vector && (xy[0] > xy[header->n_rows-1]) && (dummy[0] > dummy[1])) /* Here the lat vector is top-down but range is bottum-up */
852 HH->row_order = k_nc_start_north;
853 else if (HH->row_order != k_nc_start_north) /* If not already set to north in an above test */
854 HH->row_order = k_nc_start_south;
855
856 /* Determine grid step */
857 header->inc[GMT_Y] = gmt_M_get_inc (GMT, dummy[0], dummy[1], header->n_rows, registration);
858 if (gmt_M_is_dnan(header->inc[GMT_Y]) || gmt_M_is_zero (header->inc[GMT_Y])) header->inc[GMT_Y] = 1.0;
859 if (header->n_rows == 1) registration = GMT_GRID_PIXEL_REG; /* The only way to have a grid like that */
860
861 #ifdef NC4_DEBUG
862 GMT_Report (GMT->parent, GMT_MSG_WARNING, "y registration: %u\n", header->registration);
863 GMT_Report (GMT->parent, GMT_MSG_WARNING, "y dummy: %g %g\n", dummy[0], dummy[1]);
864 GMT_Report (GMT->parent, GMT_MSG_WARNING, "y[0] y[ny-1]: %g %g\n", xy[0], xy[header->n_rows-1]);
865 GMT_Report (GMT->parent, GMT_MSG_WARNING, "xinc: %g %g\n", header->inc[GMT_Y]);
866 #endif
867
868 /* Extend y boundaries by half if we found pixel registration */
869 if (registration == GMT_GRID_NODE_REG && header->registration == GMT_GRID_PIXEL_REG)
870 header->wesn[YLO] = dummy[0] - header->inc[GMT_Y] / 2.0,
871 header->wesn[YHI] = dummy[1] + header->inc[GMT_Y] / 2.0;
872 else /* Use as is */
873 header->wesn[YLO] = dummy[0], header->wesn[YHI] = dummy[1];
874
875 gmt_M_free (GMT, xy); /* Done with the array */
876
877 /* Get information about z variable */
878 gmtnc_get_units (GMT, ncid, z_id, header->z_units);
879 if (nc_get_att_double (ncid, z_id, "scale_factor", &header->z_scale_factor)) header->z_scale_factor = 1.0;
880 if (nc_get_att_double (ncid, z_id, "add_offset", &header->z_add_offset)) header->z_add_offset = 0.0;
881 #ifdef DOUBLE_PRECISION_GRID
882 if (nc_get_att_double (ncid, z_id, "_FillValue", &header->nan_value))
883 nc_get_att_double (ncid, z_id, "missing_value", &header->nan_value);
884 #else
885 if (nc_get_att_float (ncid, z_id, "_FillValue", &header->nan_value))
886 nc_get_att_float (ncid, z_id, "missing_value", &header->nan_value);
887 #endif
888 if (!nc_get_att_double (ncid, z_id, "actual_range", dummy)) {
889 /* z-limits need to be converted from actual to internal grid units. */
890 header->z_min = (dummy[0] - header->z_add_offset) / header->z_scale_factor;
891 header->z_max = (dummy[1] - header->z_add_offset) / header->z_scale_factor;
892 }
893 else if (!nc_get_att_double (ncid, z_id, "valid_range", dummy) ||
894 !(nc_get_att_double (ncid, ids[HH->xy_dim[1]], "valid_min", &dummy[0]) +
895 nc_get_att_double (ncid, ids[HH->xy_dim[1]], "valid_max", &dummy[1]))) {
896 /* Valid range is already in packed units, so do not convert */
897 header->z_min = dummy[0], header->z_max = dummy[1];
898 }
899 if (gmt_M_is_dnan (header->z_min) && gmt_M_is_dnan (header->z_max)) {
900 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "netCDF grid %s information has zmin = zmax = NaN. Reset to 0/0.\n", HH->name);
901 header->z_min = header->z_max = 0.0;
902 }
903 { /* Get deflation and chunking info */
904 int storage_mode, shuffle = 0, deflate = 0, deflate_level = 0;
905 size_t chunksize[5]; /* chunksize of z */
906 gmt_M_err_trap (nc_inq_var_chunking (ncid, z_id, &storage_mode, chunksize));
907 if (storage_mode == NC_CHUNKED) {
908 HH->z_chunksize[0] = chunksize[dims[0]]; /* chunk size in vertical dimension */
909 HH->z_chunksize[1] = chunksize[dims[1]]; /* chunk size in horizontal dimension */
910 }
911 else { /* NC_CONTIGUOUS */
912 HH->z_chunksize[0] = HH->z_chunksize[1] = 0;
913 }
914 if (HH->is_netcdf4) gmt_M_err_trap (nc_inq_var_deflate (ncid, z_id, &shuffle, &deflate, &deflate_level));
915 HH->z_shuffle = shuffle ? true : false; /* if shuffle filter is turned on */
916 HH->z_deflate_level = deflate ? deflate_level : 0; /* if deflate filter is in use */
917 }
918
919 /* Determine t_index from t_value */
920 item[0] = 0;
921 for (i = 0; i < n_t_index_by_value; i++) {
922 item[1] = lens[i]-1;
923 if (nc_get_att_double (ncid, ids[i], "actual_range", dummy)) {
924 gmt_M_err_trap (nc_get_var1_double (ncid, ids[i], &item[0], &dummy[0]));
925 gmt_M_err_trap (nc_get_var1_double (ncid, ids[i], &item[1], &dummy[1]));
926 }
927 if (item[1] != 0 && dummy[0] != dummy[1]) { /* avoid dvision by 0 */
928 double index = (t_value[i] - dummy[0]) / (dummy[1] - dummy[0]) * item[1];
929 if (index > 0)
930 HH->t_index[i] = (size_t)index;
931 }
932 }
933
934 #ifdef NC4_DEBUG
935 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->wesn: %g %g %g %g\n",
936 header->wesn[XLO], header->wesn[XHI], header->wesn[YLO], header->wesn[YHI]);
937 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->registration:%u\n", header->registration);
938 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->row_order: %s\n",
939 HH->row_order == k_nc_start_south ? "S->N" : "N->S");
940 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->n_columns: %3d head->n_rows:%3d\n", header->n_columns, header->n_rows);
941 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->inc: %g %g\n", header->inc[GMT_X], header->inc[GMT_Y]);
942 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->mx: %3d head->my:%3d\n", header->mx, header->my);
943 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->nm: %3d head->size:%3d\n", header->nm, header->size);
944 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->t-index %d,%d,%d\n",
945 HH->t_index[0], HH->t_index[1], HH->t_index[2]);
946 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->pad xlo:%u xhi:%u ylo:%u yhi:%u\n",
947 header->pad[XLO], header->pad[XHI], header->pad[YLO], header->pad[YHI]);
948 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->BC xlo:%u xhi:%u ylo:%u yhi:%u\n",
949 HH->BC[XLO], HH->BC[XHI], HH->BC[YLO], HH->BC[YHI]);
950 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->grdtype:%u %u\n", HH->grdtype, GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT);
951 #endif
952 }
953 else { /* Here we are writing */
954 /* Store global attributes */
955 const int *nc_vers = gmtnc_netcdf_libvers();
956 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "netCDF Library version: %d\n", *nc_vers);
957 gmt_M_err_trap (nc_put_att_text (ncid, NC_GLOBAL, "Conventions", strlen(GMT_NC_CONVENTION), GMT_NC_CONVENTION));
958 if (header->title[0]) gmt_M_err_trap (nc_put_att_text (ncid, NC_GLOBAL, "title", strlen(header->title), header->title));
959 gmt_M_err_trap (nc_put_att_text (ncid, NC_GLOBAL, "history", strlen(header->command), header->command));
960 if (header->remark[0]) gmt_M_err_trap (nc_put_att_text (ncid, NC_GLOBAL, "description", strlen(header->remark), header->remark));
961 gmt_M_err_trap (nc_put_att_text (ncid, NC_GLOBAL, "GMT_version", strlen(GMT_VERSION), (const char *) GMT_VERSION));
962 if (header->registration == GMT_GRID_PIXEL_REG) {
963 int reg = header->registration;
964 gmt_M_err_trap (nc_put_att_int (ncid, NC_GLOBAL, "node_offset", NC_LONG, 1U, ®));
965 }
966 else
967 nc_del_att (ncid, NC_GLOBAL, "node_offset");
968
969 #ifdef HAVE_GDAL
970 /* If we have projection information create a container variable named "grid_mapping" with an attribute
971 "spatial_ref" that will hold the projection info in WKT format. GDAL and Mirone know use this info */
972 if ((header->ProjRefWKT != NULL) || (header->ProjRefPROJ4 != NULL)) {
973 int id[1], dim[1];
974
975 if (header->ProjRefWKT == NULL || !header->ProjRefWKT[0]) { /* Must convert from proj4 string to WKT */
976 OGRSpatialReferenceH hSRS = OSRNewSpatialReference(NULL);
977
978 if (header->ProjRefPROJ4 && (!strncmp(header->ProjRefPROJ4, "+unavailable", 4) || strlen(header->ProjRefPROJ4) <= 5)) { /* Silently jump out of here */
979 OSRDestroySpatialReference(hSRS);
980 goto L100;
981 }
982 GMT_Report(GMT->parent, GMT_MSG_INFORMATION, "Proj4 string to be converted to WKT:\n\t%s\n", header->ProjRefPROJ4);
983 if (OSRImportFromProj4(hSRS, header->ProjRefPROJ4) == CE_None) {
984 char *pszPrettyWkt = NULL;
985 OSRExportToPrettyWkt(hSRS, &pszPrettyWkt, false);
986 header->ProjRefWKT = strdup(pszPrettyWkt);
987 CPLFree(pszPrettyWkt);
988 GMT_Report(GMT->parent, GMT_MSG_INFORMATION, "WKT converted from proj4 string:\n%s\n", header->ProjRefWKT);
989 }
990 else {
991 header->ProjRefWKT = NULL;
992 GMT_Report(GMT->parent, GMT_MSG_WARNING, "gmtnc_grd_info failed to convert the proj4 string\n%s\n to WKT\n",
993 header->ProjRefPROJ4);
994 }
995 OSRDestroySpatialReference(hSRS);
996 }
997
998 if (header->ProjRefWKT != NULL) { /* It may be NULL if the above conversion failed */
999 if (nc_inq_varid(ncid, "grid_mapping", &id[0]) != NC_NOERR) {
1000 gmt_M_err_trap(nc_def_dim(ncid, "grid_mapping", 12U, &dim[0]));
1001 gmt_M_err_trap(nc_def_var(ncid, "grid_mapping", NC_CHAR, 1, dim, &id[0]));
1002 }
1003 gmt_M_err_trap(nc_put_att_text(ncid, id[0], "spatial_ref", strlen(header->ProjRefWKT), header->ProjRefWKT));
1004 gmt_M_err_trap(nc_put_att_text(ncid, z_id, "grid_mapping", 12U, "grid_mapping")); /* Create attrib in z variable */
1005 }
1006 }
1007 L100:
1008 #endif
1009
1010 /* Avoid NaN increments */
1011 if (gmt_M_is_dnan(header->inc[GMT_X])) header->inc[GMT_X] = 1.0;
1012 if (gmt_M_is_dnan(header->inc[GMT_Y])) header->inc[GMT_Y] = 1.0;
1013
1014 /* Define x variable */
1015 gmtnc_put_units (ncid, ids[HH->xy_dim[0]], header->x_units);
1016 dummy[0] = header->wesn[XLO], dummy[1] = header->wesn[XHI];
1017 gmt_M_err_trap (nc_put_att_double (ncid, ids[HH->xy_dim[0]], "actual_range", NC_DOUBLE, 2U, dummy));
1018 gmt_M_err_trap (nc_put_att_text (ncid, ids[HH->xy_dim[0]], "axis", 1U, "X"));
1019
1020 /* Define y variable */
1021 gmtnc_put_units (ncid, ids[HH->xy_dim[1]], header->y_units);
1022 dummy[0] = header->wesn[YLO], dummy[1] = header->wesn[YHI];
1023 gmt_M_err_trap (nc_put_att_double (ncid, ids[HH->xy_dim[1]], "actual_range", NC_DOUBLE, 2U, dummy));
1024 gmt_M_err_trap (nc_put_att_text (ncid, ids[HH->xy_dim[1]], "axis", 1U, "Y"));
1025
1026 /* When varname is given, and z_units is default, overrule z_units with varname */
1027 if (HH->varname[0] && !strcmp (header->z_units, cube ? "cube" : "z"))
1028 strncpy (header->z_units, HH->varname, GMT_GRID_UNIT_LEN80-1);
1029
1030 /* Define z variable. Attempt to remove "scale_factor" or "add_offset" when no longer needed */
1031 gmtnc_put_units (ncid, z_id, header->z_units);
1032
1033 if (header->z_scale_factor != 1.0) {
1034 gmt_M_err_trap (nc_put_att_double (ncid, z_id, "scale_factor", NC_DOUBLE, 1U, &header->z_scale_factor));
1035 }
1036 else if (job == 'u')
1037 nc_del_att (ncid, z_id, "scale_factor");
1038
1039 if (header->z_add_offset != 0.0) {
1040 gmt_M_err_trap (nc_put_att_double (ncid, z_id, "add_offset", NC_DOUBLE, 1U, &header->z_add_offset));
1041 }
1042 else if (job == 'u')
1043 nc_del_att (ncid, z_id, "add_offset");
1044
1045 if (z_type != NC_FLOAT && z_type != NC_DOUBLE)
1046 header->nan_value = rintf (header->nan_value); /* round to integer */
1047 if (job == 'u' && HH->is_netcdf4) {
1048 /* netCDF-4 has a bug and crash when * rewriting the _FillValue attribute in netCDF-4 files
1049 https://bugtracking.unidata.ucar.edu/browse/NCF-187
1050 To work-around it we implement the renaming trick advised on NCF-133
1051 Edit: This work-around should eventually be removed because the bug was fixed as of 2015-04-02 (any version >4.3.3.1 should be fine).
1052 */
1053 gmt_M_err_trap (nc_rename_att (ncid, z_id, "_FillValue", "_fillValue"));
1054 #ifdef DOUBLE_PRECISION_GRID
1055 gmt_M_err_trap (nc_put_att_double (ncid, z_id, "_fillValue", z_type, 1U, &header->nan_value));
1056 #else
1057 gmt_M_err_trap (nc_put_att_float (ncid, z_id, "_fillValue", z_type, 1U, &header->nan_value));
1058 #endif
1059 gmt_M_err_trap (nc_rename_att (ncid, z_id, "_fillValue", "_FillValue"));
1060 }
1061 else {
1062 #ifdef DOUBLE_PRECISION_GRID
1063 gmt_M_err_trap (nc_put_att_double (ncid, z_id, "_FillValue", z_type, 1U, &header->nan_value));
1064 #else
1065 gmt_M_err_trap (nc_put_att_float (ncid, z_id, "_FillValue", z_type, 1U, &header->nan_value));
1066 #endif
1067 }
1068
1069 /* Limits need to be stored in actual, not internal grid, units */
1070 if (header->z_min <= header->z_max) {
1071 dummy[0] = header->z_min * header->z_scale_factor + header->z_add_offset;
1072 dummy[1] = header->z_max * header->z_scale_factor + header->z_add_offset;
1073 }
1074 else
1075 dummy[0] = 0.0, dummy[1] = 0.0;
1076 gmt_M_err_trap (nc_put_att_double (ncid, z_id, "actual_range", NC_DOUBLE, 2U, dummy));
1077
1078 /* Store values along x and y axes */
1079 nc_inq_nvars (ncid, &nvars);
1080 for (j = 0; j < nvars; j++) {
1081 gmt_M_err_trap (nc_inq_varndims (ncid, j, &ndims));
1082 }
1083 if (!cube) { /* Hold off on this until we also have the 3rd dimension */
1084 gmt_M_err_trap (nc_enddef (ncid)); /* End definition mode */
1085 gmtnc_put_xy_vectors (GMT, header); /* Place x and y vectors */
1086 }
1087 }
1088
1089 /* Close NetCDF file, unless job == 'W' */
1090
1091 if (job != 'W') gmt_M_err_trap (gmt_nc_close (GMT, ncid));
1092 return (GMT_NOERROR);
1093 }
1094
1095 /* Shift columns in a grid to the right (n_shift < 0) or to the left (n_shift < 0) */
gmtnc_right_shift_grid(void * gridp,const unsigned n_cols,const unsigned n_rows,int n_shift,size_t cell_size)1096 GMT_LOCAL void gmtnc_right_shift_grid (void *gridp, const unsigned n_cols, const unsigned n_rows, int n_shift, size_t cell_size) {
1097 char *tmp = NULL, *grid = (char*)gridp;
1098 size_t row, n_shift_abs = abs (n_shift), nm;
1099 size_t n_cols_t = (size_t)n_cols, n_rows_t = (size_t)n_rows;
1100
1101 assert (n_shift_abs != 0 && n_cols_t > n_shift_abs && n_cols_t > 0 && n_rows_t > 0);
1102
1103 tmp = calloc (n_shift_abs, cell_size);
1104
1105 if (n_shift > 0) { /* right shift */
1106 for (row = 0; row < n_rows_t; ++row) {
1107 nm = row * n_cols_t;
1108 /* copy last n_shift_abs cols into tmp buffer */
1109 memcpy (tmp, grid + (nm + n_cols_t - n_shift_abs) * cell_size, n_shift_abs * cell_size);
1110 /* right shift row */
1111 memmove (grid + (nm + n_shift_abs) * cell_size,
1112 grid + nm * cell_size,
1113 (n_cols_t - n_shift_abs) * cell_size);
1114 /* prepend tmp buffer */
1115 memcpy (grid + nm * cell_size, tmp, n_shift_abs * cell_size);
1116 }
1117 }
1118 else { /* n_shift_abs < 0 */
1119 for (row = 0; row < n_rows_t; ++row) {
1120 nm = row * n_cols_t;
1121 /* copy first n_shift_abs cols into tmp buffer */
1122 memcpy (tmp, grid + nm * cell_size, n_shift_abs * cell_size);
1123 /* left shift row */
1124 memmove (grid + nm * cell_size,
1125 grid + (nm + n_shift_abs) * cell_size,
1126 (n_cols_t - n_shift_abs) * cell_size);
1127 /* append tmp buffer */
1128 memcpy (grid + (nm + n_cols_t - n_shift_abs) * cell_size, tmp, n_shift_abs * cell_size);
1129 }
1130 }
1131 gmt_M_str_free (tmp);
1132 }
1133
1134 /* Fill padding by replicating the border cells or wrapping around a
1135 * row if columns are periodic */
gmtnc_padding_copy(void * gridp,const unsigned n_cols,const unsigned n_rows,const unsigned * n_pad,size_t cell_size,bool periodic_cols)1136 GMT_LOCAL void gmtnc_padding_copy (void *gridp, const unsigned n_cols, const unsigned n_rows, const unsigned *n_pad, size_t cell_size, bool periodic_cols) {
1137 /* n_cols and n_rows are dimensions of the padded grid */
1138 char *grid = (char*)gridp;
1139 size_t row, cell, nm, nm2;
1140 size_t n_cols_t = (size_t)n_cols, n_rows_t = (size_t)n_rows;
1141 size_t pad_t[4];
1142
1143 assert (n_cols > n_pad[XLO] + n_pad[XHI] && n_rows > n_pad[YLO] + n_pad[YHI] &&
1144 n_pad[XLO] + n_pad[XHI] + n_pad[YLO] + n_pad[YHI] > 0 && cell_size > 0);
1145
1146 for (row = 0; row < 4; row++) pad_t[row] = (size_t)n_pad[row];
1147
1148 if (periodic_cols) {
1149 /* A periodic grid wraps around */
1150 for (row = pad_t[YHI]; (row + pad_t[YLO]) < n_rows_t; ++row) {
1151 nm = row * n_cols_t;
1152 /* Iterate over rows that contain data */
1153 for (cell = 0; cell < pad_t[XLO]; ++cell) {
1154 /* Copy end of this row into first pad_t[XLO] columns:
1155 * X X 0 1 2 3 4 5 X X -> 4 5 0 1 2 3 4 5 X X */
1156 memcpy (grid + (nm + cell) * cell_size,
1157 grid + (nm + n_cols_t + cell - pad_t[XLO] - pad_t[XHI]) * cell_size,
1158 cell_size);
1159 }
1160 for (cell = 0; cell < pad_t[XHI]; ++cell) {
1161 /* Copy start of this row into last n_pad[XHI] columns:
1162 * 4 5 0 1 2 3 4 5 X X -> 4 5 0 1 2 3 4 5 0 1 */
1163 memcpy (grid + (nm + n_cols_t - cell - 1) * cell_size,
1164 grid + (nm + pad_t[XLO] + pad_t[XHI] - cell - 1) * cell_size,
1165 cell_size);
1166 }
1167 }
1168 }
1169 else { /* !periodic_cols */
1170 for (row = pad_t[YHI]; (row + pad_t[YLO]) < n_rows_t; ++row) {
1171 nm = row * n_cols_t;
1172 /* Iterate over rows that contain data */
1173 for (cell = 0; cell < pad_t[XLO]; ++cell) {
1174 /* Duplicate first pad_t[XLO] columns in this row:
1175 * 4 5 0 1 2 3 4 5 X X -> 0 0 0 1 2 3 4 5 X X */
1176 memcpy (grid + (nm + cell) * cell_size,
1177 grid + (nm + pad_t[XLO]) * cell_size,
1178 cell_size);
1179 }
1180 for (cell = 0; cell < pad_t[XHI]; ++cell) {
1181 /* Duplicate last pad_t[XHI] columns in this row:
1182 * 0 0 0 1 2 3 4 5 X X -> 0 0 0 1 2 3 4 5 5 5 */
1183 memcpy (grid + (nm + n_cols_t - cell - 1) * cell_size,
1184 grid + (nm + n_cols_t - pad_t[XHI] - 1) * cell_size,
1185 cell_size);
1186 }
1187 }
1188 }
1189
1190 for (cell = 0; cell < pad_t[YHI]; ++cell) {
1191 nm = cell * n_cols_t;
1192 /* Duplicate pad_t[YHI] rows in the beginning */
1193 memcpy (grid + nm * cell_size,
1194 grid + pad_t[YHI] * n_cols_t * cell_size,
1195 n_cols_t * cell_size);
1196 }
1197 nm2 = (n_rows_t - pad_t[YLO] - 1) * n_cols_t;
1198 for (cell = 0; cell < pad_t[YLO]; ++cell) {
1199 nm = (n_rows_t - cell - 1) * n_cols_t;
1200 /* Duplicate last pad_t[YLO] rows */
1201 memcpy (grid + nm * cell_size,
1202 grid + nm2 * cell_size,
1203 n_cols_t * cell_size);
1204 }
1205 }
1206
1207 /* Fill padding with zeros */
gmtnc_padding_zero(void * gridp,const unsigned n_cols,const unsigned n_rows,const unsigned * n_pad,size_t cell_size)1208 GMT_LOCAL void gmtnc_padding_zero (void *gridp, const unsigned n_cols, const unsigned n_rows, const unsigned *n_pad, size_t cell_size) {
1209 /* n_cols and n_rows are dimensions of the padded grid */
1210 char *grid = (char*)gridp;
1211 size_t row, nm;
1212 size_t n_cols_t = (size_t)n_cols, n_rows_t = (size_t)n_rows;
1213 size_t pad_t[4];
1214
1215 assert (n_cols > n_pad[XLO] + n_pad[XHI] && n_rows > n_pad[YLO] + n_pad[YHI] &&
1216 n_pad[XLO] + n_pad[XHI] + n_pad[YLO] + n_pad[YHI] > 0 && cell_size > 0);
1217
1218 for (row = 0; row < 4; row++) pad_t[row] = (size_t)n_pad[row];
1219
1220 /* Iterate over rows that contain data */
1221 for (row = pad_t[YHI]; (row + pad_t[YLO]) < n_rows_t; ++row) {
1222 nm = row * n_cols_t;
1223 /* Zero n cells at beginning of row */
1224 memset (grid + nm * cell_size, 0, pad_t[XLO] * cell_size);
1225 /* Zero n cells at end of row */
1226 memset (grid + (nm + n_cols_t - pad_t[XHI]) * cell_size, 0, pad_t[XHI] * cell_size);
1227 }
1228 /* Zero pad_t[YHI] rows in the beginning */
1229 memset (grid, 0, pad_t[YHI] * n_cols_t * cell_size);
1230 /* Zero last pad_t[YLO] rows */
1231 memset(grid + (n_rows_t-pad_t[YLO]) * n_cols_t * cell_size, 0, pad_t[YLO] * n_cols_t * cell_size);
1232 }
1233
1234 /* Fill mode for grid padding */
1235 enum Grid_padding_mode {
1236 k_pad_fill_none = 0, /* Leave padded cells untouched */
1237 k_pad_fill_zero, /* Fill padded grid cells with zeros */
1238 k_pad_fill_copy, /* Padded cells get the value of their nearest neighbor */
1239 k_pad_fill_copy_wrap /* Padded cells get wrapped values from the other side of the row (gridswith periodic columns) */
1240 };
1241
1242 /* Add padding to a matrix/grid and reshape data */
gmtnc_pad_grid(void * gridp,const unsigned n_cols,const unsigned n_rows,const unsigned * n_pad,size_t cell_size,unsigned filltype)1243 GMT_LOCAL void gmtnc_pad_grid (void *gridp, const unsigned n_cols, const unsigned n_rows, const unsigned *n_pad, size_t cell_size, unsigned filltype) {
1244 /* n_cols and n_rows are dimensions of the grid without padding
1245 * cell_size is the size in bytes of each element in grid
1246 * n_pad[4] contains the number of cols/rows to pad on each side {W,E,S,N}
1247 *
1248 * Note: when grid is complex, we pass 2x n_rows */
1249 char *grid = (char*)gridp;
1250 size_t n_cols_t = (size_t)n_cols, n_rows_t = (size_t)n_rows;
1251 size_t new_row;
1252 size_t old_row = n_rows_t-1;
1253 size_t n_new_cols = n_cols_t + n_pad[XLO] + n_pad[XHI];
1254 size_t n_new_rows = n_rows_t + n_pad[YLO] + n_pad[YHI];
1255
1256 #ifdef NC4_DEBUG
1257 fprintf (stderr, "pad grid w:%u e:%u s:%u n:%u\n",
1258 n_pad[XLO], n_pad[XHI], n_pad[YLO], n_pad[YHI]);
1259 #endif
1260 if (n_pad[XLO] + n_pad[XHI] + n_pad[YLO] + n_pad[YHI] == 0)
1261 return; /* nothing to pad */
1262
1263 assert (n_cols > 0 && n_rows > 0 && cell_size > 0);
1264
1265 /* Reshape matrix */
1266 if (n_pad[XLO] + n_pad[XHI] + n_pad[YHI] != 0) {
1267 /* When padding W, E, and N (not necessary when padding S only). */
1268 for (new_row = n_new_rows - n_pad[YLO] - 1; new_row + 1 > n_pad[YHI]; --new_row, --old_row) {
1269 /* Copy original row to new row, bottom upwards */
1270 void *from = grid + old_row * n_cols_t * cell_size;
1271 void *to = grid + (new_row * n_new_cols + ((size_t)n_pad[XLO])) * cell_size;
1272 if (n_pad[YHI] == 0) /* rows overlap! */
1273 memmove (to, from, n_cols_t * cell_size);
1274 else /* no overlap, memcpy is safe */
1275 memcpy (to, from, n_cols_t * cell_size);
1276 }
1277 }
1278
1279 /* Fill padded grid cells */
1280 switch (filltype) {
1281 case k_pad_fill_zero:
1282 gmtnc_padding_zero (grid, (const unsigned)n_new_cols, (const unsigned)n_new_rows, n_pad, cell_size);
1283 break;
1284 case k_pad_fill_copy:
1285 gmtnc_padding_copy (grid, (const unsigned)n_new_cols, (const unsigned)n_new_rows, n_pad, cell_size, false);
1286 break;
1287 case k_pad_fill_copy_wrap:
1288 gmtnc_padding_copy (grid, (const unsigned)n_new_cols, (const unsigned)n_new_rows, n_pad, cell_size, true);
1289 break;
1290 }
1291 }
1292
1293 /* Remove padding from a matrix/grid and reshape data */
gmtnc_unpad_grid(void * gridp,const unsigned n_cols,const unsigned n_rows,const unsigned * n_pad,size_t cell_size)1294 GMT_LOCAL void gmtnc_unpad_grid (void *gridp, const unsigned n_cols, const unsigned n_rows, const unsigned *n_pad, size_t cell_size) {
1295 /* n_cols and n_rows are dimensions of the grid without padding
1296 * cell_size is the size in bytes of each element in grid
1297 * n_pad[4] contains the number of cols/rows to pad on each side {W,E,S,N}
1298 *
1299 * Note: when grid is complex, we pass 2x n_rows */
1300 char *grid = (char*)gridp;
1301 size_t n_old_cols = n_cols + n_pad[XLO] + n_pad[XHI];
1302 size_t n_cols_t = (size_t)n_cols, n_rows_t = (size_t)n_rows;
1303 size_t row;
1304
1305 #ifdef NC4_DEBUG
1306 fprintf (stderr, "unpad grid w:%u e:%u s:%u n:%u\n",
1307 n_pad[XLO], n_pad[XHI], n_pad[YLO], n_pad[YHI]);
1308 #endif
1309 if (n_pad[XLO] + n_pad[XHI] + n_pad[YHI] == 0)
1310 return; /* nothing to unpad (we just ignore n_pad[YLO]) */
1311
1312 assert (n_cols > 0 && n_rows > 0 && cell_size > 0);
1313
1314 /* Reshape matrix */
1315 for (row = 0; row < n_rows_t; ++row) {
1316 size_t old_row = row + (size_t)n_pad[YHI];
1317 void *from = grid + (old_row * n_old_cols + ((size_t)n_pad[XLO])) * cell_size;
1318 void *to = grid + row * n_cols_t * cell_size;
1319 /* Copy original row to new row */
1320 if (n_pad[YHI] == 0) /* rows overlap! */
1321 memmove (to, from, n_cols_t * cell_size);
1322 else /* no overlap, memcpy is safe */
1323 memcpy (to, from, n_cols_t * cell_size);
1324 }
1325 }
1326
1327 /* Ensure that repeating columns in geographic gridline registered grids
1328 * do not contain conflicting information */
gmtnc_grid_fix_repeat_col(struct GMT_CTRL * GMT,void * gridp,const unsigned n_cols,const unsigned n_rows,size_t cell_size)1329 GMT_LOCAL void gmtnc_grid_fix_repeat_col (struct GMT_CTRL *GMT, void *gridp, const unsigned n_cols, const unsigned n_rows, size_t cell_size) {
1330 /* Note: when grid is complex, pass 2x n_rows */
1331 char *grid = (char*)gridp;
1332 unsigned n_conflicts = 0;
1333 size_t row;
1334 size_t n_cols_t = (size_t)n_cols, n_rows_t = (size_t)n_rows;
1335
1336 for (row = 0; row < n_rows_t; ++row) {
1337 char *first = grid + row * n_cols_t * cell_size; /* first element in row */
1338 char *last = grid + (row * n_cols_t + n_cols_t - 1) * cell_size; /* last element in row */
1339 if ( memcmp(last, first, cell_size) ) {
1340 /* elements differ: replace value of last element in row with value of first */
1341 memcpy (last, first, cell_size);
1342 ++n_conflicts;
1343 }
1344 }
1345
1346 if (n_conflicts)
1347 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Detected %u inconsistent values along east boundary of grid. Values fixed by duplicating west boundary.\n", n_conflicts);
1348 }
1349
1350 /* Change the default chunk cache settings in the HDF5 library for all variables
1351 * in the nc-file. The settings apply for subsequent file opens/creates. */
gmtnc_setup_chunk_cache(void)1352 static inline void gmtnc_setup_chunk_cache (void) {
1353 static bool already_setup = false;
1354 if (!already_setup) {
1355 nc_set_chunk_cache (NC_CACHE_SIZE, NC_CACHE_NELEMS, NC_CACHE_PREEMPTION);
1356 already_setup = true;
1357 }
1358 }
1359
1360
gmtnc_grd_prep_io(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,double wesn[4],unsigned int * width,unsigned int * height,int * n_shift,unsigned origin[2],unsigned dim[2],unsigned origin2[2],unsigned dim2[2])1361 GMT_LOCAL int gmtnc_grd_prep_io (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, double wesn[4], unsigned int *width, unsigned int *height, int *n_shift, unsigned origin[2], unsigned dim[2], unsigned origin2[2], unsigned dim2[2]) {
1362 /* Determines which rows and columns to extract from a grid, based on
1363 * w,e,s,n. This routine first rounds the w,e,s,n boundaries to the nearest
1364 * gridlines or pixels, then determines the first and last columns and rows,
1365 * and the width and height of the subset (in cells).
1366 */
1367 bool is_gridline_reg, is_global, is_global_repeat;
1368 unsigned last_row, first_col, last_col, first_row;
1369 unsigned first_col2, last_col2;
1370 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1371
1372 *n_shift = 0;
1373 memset (origin2, 0, 2 * sizeof(unsigned));
1374 memset (dim2, 0, 2 * sizeof(unsigned));
1375
1376 is_global = gmt_grd_is_global (GMT, header);
1377 is_global_repeat = HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT;
1378 is_gridline_reg = header->registration != GMT_GRID_PIXEL_REG;
1379
1380 #ifdef NC4_DEBUG
1381 GMT_Report (GMT->parent, GMT_MSG_WARNING, " x-region: %g %g, grid: %g %g\n", wesn[XLO], wesn[XHI], header->wesn[XLO], header->wesn[XHI]);
1382 GMT_Report (GMT->parent, GMT_MSG_WARNING, " y-region: %g %g, grid: %g %g\n", wesn[YLO], wesn[YHI], header->wesn[YLO], header->wesn[YHI]);
1383 #endif
1384
1385 if (wesn[XLO] == 0 && wesn[XHI] == 0 && wesn[YLO] == 0 && wesn[YHI] == 0) {
1386 /* When -R... matches the exact dimensions of the grid: read whole grid */
1387 *width = header->n_columns;
1388 *height = header->n_rows;
1389 first_col = first_row = 0;
1390 last_col = header->n_columns - 1;
1391 last_row = header->n_rows - 1;
1392 gmt_M_memcpy (wesn, header->wesn, 4, double);
1393 }
1394 else {
1395 /* Must deal with a subregion */
1396 double x;
1397 x = fabs (header->wesn[YLO] - wesn[YLO]); /* if |x| < GMT_CONV4_LIMIT * header->inc[GMT_Y] we set wesn to the grid limit */
1398 if (x > 0.0 && x < GMT_CONV4_LIMIT * header->inc[GMT_Y]) wesn[YLO] = header->wesn[YLO]; /* Avoid snafu */
1399 x = fabs (header->wesn[YHI] - wesn[YHI]); /* if |x| < GMT_CONV4_LIMIT * header->inc[GMT_Y] we set wesn to the grid limit */
1400 if (x > 0.0 && x < GMT_CONV4_LIMIT * header->inc[GMT_Y]) wesn[YHI] = header->wesn[YHI]; /* Avoid snafu */
1401 if (wesn[YLO] < header->wesn[YLO] || wesn[YHI] > header->wesn[YHI])
1402 return (GMT_GRDIO_DOMAIN_VIOLATION); /* Calling program goofed... */
1403
1404 /* Make sure w,e,s,n are proper multiples of x_inc,y_inc away from x_min,y_min */
1405 gmt_M_err_pass (GMT, gmt_adjust_loose_wesn (GMT, wesn, header), HH->name);
1406
1407 /* Global grids: ensure that wesn >= header->wesn (w+e only) */
1408 if ( is_global ) { /* Deal with wrapping issues */
1409 while (wesn[XLO] > header->wesn[XHI]) {
1410 wesn[XLO] -= 360;
1411 wesn[XHI] -= 360;
1412 }
1413 while (wesn[XLO] < header->wesn[XLO]) {
1414 wesn[XLO] += 360;
1415 wesn[XHI] += 360;
1416 }
1417 }
1418 else /* Apply a 10^-4 times inc sloppiness in the test [it was zero which was too harsh for sloppy grids] */
1419 assert ((wesn[XLO]+GMT_CONV4_LIMIT*header->inc[GMT_X]) >= header->wesn[XLO] && (wesn[XHI]-GMT_CONV4_LIMIT*header->inc[GMT_X]) <= header->wesn[XHI]);
1420
1421 /* Get dimension of subregion */
1422 *width = urint ((wesn[XHI] - wesn[XLO]) * HH->r_inc[GMT_X]) + is_gridline_reg;
1423 *height = urint ((wesn[YHI] - wesn[YLO]) * HH->r_inc[GMT_Y]) + is_gridline_reg;
1424
1425 /* Get first and last row and column numbers */
1426 first_col = urint ((wesn[XLO] - header->wesn[XLO]) * HH->r_inc[GMT_X]);
1427 last_col = urint ((wesn[XHI] - header->wesn[XLO]) * HH->r_inc[GMT_X]) + is_gridline_reg - 1;
1428 first_row = urint ((header->wesn[YHI] - wesn[YHI]) * HH->r_inc[GMT_Y]);
1429 last_row = urint ((header->wesn[YHI] - wesn[YLO]) * HH->r_inc[GMT_Y]) + is_gridline_reg - 1;
1430
1431 /* Adjust first_row */
1432 if (HH->row_order == k_nc_start_south)
1433 first_row = header->n_rows - 1 - last_row;
1434
1435 /* Global grids: if -R + padding is equal or exceeds grid bounds */
1436 if (is_global && 1 + is_global_repeat + last_col - first_col >= header->n_columns) {
1437 /* Number of requested cols >= n_columns: read whole grid and shift */
1438 *n_shift = -(int)first_col;
1439 first_col = 0;
1440 last_col = header->n_columns - 1;
1441 }
1442 else if (is_global && last_col + 1 > header->n_columns) {
1443 /* Subset of a global grid that wraps around east boundary. This means
1444 * we have to read the grid in two parts and stich them together. */
1445 first_col2 = 0;
1446 last_col2 = is_global_repeat + last_col - header->n_columns;
1447 last_col = header->n_columns - 1;
1448 #ifdef NC4_DEBUG
1449 GMT_Report (GMT->parent, GMT_MSG_WARNING, "col2: %u %u\n", first_col2, last_col2);
1450 #endif
1451
1452 origin2[0] = first_row;
1453 origin2[1] = first_col2;
1454 dim2[0] = *height;
1455 assert (first_col2 <= 1 + last_col2); /* check for unsigned overflow */
1456 dim2[1] = 1 + last_col2 - first_col2;
1457 }
1458 assert (last_col + 1 <= header->n_columns);
1459 }
1460
1461 /* Do not read last column from global repeating grids */
1462 if (is_global_repeat && last_col + 1 == header->n_columns)
1463 --last_col;
1464
1465 origin[0] = first_row;
1466 origin[1] = first_col;
1467 dim[0] = *height;
1468 assert (first_col <= 1 + last_col); /* check for unsigned overflow */
1469 dim[1] = 1 + last_col - first_col;
1470
1471 #ifdef NC4_DEBUG
1472 GMT_Report (GMT->parent, GMT_MSG_WARNING, "-> x-region: %g %g, grid: %g %g\n", wesn[XLO], wesn[XHI], header->wesn[XLO], header->wesn[XHI]);
1473 GMT_Report (GMT->parent, GMT_MSG_WARNING, "-> y-region: %g %g, grid: %g %g\n", wesn[YLO], wesn[YHI], header->wesn[YLO], header->wesn[YHI]);
1474 GMT_Report (GMT->parent, GMT_MSG_WARNING, "row: %u %u col: %u %u r_shift: %d\n", first_row, last_row, first_col, last_col, *n_shift);
1475 GMT_Report (GMT->parent, GMT_MSG_WARNING, "origin : %u,%u dim : %u,%u\n", origin[0], origin[1], dim[0], dim[1]);
1476 GMT_Report (GMT->parent, GMT_MSG_WARNING, "origin2: %u,%u dim2: %u,%u\n", origin2[0], origin2[1], dim2[0], dim2[1]);
1477 #endif
1478
1479 return GMT_NOERROR;
1480 }
1481
gmt_nc_create(struct GMT_CTRL * GMT,char * path,int mode,int * id)1482 int gmt_nc_create (struct GMT_CTRL *GMT, char *path, int mode, int *id) {
1483 int err = nc_create (path, mode, id);
1484 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Calling nc_create on %s, ncid = %d, err = %d\n", path, *id, err);
1485 return (err);
1486 }
1487
gmt_nc_open(struct GMT_CTRL * GMT,char * path,int mode,int * id)1488 int gmt_nc_open (struct GMT_CTRL *GMT, char *path, int mode, int *id) {
1489 int err = nc_open (path, mode, id); /* Open the file, return error code */
1490 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Calling nc_open on %s, ncid = %d, err = %d\n", path, *id, err);
1491 return err; /* Open the file, return error code */
1492 }
1493
gmt_nc_close(struct GMT_CTRL * GMT,int id)1494 int gmt_nc_close (struct GMT_CTRL *GMT, int id) {
1495 int err = nc_close (id);
1496 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "Calling nc_close on ncid %d, err = %d\n", id, err);
1497 return err;
1498 }
1499
gmtlib_is_nc_grid(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)1500 int gmtlib_is_nc_grid (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
1501 /* Returns GMT_NOERROR if NetCDF grid */
1502 int ncid, z_id = GMT_NOTSET, j = 0, nvars, ndims, err, old = false;
1503 nc_type z_type;
1504 char varname[GMT_GRID_VARNAME_LEN80];
1505 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1506
1507 /* Extract levels name from variable name */
1508 strncpy (varname, HH->varname, GMT_GRID_VARNAME_LEN80-1);
1509 if (varname[0]) {
1510 j = 0;
1511 while (varname[j] && varname[j] != '[' && varname[j] != '(') j++;
1512 if (varname[j])
1513 varname[j] = '\0';
1514 }
1515 if (!strcmp (HH->name, "="))
1516 return (GMT_GRDIO_NC_NO_PIPE);
1517
1518 /* Open the file and look for the required variable */
1519 if (gmt_access (GMT, HH->name, F_OK))
1520 return (GMT_GRDIO_FILE_NOT_FOUND);
1521 if ((err = gmt_nc_open (GMT, HH->name, NC_NOWRITE, &ncid)))
1522 return (GMT_GRDIO_OPEN_FAILED);
1523 if (!nc_inq_dimid (ncid, "xysize", &z_id)) {
1524 /* Old style GMT netCDF grid */
1525 old = true;
1526 if (nc_inq_varid (ncid, "z", &z_id))
1527 return (GMT_GRDIO_NO_VAR);
1528 }
1529 else if (varname[0]) {
1530 /* ?<varname> used */
1531 if (nc_inq_varid (ncid, varname, &z_id))
1532 return (GMT_GRDIO_NO_VAR);
1533 }
1534 else { /* Look for first 2D grid, with fallback to first higher-dimension (3-4D) grid if 2D not found */
1535 int ID = GMT_NOTSET, dim = 0;
1536 nc_inq_nvars (ncid, &nvars);
1537 for (j = 0; j < nvars && z_id < 0; j++) {
1538 gmt_M_err_trap (nc_inq_varndims (ncid, j, &ndims));
1539 if (ndims == 2) /* Found the first 2-D grid */
1540 z_id = j;
1541 else if (ID == GMT_NOTSET && ndims > 2 && ndims < 5) { /* Also look for higher-dim grid in case no 2-D */
1542 ID = j;
1543 dim = ndims;
1544 }
1545 }
1546 if (z_id < 0) { /* No 2-D grid found, check if we found a higher dimension cube */
1547 if (ID == GMT_NOTSET) return (GMT_GRDIO_NO_2DVAR); /* No we didn't */
1548 z_id = ID; /* Pick the higher dimensioned cube instead, get its name, and warn */
1549 nc_inq_varname (ncid, z_id, varname);
1550 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No 2-D array in file %s. Selecting first 2-D slice in the %d-D array %s\n", HH->name, dim, varname);
1551 }
1552 }
1553
1554 gmt_M_err_trap (nc_inq_vartype (ncid, z_id, &z_type));
1555 switch (z_type) {
1556 case NC_UBYTE: header->type = old ? GMT_GRID_IS_CB : GMT_GRID_IS_NB; HH->orig_datatype = GMT_UCHAR; break;
1557 case NC_BYTE: header->type = old ? GMT_GRID_IS_CB : GMT_GRID_IS_NB; HH->orig_datatype = GMT_CHAR; break;
1558 case NC_SHORT: header->type = old ? GMT_GRID_IS_CS : GMT_GRID_IS_NS; HH->orig_datatype = GMT_SHORT; break;
1559 case NC_INT: header->type = old ? GMT_GRID_IS_CI : GMT_GRID_IS_NI; HH->orig_datatype = GMT_INT; break;
1560 case NC_FLOAT: header->type = old ? GMT_GRID_IS_CF : GMT_GRID_IS_NF; HH->orig_datatype = GMT_FLOAT; break;
1561 case NC_DOUBLE: header->type = old ? GMT_GRID_IS_CD : GMT_GRID_IS_ND; HH->orig_datatype = GMT_DOUBLE; break;
1562 default: header->type = k_grd_unknown_fmt; break;
1563 }
1564 gmt_nc_close (GMT, ncid);
1565 return GMT_NOERROR;
1566 }
1567
gmt_nc_read_grd_info(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)1568 int gmt_nc_read_grd_info (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
1569 return (gmtnc_grd_info (GMT, header, 'r', false, 0));
1570 }
1571
gmt_nc_update_grd_info(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)1572 int gmt_nc_update_grd_info (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
1573 return (gmtnc_grd_info (GMT, header, 'u', false, 0));
1574 }
1575
gmt_nc_write_grd_info(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header)1576 int gmt_nc_write_grd_info (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header) {
1577 return (gmtnc_grd_info (GMT, header, 'w', false, 0));
1578 }
1579
gmt_nc_read_grd(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,double wesn[],unsigned int * pad,unsigned int complex_mode)1580 int gmt_nc_read_grd (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, double wesn[], unsigned int *pad, unsigned int complex_mode) {
1581 /* header: grid structure header
1582 * grid: array with final grid
1583 * wesn: Sub-region to extract [Use entire file if 0,0,0,0]
1584 * padding: # of empty rows/columns to add on w, e, s, n of grid, respectively
1585 * complex_mode: &4 | &8 if complex array is to hold real (4) and imaginary (8) parts (otherwise read as real only)
1586 * Note: The file has only real values, we simply allow space in the complex array
1587 * for real and imaginary parts when processed by grdfft etc.
1588 *
1589 * Reads a subset of a grdfile and optionally pads the array with extra rows and columns
1590 * header values for n_columns and n_rows are reset to reflect the dimensions of the logical array,
1591 * not the physical size (i.e., the padding is not counted in n_columns and n_rows)
1592 */
1593
1594 bool adj_nan_value; /* if we need to change the fill value */
1595 int err; /* netcdf errors */
1596 int n_shift;
1597 int error;
1598 unsigned dim[2], dim2[2], origin[2], origin2[2]; /* dimension and origin {y,x} of subset to read from netcdf */
1599 unsigned width = 0, height = 0;
1600 size_t width_t, height_t, row, stride_t;
1601 uint64_t imag_offset;
1602 gmt_grdfloat *pgrid = NULL;
1603 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1604
1605 /* Check type: is file in old NetCDF format or not at all? */
1606 if (GMT->session.grdformat[header->type][0] == 'c')
1607 return (gmt_cdf_read_grd (GMT, header, grid, wesn, pad, complex_mode));
1608 else if (GMT->session.grdformat[header->type][0] != 'n')
1609 return (NC_ENOTNC);
1610
1611 if ((error = gmt_M_err_fail (GMT, gmtnc_grd_prep_io (GMT, header, wesn, &width, &height, &n_shift, origin, dim, origin2, dim2), HH->name)))
1612 return (error);
1613
1614 /* Set stride and offset if complex */
1615 (void)gmtlib_init_complex (header, complex_mode, &imag_offset); /* Set offset for imaginary complex component */
1616 pgrid = grid + imag_offset; /* Start of this complex component (or start of non-complex grid) */
1617
1618 #ifdef NC4_DEBUG
1619 GMT_Report (GMT->parent, GMT_MSG_WARNING, " wesn: %g %g %g %g\n", wesn[XLO], wesn[XHI], wesn[YLO], wesn[YHI]);
1620 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->wesn: %g %g %g %g\n", header->wesn[XLO], header->wesn[XHI], header->wesn[YLO], header->wesn[YHI]);
1621 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->registration:%u\n", header->registration);
1622 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->row_order: %s\n", HH->row_order == k_nc_start_south ? "S->N" : "N->S");
1623 GMT_Report (GMT->parent, GMT_MSG_WARNING, "width: %3d height:%3d\n", width, height);
1624 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->n_columns: %3d head->n_rows:%3d\n", header->n_columns, header->n_rows);
1625 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->mx: %3d head->my:%3d\n", header->mx, header->my);
1626 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->nm: %3d head->size:%3d\n", header->nm, header->size);
1627 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->t-index %d,%d,%d\n", HH->t_index[0], HH->t_index[1], HH->t_index[2]);
1628 GMT_Report (GMT->parent, GMT_MSG_WARNING, " pad xlo:%u xhi:%u ylo:%u yhi:%u\n", pad[XLO], pad[XHI], pad[YLO], pad[YHI]);
1629 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->pad xlo:%u xhi:%u ylo:%u yhi:%u\n", header->pad[XLO], header->pad[XHI], header->pad[YLO], header->pad[YHI]);
1630 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->BC xlo:%u xhi:%u ylo:%u yhi:%u\n", HH->BC[XLO], HH->BC[XHI], HH->BC[YLO], HH->BC[YHI]);
1631 GMT_Report (GMT->parent, GMT_MSG_WARNING, "head->grdtype:%u %u\n", HH->grdtype, GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT);
1632 GMT_Report (GMT->parent, GMT_MSG_WARNING, "imag_offset: %" PRIu64 "\n", imag_offset);
1633 #endif
1634
1635 /* Open the NetCDF file */
1636 if (!strcmp (HH->name,"="))
1637 return (GMT_GRDIO_NC_NO_PIPE);
1638 gmtnc_setup_chunk_cache();
1639 gmt_M_err_trap (gmt_nc_open (GMT, HH->name, NC_NOWRITE, &HH->ncid));
1640
1641 /* Read grid */
1642 if (dim2[1] == 0)
1643 gmtnc_io_nc_grid (GMT, header, dim, origin, HH->stride, k_get_netcdf, pgrid + HH->data_offset, false);
1644 else {
1645 /* Read grid in two parts */
1646 unsigned int stride_or_width = HH->stride != 0 ? HH->stride : width;
1647 gmtnc_io_nc_grid (GMT, header, dim, origin, stride_or_width, k_get_netcdf, pgrid + HH->data_offset, false);
1648 gmtnc_io_nc_grid (GMT, header, dim2, origin2, stride_or_width, k_get_netcdf, pgrid + HH->data_offset + dim[1], false);
1649 }
1650
1651 /* If we need to shift grid */
1652 if (n_shift) {
1653 #ifdef NC4_DEBUG
1654 GMT_Report (GMT->parent, GMT_MSG_WARNING, "gmtnc_right_shift_grid: %d\n", n_shift);
1655 #endif
1656 gmtnc_right_shift_grid (pgrid, dim[1], dim[0], n_shift, sizeof(grid[0]));
1657 }
1658
1659 /* If dim[1] + dim2[1] was < requested width: wrap-pad east border */
1660 if (gmt_grd_is_global(GMT, header) && width > dim[1] + dim2[1]) {
1661 unsigned int fix_pad[4] = {0,0,0,0};
1662 fix_pad[XHI] = width - dim[1] - dim2[1];
1663 gmtnc_pad_grid (pgrid, width - fix_pad[XHI], height, fix_pad, sizeof(grid[0]), k_pad_fill_copy_wrap);
1664 }
1665 else
1666 assert (width == dim[1] + dim2[1]);
1667
1668 /* Get stats */
1669 width_t = (size_t)width;
1670 height_t = (size_t)height;
1671 stride_t = (size_t)HH->stride;
1672 header->z_min = DBL_MAX;
1673 header->z_max = -DBL_MAX;
1674 adj_nan_value = !isnan (header->nan_value);
1675 HH->has_NaNs = GMT_GRID_NO_NANS; /* We are about to check for NaNs and if none are found we retain 1, else 2 */
1676 for (row = 0; row < height_t; ++row) {
1677 gmt_grdfloat *p_data = pgrid + row * (HH->stride ? stride_t : width_t);
1678 unsigned col;
1679 for (col = 0; col < width; col ++) {
1680 if (adj_nan_value && p_data[col] == header->nan_value) {
1681 p_data[col] = (gmt_grdfloat)NAN;
1682 HH->has_NaNs = GMT_GRID_HAS_NANS;
1683 continue;
1684 }
1685 else if (!isnan (p_data[col])) {
1686 header->z_min = MIN (header->z_min, p_data[col]);
1687 header->z_max = MAX (header->z_max, p_data[col]);
1688 }
1689 else
1690 HH->has_NaNs = GMT_GRID_HAS_NANS;
1691 }
1692 }
1693 /* Check limits */
1694 if (header->z_min > header->z_max) {
1695 header->z_min = NAN;
1696 header->z_max = NAN;
1697 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No valid values in grid [%s]\n", HH->name);
1698 }
1699 else {
1700 /* Report z-range of grid (with scale and offset applied): */
1701 unsigned int level;
1702 #ifdef NC4_DEBUG
1703 level = GMT_MSG_WARNING;
1704 #else
1705 level = GMT_MSG_DEBUG;
1706 #endif
1707 GMT_Report (GMT->parent, level,
1708 "packed z-range: [%g,%g]\n", header->z_min, header->z_max);
1709 }
1710
1711 /* Flip grid upside down */
1712 if (HH->row_order == k_nc_start_south)
1713 gmt_grd_flip_vertical (pgrid + HH->data_offset, width, height, HH->stride, sizeof(grid[0]));
1714
1715 /* Add padding with border replication */
1716 gmtnc_pad_grid (pgrid, width, height, pad, sizeof(grid[0]), k_pad_fill_zero);
1717
1718 #ifdef NC4_DEBUG
1719 if (header->size < 160) {
1720 unsigned n;
1721 unsigned pad_x = pad[XLO] + pad[XHI];
1722 unsigned stride = HH->stride ? HH->stride : width;
1723 gmt_grdfloat *p_data = pgrid + HH->data_offset;
1724 for (n = 0; n < (stride + pad_x) * (height + pad[YLO] + pad[YHI]); n++) {
1725 if (n % (stride + pad_x) == 0)
1726 fprintf (stderr, "\n");
1727 fprintf (stderr, "%4.0f", p_data[n]);
1728 }
1729 fprintf (stderr, "\n");
1730 }
1731 #endif
1732
1733 /* Adjust header */
1734 gmt_M_memcpy (header->wesn, wesn, 4, double);
1735 header->n_columns = width;
1736 header->n_rows = height;
1737
1738 gmt_M_err_trap (gmt_nc_close (GMT, HH->ncid));
1739
1740 return GMT_NOERROR;
1741 }
1742
gmt_nc_write_grd(struct GMT_CTRL * GMT,struct GMT_GRID_HEADER * header,gmt_grdfloat * grid,double wesn[],unsigned int * pad,unsigned int complex_mode)1743 int gmt_nc_write_grd (struct GMT_CTRL *GMT, struct GMT_GRID_HEADER *header, gmt_grdfloat *grid, double wesn[], unsigned int *pad, unsigned int complex_mode)
1744 { /* header: grid structure header
1745 * grid: array with final grid
1746 * wesn: Sub-region to write out [Use entire file if 0,0,0,0]
1747 * padding: # of empty rows/columns to add on w, e, s, n of grid, respectively
1748 * complex_mode: &4 | &8 if complex array is to hold real (4) and imaginary (8) parts (otherwise read as real only)
1749 * Note: The file has only real values, we simply allow space in the complex array
1750 * for real and imaginary parts when processed by grdfft etc.
1751 */
1752
1753 int status = NC_NOERR;
1754 bool adj_nan_value; /* if we need to change the fill value */
1755 bool do_round = true; /* if we need to round to integral */
1756 unsigned int width, height, *actual_col = NULL;
1757 unsigned int dim[2], origin[2]; /* dimension and origin {y,x} of subset to write to netcdf */
1758 int first_col, last_col, first_row, last_row;
1759 uint64_t imag_offset;
1760 size_t n, nm;
1761 size_t width_t, height_t;
1762 size_t was_chunk_setting = GMT->current.setting.io_nc4_chunksize[0]; /* Remember current setting */
1763 double limit[2]; /* minmax of z variable */
1764 gmt_grdfloat *pgrid = NULL;
1765 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
1766
1767 /* Determine the value to be assigned to missing data, if not already done so */
1768 switch (header->type) {
1769 case GMT_GRID_IS_NB:
1770 if (isnan (header->nan_value))
1771 header->nan_value = NC_MIN_BYTE;
1772 break;
1773 case GMT_GRID_IS_NS:
1774 if (isnan (header->nan_value))
1775 header->nan_value = NC_MIN_SHORT;
1776 break;
1777 case GMT_GRID_IS_NI:
1778 if (isnan (header->nan_value))
1779 header->nan_value = NC_MIN_INT;
1780 break;
1781 case GMT_GRID_IS_ND:
1782 #ifndef DOUBLE_PRECISION_GRID
1783 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Precision loss! GMT's internal grid representation is 32-bit float.\n");
1784 #endif
1785 /* Intentionally fall through */
1786 default: /* don't round float */
1787 do_round = false;
1788 }
1789
1790 gmt_M_err_pass (GMT, gmt_grd_prep_io (GMT, header, wesn, &width, &height, &first_col, &last_col, &first_row, &last_row, &actual_col), HH->name);
1791 gmt_M_free (GMT, actual_col);
1792
1793 /* Adjust header */
1794 gmt_M_memcpy (header->wesn, wesn, 4, double);
1795 header->n_columns = width;
1796 header->n_rows = height;
1797
1798 /* Adjust first_row */
1799 if (HH->row_order == k_nc_start_south)
1800 first_row = header->n_rows - 1 - last_row;
1801
1802 /* Write grid header without closing file afterwards */
1803 gmtnc_setup_chunk_cache();
1804 status = gmtnc_grd_info (GMT, header, 'W', false, 0);
1805 if (status != NC_NOERR)
1806 goto nc_err;
1807
1808 /* Set stride and offset if complex */
1809 (void)gmtlib_init_complex (header, complex_mode, &imag_offset); /* Set offset for imaginary complex component */
1810 pgrid = grid + imag_offset; /* Beginning of this complex component (or the regular non-complex grid) */
1811
1812 /* Remove padding from grid */
1813 gmtnc_unpad_grid (pgrid, width, height, pad, sizeof(grid[0]));
1814
1815 /* Check that repeating columns do not contain conflicting information */
1816 if (HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT)
1817 gmtnc_grid_fix_repeat_col (GMT, pgrid, width, height, sizeof(grid[0]));
1818
1819 /* Flip grid upside down */
1820 if (HH->row_order == k_nc_start_south)
1821 gmt_grd_flip_vertical (pgrid, width, height, 0, sizeof(grid[0]));
1822
1823 /* Get stats */
1824 header->z_min = DBL_MAX;
1825 header->z_max = -DBL_MAX;
1826 adj_nan_value = !isnan (header->nan_value);
1827 n = 0;
1828 width_t = (size_t)width;
1829 height_t = (size_t)height;
1830 nm = width_t * height_t;
1831 while (n < nm) {
1832 if (adj_nan_value && isnan (pgrid[n]))
1833 pgrid[n] = header->nan_value;
1834 else if (!isnan (pgrid[n])) {
1835 if (do_round)
1836 pgrid[n] = rintf (pgrid[n]); /* round to int */
1837 header->z_min = MIN (header->z_min, pgrid[n]);
1838 header->z_max = MAX (header->z_max, pgrid[n]);
1839 }
1840 n++;
1841 }
1842
1843 /* Write grid */
1844 dim[0] = height, dim[1] = width;
1845 origin[0] = first_row, origin[1] = first_col;
1846 status = gmtnc_io_nc_grid (GMT, header, dim, origin, 0, k_put_netcdf, pgrid, false);
1847 if (status != NC_NOERR)
1848 goto nc_err;
1849
1850 if (header->z_min <= header->z_max) {
1851 /* Warn if z-range exceeds the precision of a single precision float: */
1852 static const uint32_t exp2_24 = 0x1000000; /* exp2 (24) */
1853 unsigned int level;
1854 if (fabs(header->z_min) >= exp2_24 || fabs(header->z_max) >= exp2_24)
1855 GMT_Report (GMT->parent, GMT_MSG_DEBUG, "The z-range, [%g,%g], might exceed the significand's precision of 24 bits; round-off errors may occur.\n", header->z_min, header->z_max);
1856
1857 /* Report z-range of grid (with scale and offset applied): */
1858 #ifdef NC4_DEBUG
1859 level = GMT_MSG_WARNING;
1860 #else
1861 level = GMT_MSG_DEBUG;
1862 #endif
1863 GMT_Report (GMT->parent, level,
1864 "packed z-range: [%g,%g]\n", header->z_min, header->z_max);
1865
1866 /* Limits need to be written in actual, not internal grid, units: */
1867 limit[0] = header->z_min * header->z_scale_factor + header->z_add_offset;
1868 limit[1] = header->z_max * header->z_scale_factor + header->z_add_offset;
1869 }
1870 else {
1871 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No valid values in grid [%s]\n", HH->name);
1872 limit[0] = limit[1] = NAN; /* Set limit to NaN */
1873 }
1874 status = nc_put_att_double (HH->ncid, HH->z_id, "actual_range", NC_DOUBLE, 2, limit);
1875 if (status != NC_NOERR)
1876 goto nc_err;
1877
1878 /* Close grid */
1879 status = gmt_nc_close (GMT, HH->ncid);
1880 if (status != NC_NOERR)
1881 goto nc_err;
1882
1883 GMT->current.setting.io_nc4_chunksize[0] = was_chunk_setting; /* Restore to previous status */
1884
1885 return GMT_NOERROR;
1886
1887 nc_err:
1888 /* exit gracefully */
1889 gmt_nc_close (GMT, HH->ncid); /* close nc-file */
1890 unlink (HH->name); /* remove nc-file */
1891 if (status == NC_ERANGE) {
1892 /* report out of range z variable */
1893 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot write format %s.\n", GMT->session.grdformat[header->type]);
1894 GMT_Report (GMT->parent, GMT_MSG_ERROR, "The packed z-range, [%g,%g], exceeds the maximum representable size. Adjust scale and offset parameters or remove out-of-range values.\n", header->z_min, header->z_max);
1895 }
1896 return status;
1897 }
1898
gmt_nc_is_cube(struct GMTAPI_CTRL * API,char * file)1899 bool gmt_nc_is_cube (struct GMTAPI_CTRL *API, char *file) {
1900 /* Return true if the file is a 3-D netCDF cube */
1901 bool is_cube = false;
1902 int i, ID = GMT_NOTSET, ncid, z_id = GMT_NOTSET, nvars, ndims = 0;
1903 char varname[GMT_GRID_NAME_LEN256] = {""}, *c = NULL;
1904 gmt_M_unused (API);
1905
1906 if (file == NULL || file[0] == '\0') return false; /* Got no file */
1907 if (!strcmp (file,"=")) return (false); /* NetCDF is not pipeable */
1908 if (gmt_M_file_is_memory (file)) return ((file[GMTAPI_OBJECT_FAMILY_START] == 'U') ? true : false); /* Memory cube or not */
1909 if ((c = strchr (file, '?'))) { /* Specific variable */
1910 strcpy (varname, &c[1]);
1911 c[0] = '\0'; /* Chop off for now */
1912 }
1913 if (gmt_nc_open (API->GMT, file, NC_NOWRITE, &ncid)) {
1914 if (c) c[0] = '?'; /* Restore */
1915 return false; /* Unable to open, so probably not netCDF file */
1916 }
1917 /* Here file is open so must close before returning */
1918 if (c) c[0] = '?'; /* Restore */
1919 if (nc_inq_nvars (ncid, &nvars)) goto nc_cube_return; /* No variables, no good */
1920 if (c) {
1921 if (nc_inq_varid (ncid, varname, &z_id) == NC_NOERR) { /* Gave a named variable that exist, is it 2-4 D? */
1922 if (nc_inq_varndims (ncid, z_id, &ndims)) goto nc_cube_return; /* Not god */
1923 if (ndims != 3) z_id = GMT_NOTSET; /* But not 3-D */
1924 if (ndims > 2 && ndims <= 5) ID = z_id;
1925 }
1926 }
1927 else { /* Must search */
1928 i = 0;
1929 while (i < nvars && z_id < 0) { /* Look for first 3D grid, with fallback to first higher-dimension (4-%D) grid if 3D not found */
1930 if (nc_inq_varndims (ncid, i, &ndims)) goto nc_cube_return; /* Not good */
1931 if (ndims == 3) /* Found the first 3-D grid */
1932 z_id = i;
1933 else if (ID == GMT_NOTSET && ndims > 3 && ndims < 5) /* Also look for higher-dim grid in case no 3-D */
1934 ID = i;
1935 i++;
1936 }
1937 }
1938 if (ID != GMT_NOTSET || z_id >= 0) is_cube = true; /* Found 3-D grid */
1939 nc_cube_return:
1940 gmt_nc_close (API->GMT, ncid); /* close nc-file */
1941 return (is_cube);
1942 }
1943
1944 /* Examine the netCDF data cube and determine if it is a 3-D cube and return the knots */
1945
gmt_nc_read_cube_info(struct GMT_CTRL * GMT,char * file,double * w_range,uint64_t * nz,double ** zarray,char * z_unit)1946 int gmt_nc_read_cube_info (struct GMT_CTRL *GMT, char *file, double *w_range, uint64_t *nz, double **zarray, char *z_unit) {
1947 int i, err, ID = GMT_NOTSET, dim = 0, ncid, z_id = GMT_NOTSET, ids[5] = {-1,-1,-1,-1,-1}, dims[5], nvars;
1948 int has_vector, ndims = 0, z_dim, status;
1949 uint64_t n_layers = 0;
1950 size_t lens[5];
1951 char varname[GMT_GRID_VARNAME_LEN80], dimname[GMT_GRID_UNIT_LEN80], z_units[GMT_GRID_UNIT_LEN80];
1952 double *z = NULL, dummy[2] = {0.0, 0.0};
1953
1954 gmt_M_err_trap (gmt_nc_open (GMT, file, NC_NOWRITE, &ncid));
1955
1956 gmt_M_err_trap (nc_inq_nvars (ncid, &nvars));
1957 i = 0;
1958 while (i < nvars && z_id < 0) { /* Look for first 3D grid, with fallback to first higher-dimension (4-5D) grid if 3D not found */
1959 gmt_M_err_trap (nc_inq_varndims (ncid, i, &ndims));
1960 if (ndims == 3) /* Found the first 3-D grid */
1961 z_id = i;
1962 else if (ID == GMT_NOTSET && ndims > 3 && ndims < 5) { /* Also look for higher-dim grid in case no 3-D */
1963 ID = i;
1964 dim = ndims;
1965 }
1966 i++;
1967 }
1968 if (z_id < 0) { /* No 3-D grid found, check if we found a higher dimension cube */
1969 if (ID == GMT_NOTSET) { /* No we didn't */
1970 GMT_Report (GMT->parent, GMT_MSG_ERROR, "No 3-D data cube found in file %s.\n", file);
1971 return GMT_GRDIO_NO_2DVAR;
1972 }
1973 z_id = ID; /* Pick the higher dimensioned cube instead, get its name, and warn */
1974 nc_inq_varname (ncid, z_id, varname);
1975 GMT_Report (GMT->parent, GMT_MSG_WARNING, "No 3-D array in file %s. Selecting first 3-D slice in the %d-D array %s\n", file, dim, varname);
1976 }
1977 gmt_M_err_trap (nc_inq_vardimid (ncid, z_id, dims));
1978
1979 /* Get the ids of the x and y (and depth and time) coordinate variables */
1980 for (i = 0; i < ndims; i++) {
1981 gmt_M_err_trap (nc_inq_dim (ncid, dims[i], dimname, &lens[i]));
1982 if ((status = nc_inq_varid (ncid, dimname, &ids[i])) != NC_NOERR)
1983 GMT_Report (GMT->parent, GMT_MSG_WARNING, "\"%s\", %s\n\tIf something bad happens later, try importing via GDAL.\n",
1984 dimname, nc_strerror(status));
1985 }
1986 z_dim = ndims-3;
1987 n_layers = lens[z_dim];
1988
1989 /* Create enough memory to store the level-coordinate values */
1990 z = gmt_M_memory (GMT, NULL, n_layers, double);
1991
1992 /* Get information about z variable */
1993 gmtnc_get_units (GMT, ncid, ids[z_dim], z_units);
1994 if (strstr (z_units, "seconds since 1970-01-01"))
1995 gmt_set_column_type (GMT, GMT_IN, GMT_Z, GMT_IS_ABSTIME);
1996 else if (z_units[0] == '\0')
1997 strcpy (z_units, "z"); /* Set default z-unit name */
1998 /* Look for the z-coordinate vector */
1999 if ((has_vector = nc_get_var_double (ncid, ids[z_dim], z))) {
2000 GMT_Report (GMT->parent, GMT_MSG_ERROR, "No 3rd-dimension coordinate vector found in %s\n", file);
2001 return GMT_GRDIO_NO_2DVAR;
2002 }
2003
2004 if (w_range) gmt_M_memset (w_range, 2U, double);
2005 if (w_range && (!nc_get_att_double (ncid, z_id, "actual_range", dummy) ||
2006 !nc_get_att_double (ncid, z_id, "valid_range", dummy) ||
2007 !(nc_get_att_double (ncid, z_id, "valid_min", &dummy[0]) +
2008 nc_get_att_double (ncid, z_id, "valid_max", &dummy[1]))))
2009 gmt_M_memcpy (w_range, dummy, 2U, double);
2010
2011 *zarray = z;
2012 *nz = n_layers;
2013 if (z_unit) /* Passed pointer to storage for z-unit in cubes */
2014 strncpy (z_unit, z_units, GMT_GRID_UNIT_LEN80);
2015
2016 return GMT_NOERROR;
2017 }
2018
2019 /* Write a 3-D cube to file; cube is represented internally by a stack of 2-D grids and a layer z-array */
gmt_nc_write_cube(struct GMT_CTRL * GMT,struct GMT_CUBE * C,double wesn[],const char * file)2020 int gmt_nc_write_cube (struct GMT_CTRL *GMT, struct GMT_CUBE *C, double wesn[], const char *file) {
2021 /* Depending on mode, we either write individual layer grid files or a single 3-D data cube */
2022 int status = NC_NOERR, err = 0;
2023 uint64_t k, k0, k1, n_layers_used, save_n_bands, here = 0;
2024 struct GMTAPI_CTRL *API = GMT->parent;
2025
2026 /* Determine which layers we want to write */
2027 k0 = 0; k1 = C->header->n_bands - 1; /* Default is all */
2028 if (wesn && wesn[ZHI] > wesn[ZLO]) { /* Want to write a subset of layers */
2029 if (gmt_get_active_layers (GMT, C, &(wesn[ZLO]), &k0, &k1) == 0) {
2030 gmtlib_report_error (API, GMT_RUNTIME_ERROR);
2031 }
2032 }
2033 n_layers_used = k1 - k0 + 1; /* Total number of layers actually to be written */
2034 if (n_layers_used == 0) {
2035 GMT_Report (API, GMT_MSG_ERROR, "gmt_nc_write_cube: No layers selected from GMT_IS_CUBE.\n");
2036 return (gmtlib_report_error (API, GMT_DIM_TOO_SMALL));
2037 }
2038 save_n_bands = C->header->n_bands; /* Remember how many bands */
2039 here = k0 * C->header->size; /* Start position in the cube for layer k0 */
2040
2041 if (strchr (file, '%') || n_layers_used == 1) { /* Format specifier found, do individual layer grids, or a single layer so no format needed */
2042 char gfile[PATH_MAX] = {""};
2043 struct GMT_GRID *G = NULL;
2044
2045 /* !! Remember that the gmt_nc.c codes will unpad and mess up C->data */
2046 G = gmt_get_grid (GMT); /* Need a dummy grid for writing */
2047 G->header = C->header; /* Use this pointer for now */
2048 G->header->n_bands = 1; /* Grids only have one band [must undo this below] */
2049 /* Write the layers via a grid */
2050 for (k = k0; k <= k1; k++) { /* For all selected output levels */
2051 if (n_layers_used > 1 || strchr (file, '%')) /* Create the k'th layer file name from template */
2052 sprintf (gfile, file, C->z[k]);
2053 else /* Just this one layer grid */
2054 sprintf (gfile, "%s", file);
2055 G->data = &C->data[here]; /* Point to start of this layer */
2056 GMT_Report (API, GMT_MSG_DEBUG, "gmt_nc_write_cube: Layer %" PRIu64 ", offset = %" PRIu64 ".\n", k, here);
2057 if (GMT_Write_Data (API, GMT_IS_GRID, GMT_IS_FILE, GMT_IS_SURFACE, GMT_CONTAINER_AND_DATA, wesn, gfile, G) != GMT_NOERROR) {
2058 return (API->error);
2059 }
2060 here += C->header->size; /* Move to next level */
2061 }
2062 /* Wipe and free the temporary grid structure */
2063 G->header->n_bands = save_n_bands; /* Restore bands */
2064 G->data = NULL;
2065 G->header = NULL;
2066 gmt_free_grid (GMT, &G, true);
2067 return (GMT_NOERROR);
2068 }
2069 else { /* Here we must write a 3-D netcdf data cube NOT COMPLETED SO NOT WORKING */
2070 bool adj_nan_value; /* if we need to change the fill value */
2071 bool do_round = true; /* if we need to round to integral */
2072 unsigned int width, height;
2073 unsigned int dim[3], origin[2]; /* dimension and origin {y,x} of subset to write to netcdf */
2074 int first_col, first_row, last_row;
2075 size_t n, nm;
2076 size_t width_t, height_t;
2077 double level_min, level_max; /* minmax of level variable */
2078 gmt_grdfloat *pgrid = NULL;
2079 struct GMT_GRID_HEADER *header = C->header;
2080 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (header);
2081
2082 width = header->n_columns; height = header->n_rows;
2083 header->n_bands = n_layers_used; /* Number of layers to actually write */
2084 strncpy (HH->name, file, GMT_GRID_NAME_LEN256);
2085 /* Determine the value to be assigned to missing data, if not already done so */
2086 switch (header->type) {
2087 case GMT_GRID_IS_NB:
2088 if (isnan (header->nan_value))
2089 header->nan_value = NC_MIN_BYTE;
2090 break;
2091 case GMT_GRID_IS_NS:
2092 if (isnan (header->nan_value))
2093 header->nan_value = NC_MIN_SHORT;
2094 break;
2095 case GMT_GRID_IS_NI:
2096 if (isnan (header->nan_value))
2097 header->nan_value = NC_MIN_INT;
2098 break;
2099 case GMT_GRID_IS_ND:
2100 #ifndef DOUBLE_PRECISION_GRID
2101 GMT_Report (GMT->parent, GMT_MSG_WARNING, "Precision loss! GMT's internal grid representation is 32-bit float.\n");
2102 #endif
2103 /* Intentionally fall through */
2104 default: /* don't round float */
2105 do_round = false;
2106 }
2107
2108 first_col = first_row = 0;
2109 last_row = header->n_rows - 1;
2110 level_min = DBL_MAX;
2111 level_max = -DBL_MAX;
2112
2113 /* Get stats */
2114 adj_nan_value = !isnan (header->nan_value);
2115 width_t = (size_t)width;
2116 height_t = (size_t)height;
2117 nm = width_t * height_t;
2118 for (k = k0; k <= k1; k++) { /* For all selected output levels */
2119 pgrid = &C->data[here];
2120 /* Update stats and set NaN-values */
2121 n = 0;
2122 while (n < nm) {
2123 if (adj_nan_value && isnan (pgrid[n]))
2124 pgrid[n] = header->nan_value;
2125 else if (!isnan (pgrid[n])) {
2126 if (do_round)
2127 pgrid[n] = rintf (pgrid[n]); /* round to int */
2128 level_min = MIN (level_min, pgrid[n]);
2129 level_max = MAX (level_max, pgrid[n]);
2130 }
2131 n++;
2132 }
2133 here += C->header->size;
2134 }
2135 /* The min/max of the cube */
2136 header->z_min = level_min;
2137 header->z_max = level_max;
2138
2139 /* Adjust first_row */
2140 if (HH->row_order == k_nc_start_south)
2141 first_row = header->n_rows - 1 - last_row;
2142
2143 /* Write grid header without closing file afterwards so more items can be added */
2144 gmtnc_setup_chunk_cache();
2145 status = gmtnc_grd_info (GMT, header, 'W', true, n_layers_used);
2146 if (status != NC_NOERR)
2147 goto nc_err;
2148
2149 /* Add cube parameters, etc not done by gmtnc_grd_info */
2150
2151 /* Define 3-D z variable and write the attributes */
2152
2153 gmtnc_put_units (HH->ncid, HH->xyz_id[GMT_Z], C->units);
2154
2155 // if (C->name[0])
2156 // nc_put_att_text (HH->ncid, HH->z_id, "long_name", strlen(C->name), C->name);
2157 // else
2158 // nc_put_att_text (HH->ncid, HH->z_id, "long_name", 4U, "cube");
2159 // if (C->units[0]) { /* This is the 3rd dimension name and units */
2160 // int i = 0, j = 0;
2161 // bool copy = false;
2162 // char name[GMT_GRID_UNIT_LEN80], units[GMT_GRID_UNIT_LEN80];
2163 // strncpy (name, C->units, GMT_GRID_UNIT_LEN80-1);
2164 // units[0] = '\0';
2165 // for (i = 0; i < GMT_GRID_UNIT_LEN80 && name[i]; i++) {
2166 // if (name[i] == ']') copy = false, units[j] = '\0';
2167 // if (copy) units[j++] = name[i];
2168 // if (name[i] == '[') {
2169 // name[i] = '\0';
2170 // if (i > 0 && name[i-1] == ' ') name[i-1] = '\0';
2171 // copy = true;
2172 // }
2173 // }
2174 // if (name[0]) nc_put_att_text (HH->ncid, HH->xyz_id[GMT_Z], "long_name", strlen(name), name);
2175 // if (units[0]) nc_put_att_text (HH->ncid, HH->xyz_id[GMT_Z], "units", strlen(units), units);
2176 // if (name[0] && strstr (name, "time"))
2177 // nc_put_att_text (HH->ncid, HH->xyz_id[GMT_Z], "standard_name", 4U, "time");
2178 // else if (name[0] == '\0')
2179 // nc_put_att_text (HH->ncid, HH->xyz_id[GMT_Z], "long_name", 1U, "z");
2180 // }
2181 // else /* No information about z, so generic z */
2182 // nc_put_att_text (HH->ncid, HH->xyz_id[GMT_Z], "long_name", 1U, "z");
2183 if (gmt_M_is_geographic (GMT, GMT_OUT))
2184 nc_put_att_text (HH->ncid, HH->xyz_id[GMT_Z], "axis", 1, "Z");
2185 gmt_M_err_trap (nc_put_att_double (HH->ncid, HH->xyz_id[GMT_Z], "actual_range", NC_DOUBLE, 2U, C->z_range));
2186
2187 gmt_M_err_trap (nc_enddef (HH->ncid)); /* Belatedly end definition mode */
2188 gmtnc_put_xy_vectors (GMT, header); /* Place x and y vectors */
2189 {
2190 size_t z_zero = 0, z_count = n_layers_used;
2191 gmt_M_err_trap (nc_put_vara_double (HH->ncid, HH->xyz_id[GMT_Z], &z_zero, &z_count, &C->z[k0]));
2192 }
2193
2194 /* Get stats */
2195 //dim[0] = height, dim[1] = width; dim[2] = n_layers_used;
2196 dim[1] = height, dim[2] = width; dim[0] = n_layers_used;
2197 origin[0] = 0, origin[1] = 0;
2198 here = k0 * C->header->size; /* Start position in the cube for layer k0 */
2199
2200 for (k = k0; k <= k1; k++) { /* For all selected output levels */
2201 pgrid = &C->data[here];
2202 HH->t_index[0] = k - k0; /* Layer index */
2203 /* Remove padding from grid */
2204 gmtnc_unpad_grid (pgrid, width, height, header->pad, sizeof(pgrid[0]));
2205
2206 /* Check that repeating columns do not contain conflicting information */
2207 if (HH->grdtype == GMT_GRID_GEOGRAPHIC_EXACT360_REPEAT)
2208 gmtnc_grid_fix_repeat_col (GMT, pgrid, width, height, sizeof(pgrid[0]));
2209
2210 /* Flip grid upside down */
2211 if (HH->row_order == k_nc_start_south)
2212 gmt_grd_flip_vertical (pgrid, width, height, 0, sizeof(pgrid[0]));
2213
2214 /* Write grid layer */
2215 status = gmtnc_io_nc_grid (GMT, header, dim, origin, 0, k_put_netcdf, pgrid, true);
2216 if (status != NC_NOERR)
2217 goto nc_err;
2218 here += C->header->size;
2219 }
2220 if (level_min <= level_max) {
2221 /* Warn if level-range exceeds the precision of a single precision float: */
2222 static const uint32_t exp2_24 = 0x1000000; /* exp2 (24) */
2223 unsigned int level;
2224 if (fabs(level_min) >= exp2_24 || fabs(level_max) >= exp2_24)
2225 GMT_Report (GMT->parent, GMT_MSG_WARNING, "The level-range, [%g,%g], might exceed the significand's precision of 24 bits; round-off errors may occur.\n", level_min, level_max);
2226
2227 /* Report level-range of grid layer (with scale and offset applied): */
2228 #ifdef NC4_DEBUG
2229 level = GMT_MSG_WARNING;
2230 #else
2231 level = GMT_MSG_DEBUG;
2232 #endif
2233 GMT_Report (GMT->parent, level,
2234 "packed z-range: [%g,%g]\n", level_min, level_max);
2235
2236 }
2237 else
2238 GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "No valid values in grid [%s]\n", HH->name);
2239
2240 if (status != NC_NOERR)
2241 goto nc_err;
2242 /* Close grid */
2243 status = gmt_nc_close (GMT, HH->ncid);
2244 if (status != NC_NOERR)
2245 goto nc_err;
2246
2247 return GMT_NOERROR;
2248
2249 nc_err:
2250 /* exit gracefully */
2251 gmt_nc_close (GMT, HH->ncid); /* close nc-file */
2252 unlink (HH->name); /* remove nc-file */
2253 if (status == NC_ERANGE) {
2254 /* report out of range z variable */
2255 GMT_Report (GMT->parent, GMT_MSG_ERROR, "Cannot write format %s.\n", GMT->session.grdformat[header->type]);
2256 GMT_Report (GMT->parent, GMT_MSG_ERROR, "The packed z-range, [%g,%g], exceeds the maximum representable size. Adjust scale and offset parameters or remove out-of-range values.\n", level_min, level_max);
2257 }
2258 return status;
2259 }
2260 }
2261