1 /*--------------------------------------------------------------------
2 *
3 * Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
4 * See LICENSE.TXT file for copying and redistribution conditions.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; version 3 or any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * Contact info: www.generic-mapping-tools.org
16 *--------------------------------------------------------------------*/
17 /*
18 * G M T _ B C R . C
19 *
20 *- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
21 * GMT_bcr.c contains interpolation routines based on convolution kernels
22 * (BCR = bi-dimensional convolution routines)
23 *
24 * Designed to operate with two rows and columns around each edge,
25 * to allow derivative computations.
26 * Follows the outline in Lancaster and Salkauskas, section 9.3
27 * Meant to replace Taylor routines in GMT in version 3.0 of GMT.
28 * Significantly simplified and extended for GMT version 4.2.1.
29 *
30 * Currently supports:
31 * - Nearest neighbor "interpolation"
32 * This attaches a weight of 1 to the nearest grid cell only.
33 *
34 * - Bi-linear interpolation
35 * The weight given to each of the 4 vertices surrounding the interpolation
36 * point is the product of the weight function in x and y direction
37 * (wx and wy) who have the same form and depend on the projected distance
38 * |t| between the interpolation point and each vertex.
39 *
40 * wx = 1 - |t| for 0 <= |t| <= 1
41 * = 0 for 1 <= |t|
42 *
43 * - B-spline smoothing
44 * This is a smoothing kernel. I.e., the interpolated value directly
45 * at a grid node is not equal to the nodal value. This is because wx(0) < 1.
46 * The convolution kernel spans 16 vertices surrounding the interpolation point.
47 *
48 * wx = 1/2 |t|^3 - |t|^2 + 2/3 for 0 <= |t| <= 1
49 * = -1/6 |t|^3 + |t|^2 -2 |t| + 4/3 for 1 <= |t| <= 2
50 * = 0 for 2 <= |t|
51 *
52 * - Bi-cubic interpolation
53 * This is the preferred option. It provides a smooth interpolation that
54 * is twice differential in both directions. The values at the grid nodes
55 * remain unchanged. The convolution kernel spans 16 vertices surrounding the
56 * interpolation point.
57 *
58 * wx = (a+2) |t|^3 - (a+3) |t|^2 + 1 for 0 <= |t| <= 1
59 * = a |t|^3 - 5a |t|^2 + 8a |t| - 4a for 1 <= |t| <= 2
60 * = 0 for 2 <= |t|
61 * and a = -1/2
62 *
63 * Because of the size of the convolution kernels, these routines assume that
64 * the grid is padded with appropriate (periodic or natural) boundary conditions.
65 *
66 * Authors: Walter Smith and Remko Scharroo
67 * Date: 23-SEP-1993 and 11-SEP-2007
68 * Version: 5
69 * Now 64-bit enabled.
70 *
71 * Public functions (3):
72 *
73 * gmt_bcr_get_z : Get interpolated grid value by convolution
74 * gmt_bcr_get_z_fast : Same, but skips NaN or outside checking
75 * gmtlib_bcr_get_img : Get interpolated image value(s) by convolution
76 */
77
78 #include "gmt_dev.h"
79 #include "gmt_internals.h"
80
81 /* Local functions */
82
gmtbcr_reject(struct GMT_GRID_HEADER * h,double * xx,double * yy)83 GMT_LOCAL unsigned int gmtbcr_reject (struct GMT_GRID_HEADER *h, double *xx, double *yy) {
84
85 /* First check that xx,yy are not Nan - if so return NaN */
86
87 if (gmt_M_is_dnan (*xx) || gmt_M_is_dnan (*yy)) return (2);
88
89 /* First check if the xx and yy are within the grid.
90 16-Sep-2007: Added some slack (GMT_CONV4_LIMIT) here to avoid setting to NaN points
91 that are really on the edge but because of rounding errors are regarded outside.
92 Remember that we have padded the grid with 2 extra values, so this should not be
93 a problem. */
94 /* 9-Sep-2014: No, it is a problem when things are outside, since at the end we loop over
95 4 rows. So if just outside by < GMT_CONV4_LIMIT then we move the points onto the boundary */
96
97 if (*xx < h->wesn[XLO]) { /* If left of xmin... */
98 if (*xx < h->wesn[XLO] - GMT_CONV4_LIMIT) return (1); /* ...by this much then truly outside */
99 *xx = h->wesn[XLO]; /* Else we say it is on xmin border */
100 }
101 else if (*xx > h->wesn[XHI]) { /* If right of xmax... */
102 if (*xx > h->wesn[XHI] + GMT_CONV4_LIMIT) return (1); /* ...by this much then truly outside */
103 *xx = h->wesn[XHI]; /* Else we say it is on xmax border */
104 }
105
106 if (*yy < h->wesn[YLO]) { /* If below ymin... */
107 if (*yy < h->wesn[YLO] - GMT_CONV4_LIMIT) return (1); /* ...by this much then truly outside */
108 *yy = h->wesn[YLO]; /* Else we say it is on ymin border */
109 }
110 else if (*yy > h->wesn[YHI]) { /* If above ymax... */
111 if (*yy > h->wesn[YHI] + GMT_CONV4_LIMIT) return (1); /* ...by this much then truly outside */
112 *yy = h->wesn[YHI]; /* Else we say it is on ymin border */
113 }
114
115 return (0); /* Good to use */
116 }
117
gmtbcr_prep(struct GMT_GRID_HEADER * h,double xx,double yy,double wx[],double wy[])118 GMT_LOCAL uint64_t gmtbcr_prep (struct GMT_GRID_HEADER *h, double xx, double yy, double wx[], double wy[]) {
119 int col, row;
120 uint64_t ij;
121 double x, y, wp, wq, w, xi, yj;
122 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (h);
123
124 /* Compute the normalized real indices (x,y) of the point (xx,yy) within the grid.
125 Note that the y axis points down from the upper left corner of the grid. */
126
127 x = (xx - h->wesn[XLO]) * HH->r_inc[GMT_X] - h->xy_off;
128 y = (h->wesn[YHI] - yy) * HH->r_inc[GMT_Y] - h->xy_off;
129
130 if (HH->bcr_interpolant == BCR_NEARNEIGHBOR) {
131 /* Find the indices (i,j) of the closest node. */
132 col = irint (x);
133 row = irint (y);
134 }
135 else {
136 /* Find the indices (i,j) of the node to the upper left of that.
137 Because of padding, i and j can be on the edge. */
138 xi = floor (x);
139 yj = floor (y);
140 col = irint (xi);
141 row = irint (yj);
142
143 /* Determine the offset of (x,y) with respect to (i,j). */
144 x -= xi;
145 y -= yj;
146
147 /* For 4x4 interpolants, move over one more cell to the upper left corner */
148 if (HH->bcr_n == 4) { col--; row--; }
149 }
150
151 /* Normally, one would expect here a check on the value (i,j) to make sure that the
152 corners of the convolution kernel, (i,j) and (i+bcr->n-1,j+bcr->n-1), are both within
153 the padded grid. However, the check on (xx, yy) above, even with the slack, ensures
154 that the corner points are between (-2,-2) and (G->header->n_columns+1,G->header->n_rows+1), the corners
155 of the padding.
156 */
157
158 /* Save the location of the upper left corner point of the convolution kernel */
159 ij = gmt_M_ijp (h, row, col);
160
161 /* Build weights */
162
163 switch (HH->bcr_interpolant) {
164 case BCR_NEARNEIGHBOR:
165 wx[0] = wy[0] = 1.0;
166 break;
167 case BCR_BILINEAR:
168 /* Simple 1-D linear weights */
169 wx[0] = 1.0 - x;
170 wx[1] = x;
171
172 wy[0] = 1.0 - y;
173 wy[1] = y;
174 break;
175 case BCR_BSPLINE:
176 /* These are B-spline weights */
177 wp = x * x;
178 wq = wp * x;
179 wx[1] = wq / 2 - wp + 2.0 / 3.0;
180 wx[3] = wq / 6;
181 w = 1.0 - x;
182 wp = w * w;
183 wq = wp * w;
184 wx[2] = wq / 2 - wp + 2.0 / 3.0;
185 wx[0] = wq / 6;
186
187 wp = y * y;
188 wq = wp * y;
189 wy[1] = wq / 2 - wp + 2.0 / 3.0;
190 wy[3] = wq / 6;
191 w = 1.0 - y;
192 wp = w * w;
193 wq = wp * w;
194 wy[2] = wq / 2 - wp + 2.0 / 3.0;
195 wy[0] = wq / 6;
196 break;
197 default:
198 /* These weights are based on the cubic convolution kernel, see for example
199 http://undergraduate.csse.uwa.edu.au/units/CITS4241/Handouts/Lecture04.html
200 These weights include a free parameter (a), which is set to -0.5 in this case.
201
202 In the absence of NaNs, the result of this is identical to the scheme introduced
203 by Walter Smith. The current implementation, however, is much less complex, faster,
204 allows NaNs to be skipped, and much more similar to the bilinear case.
205
206 Remko Scharroo, 10 Sep 2007.
207 */
208 w = 1.0 - x;
209 wp = w * x;
210 wq = -0.5 * wp;
211 wx[0] = wq * w;
212 wx[3] = wq * x;
213 wx[1] = 3 * wx[3] + w + wp;
214 wx[2] = 3 * wx[0] + x + wp;
215
216 w = 1.0 - y;
217 wp = w * y;
218 wq = -0.5 * wp;
219 wy[0] = wq * w;
220 wy[3] = wq * y;
221 wy[1] = 3 * wy[3] + w + wp;
222 wy[2] = 3 * wy[0] + y + wp;
223 break;
224 }
225
226 return (ij);
227 }
228
229 /*----------------------------------------------------------|
230 * Public functions that are part of the GMT Devel library |
231 *----------------------------------------------------------|
232 */
233
gmt_bcr_get_z_fast(struct GMT_CTRL * GMT,struct GMT_GRID * G,double xx,double yy)234 double gmt_bcr_get_z_fast (struct GMT_CTRL *GMT, struct GMT_GRID *G, double xx, double yy) {
235 /* Given xx, yy in user's grid file (in non-normalized units)
236 this routine returns the desired interpolated value (nearest-neighbor, bilinear
237 B-spline or bicubic) at xx, yy.
238 Same as gmt_bcr_get_z but no check for Nan or outside, so calling program
239 must make sure we don't go outside array or pass nans.
240 */
241
242 unsigned int i, j;
243 uint64_t ij, node;
244 double retval, wsum, wx[4] = {0.0, 0.0, 0.0, 0.0}, wy[4] = {0.0, 0.0, 0.0, 0.0}, w;
245 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
246
247 /* Determine nearest node ij and set weights wx, wy */
248
249 ij = gmtbcr_prep (G->header, xx, yy, wx, wy);
250
251 retval = wsum = 0.0;
252 for (j = 0; j < HH->bcr_n; j++) {
253 for (i = 0; i < HH->bcr_n; i++) {
254 /* assure that index is inside bounds of the array G->data: */
255 node = ij + i;
256 assert (node < G->header->size);
257 w = wx[i] * wy[j];
258 retval += G->data[node] * w;
259 wsum += w;
260 }
261 ij += G->header->mx;
262 }
263 if ((wsum + GMT_CONV8_LIMIT - HH->bcr_threshold) > 0.0) {
264 retval /= wsum;
265 if (GMT->common.n.truncate) {
266 if (retval < G->header->z_min) retval = G->header->z_min;
267 else if (retval > G->header->z_max) retval = G->header->z_max;
268 }
269 return (retval);
270 }
271 return (GMT->session.d_NaN);
272 }
273
gmt_bcr_get_z(struct GMT_CTRL * GMT,struct GMT_GRID * G,double xx,double yy)274 double gmt_bcr_get_z (struct GMT_CTRL *GMT, struct GMT_GRID *G, double xx, double yy) {
275 /* Given xx, yy in user's grid file (in non-normalized units)
276 this routine returns the desired interpolated value (nearest-neighbor, bilinear
277 B-spline or bicubic) at xx, yy. */
278
279 unsigned int i, j;
280 uint64_t ij, node;
281 double retval, wsum, wx[4] = {0.0, 0.0, 0.0, 0.0}, wy[4] = {0.0, 0.0, 0.0, 0.0}, w;
282 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
283
284 /* First check that xx,yy are not Nan or outside domain - if so return NaN */
285
286 if (gmtbcr_reject (G->header, &xx, &yy)) return (GMT->session.d_NaN); /* NaNs or outside */
287
288 /* Determine nearest node ij and set weights wx, wy */
289
290 ij = gmtbcr_prep (G->header, xx, yy, wx, wy);
291
292 retval = wsum = 0.0;
293 for (j = 0; j < HH->bcr_n; j++) {
294 for (i = 0; i < HH->bcr_n; i++) {
295 /* assure that index is inside bounds of the array G->data: */
296 node = ij + i;
297 assert (node < G->header->size);
298 if (!gmt_M_is_fnan (G->data[node])) {
299 w = wx[i] * wy[j];
300 retval += G->data[node] * w;
301 wsum += w;
302 }
303 }
304 ij += G->header->mx;
305 }
306 if ((wsum + GMT_CONV8_LIMIT - HH->bcr_threshold) > 0.0) {
307 retval /= wsum;
308 if (GMT->common.n.truncate) {
309 if (retval < G->header->z_min) retval = G->header->z_min;
310 else if (retval > G->header->z_max) retval = G->header->z_max;
311 }
312 return (retval);
313 }
314 return (GMT->session.d_NaN);
315 }
316
gmtlib_bcr_get_img(struct GMT_CTRL * GMT,struct GMT_IMAGE * G,double xx,double yy,unsigned char * z)317 int gmtlib_bcr_get_img (struct GMT_CTRL *GMT, struct GMT_IMAGE *G, double xx, double yy, unsigned char *z) {
318 /* Given xx, yy in user's image file (in non-normalized units)
319 this routine returns the desired interpolated image value (nearest-neighbor, bilinear
320 B-spline or bicubic) at xx, yy. 8-bit components is assumed per band. */
321
322 unsigned int i, j, b, nb = G->header->n_bands;
323 uint64_t ij;
324 double retval[4], wsum, wx[4] = {0.0, 0.0, 0.0, 0.0}, wy[4] = {0.0, 0.0, 0.0, 0.0}, w;
325 struct GMT_GRID_HEADER_HIDDEN *HH = gmt_get_H_hidden (G->header);
326
327 /* First check that xx,yy are not Nan or outside domain - if so return NaN */
328
329 if (gmtbcr_reject (G->header, &xx, &yy)) return (1); /* NaNs or outside */
330
331 /* Determine nearest node ij and set weights wx wy */
332
333 ij = gmtbcr_prep (G->header, xx, yy, wx, wy);
334
335 gmt_M_memset (retval, 4, double);
336 wsum = 0.0;
337 for (j = 0; j < HH->bcr_n; j++) {
338 for (i = 0; i < HH->bcr_n; i++) {
339 w = wx[i] * wy[j];
340 wsum += w;
341 for (b = 0; b < nb; b++) retval[b] += G->data[nb*(ij+i)+b] * w;
342 }
343 ij += G->header->mx;
344 }
345 if ((wsum + GMT_CONV8_LIMIT - HH->bcr_threshold) > 0.0) { /* OK to evaluate result */
346 for (b = 0; b < nb; b++) {
347 retval[b] /= wsum;
348 z[b] = (unsigned char) lrint (gmt_M_0_255_truncate (retval[b]));
349 }
350 }
351 else
352 for (b = 0; b < nb; b++) z[b] = gmt_M_u255 (GMT->current.setting.color_patch[GMT_NAN][b]);
353 return (0);
354 }
355