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