interpolate.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /*!
  2. \file interpolate.c
  3. \brief Trilinear interpolation
  4. (C) 2014 by the GRASS Development Team
  5. This program is free software under the GNU General Public
  6. License (>=v2). Read the file COPYING that comes with GRASS
  7. for details.
  8. \author Anna Petrasova
  9. */
  10. #include <grass/gis.h>
  11. #include <grass/raster3d.h>
  12. #include "r3flow_structs.h"
  13. #include "interpolate.h"
  14. /*!
  15. \brief Finds 8 nearest voxels from a point.
  16. \param region pointer to current 3D region
  17. \param north,east,top geographic coordinates
  18. \param[out] x,y,z pointer to indices of neighbouring voxels
  19. */
  20. static void find_nearest_voxels(RASTER3D_Region * region,
  21. const double north, const double east,
  22. const double top, int *x, int *y, int *z)
  23. {
  24. double n_minus, e_minus, t_minus;
  25. double n_plus, e_plus, t_plus;
  26. n_minus = north - region->ns_res / 2;
  27. n_plus = north + region->ns_res / 2;
  28. e_minus = east - region->ew_res / 2;
  29. e_plus = east + region->ew_res / 2;
  30. t_minus = top - region->tb_res / 2;
  31. t_plus = top + region->tb_res / 2;
  32. Rast3d_location2coord(region, n_minus, e_minus, t_minus, x++, y++, z++);
  33. Rast3d_location2coord(region, n_minus, e_plus, t_minus, x++, y++, z++);
  34. Rast3d_location2coord(region, n_plus, e_minus, t_minus, x++, y++, z++);
  35. Rast3d_location2coord(region, n_plus, e_plus, t_minus, x++, y++, z++);
  36. Rast3d_location2coord(region, n_minus, e_minus, t_plus, x++, y++, z++);
  37. Rast3d_location2coord(region, n_minus, e_plus, t_plus, x++, y++, z++);
  38. Rast3d_location2coord(region, n_plus, e_minus, t_plus, x++, y++, z++);
  39. Rast3d_location2coord(region, n_plus, e_plus, t_plus, x, y, z);
  40. }
  41. /*!
  42. \brief Trilinear interpolation
  43. Computation is based on the sum of values of nearest voxels
  44. weighted by the relative distance of a point
  45. to the center of the nearest voxels.
  46. \param array_values pointer to values of 8 (neigboring) voxels
  47. \param x,y,z relative coordinates (0 - 1)
  48. \param[out] interpolated pointer to the array (of size 3) of interpolated values
  49. */
  50. static void trilinear_interpolation(const double *array_values,
  51. const double x, const double y,
  52. const double z, double *interpolated)
  53. {
  54. int i, j;
  55. double rx, ry, rz, value;
  56. double weights[8];
  57. rx = 1 - x;
  58. ry = 1 - y;
  59. rz = 1 - z;
  60. weights[0] = rx * ry * rz;
  61. weights[1] = x * ry * rz;
  62. weights[2] = rx * y * rz;
  63. weights[3] = x * y * rz;
  64. weights[4] = rx * ry * z;
  65. weights[5] = x * ry * z;
  66. weights[6] = rx * y * z;
  67. weights[7] = x * y * z;
  68. /* weighted sum of surrounding values */
  69. for (i = 0; i < 3; i++) {
  70. value = 0;
  71. for (j = 0; j < 8; j++) {
  72. value += weights[j] * array_values[i * 8 + j];
  73. }
  74. interpolated[i] = value;
  75. }
  76. }
  77. /*!
  78. \brief Converts geographic to relative coordinates
  79. Converts geographic to relative coordinates
  80. which are needed for trilinear interpolation.
  81. \param region pointer to current 3D region
  82. \param north,east,top geographic coordinates
  83. \param[out] x,y,z pointer to relative coordinates (0 - 1)
  84. */
  85. static void get_relative_coords_for_interp(RASTER3D_Region * region,
  86. const double north,
  87. const double east,
  88. const double top, double *x,
  89. double *y, double *z)
  90. {
  91. int col, row, depth;
  92. double temp;
  93. Rast3d_location2coord(region, north, east, top, &col, &row, &depth);
  94. /* x */
  95. temp = east - region->west - col * region->ew_res;
  96. *x = (temp > region->ew_res / 2 ?
  97. temp - region->ew_res / 2 : temp + region->ew_res / 2)
  98. / region->ew_res;
  99. /* y */
  100. temp = north - region->south - (region->rows - row - 1) * region->ns_res;
  101. *y = (temp > region->ns_res / 2 ?
  102. temp - region->ns_res / 2 : temp + region->ns_res / 2)
  103. / region->ns_res;
  104. /* z */
  105. temp = top - region->bottom - depth * region->tb_res;
  106. *z = (temp > region->tb_res / 2 ?
  107. temp - region->tb_res / 2 : temp + region->tb_res / 2)
  108. / region->tb_res;
  109. }
  110. /*!
  111. \brief Interpolates velocity at a given point.
  112. \param region pointer to current 3D region
  113. \param map pointer to array of 3 3D raster maps (velocity field)
  114. \param north,east,top geographic coordinates
  115. \param[out] vel_x,vel_y,vel_z interpolated velocity
  116. \return 0 success
  117. \return -1 out of region
  118. */
  119. int interpolate_velocity(RASTER3D_Region * region, RASTER3D_Map ** map,
  120. const double north, const double east,
  121. const double top, double *vel_x, double *vel_y,
  122. double *vel_z)
  123. {
  124. int i, j;
  125. double values[24]; /* 3 x 8, 3 dimensions, 8 neighbors */
  126. double value;
  127. double interpolated[3];
  128. int x[8], y[8], z[8];
  129. double rel_x, rel_y, rel_z;
  130. int type;
  131. /* check if we are out of region, any array should work */
  132. if (!Rast3d_is_valid_location(region, north, east, top))
  133. return -1;
  134. find_nearest_voxels(region, north, east, top, x, y, z);
  135. /* get values of the nearest cells */
  136. for (i = 0; i < 3; i++) {
  137. type = Rast3d_tile_type_map(map[i]);
  138. for (j = 0; j < 8; j++) {
  139. Rast3d_get_value_region(map[i], x[j], y[j], z[j], &value, type);
  140. if (Rast_is_null_value(&value, type))
  141. values[i * 8 + j] = 0;
  142. else
  143. values[i * 8 + j] = value;
  144. }
  145. }
  146. /* compute weights */
  147. get_relative_coords_for_interp(region, north, east, top,
  148. &rel_x, &rel_y, &rel_z);
  149. trilinear_interpolation(values, rel_x, rel_y, rel_z, interpolated);
  150. *vel_x = interpolated[0];
  151. *vel_y = interpolated[1];
  152. *vel_z = interpolated[2];
  153. return 0;
  154. }
  155. /*!
  156. \brief Computes gradient for a point.
  157. \param region pointer to current 3D region
  158. \param gradient_info struct which remembers values
  159. related to gradient computation to avoid computation every time
  160. \param north,east,top geographic coordinates
  161. \param[out] vel_x,vel_y,vel_z interpolated gradient components
  162. \return 0 success
  163. \return -1 out of region
  164. */
  165. int get_gradient(RASTER3D_Region * region,
  166. struct Gradient_info *gradient_info, const double north,
  167. const double east, const double top, double *vel_x, double *vel_y,
  168. double *vel_z)
  169. {
  170. int i, d, r, c, count;
  171. int near_x[8], near_y[8], near_z[8];
  172. int minx, maxx, miny, maxy, minz, maxz;
  173. int xshift, yshift, zshift;
  174. double rel_x, rel_y, rel_z;
  175. double scalar_map_array[64];
  176. double grad_x_map_array[64], grad_y_map_array[64], grad_z_map_array[64];
  177. RASTER3D_Array_double array;
  178. RASTER3D_Array_double grad_x, grad_y, grad_z;
  179. RASTER3D_Array_double *grad_xyz[3];
  180. double step[3];
  181. double interpolated[3];
  182. step[0] = region->ew_res;
  183. step[1] = region->ns_res;
  184. step[2] = region->tb_res;
  185. array.array = scalar_map_array;
  186. array.sx = array.sy = array.sz = 4;
  187. grad_x.array = grad_x_map_array;
  188. grad_y.array = grad_y_map_array;
  189. grad_z.array = grad_z_map_array;
  190. grad_x.sx = grad_x.sy = grad_x.sz = 4;
  191. grad_y.sx = grad_y.sy = grad_y.sz = 4;
  192. grad_z.sx = grad_z.sy = grad_z.sz = 4;
  193. find_nearest_voxels(region, north, east, top, near_x, near_y, near_z);
  194. minx = near_x[0];
  195. maxx = near_x[7];
  196. miny = near_y[7];
  197. maxy = near_y[0];
  198. minz = near_z[0];
  199. maxz = near_z[7];
  200. /* position of x, y, z neighboring voxels */
  201. if (!gradient_info->initialized ||
  202. (gradient_info->neighbors_pos[0] != minx ||
  203. gradient_info->neighbors_pos[1] != miny ||
  204. gradient_info->neighbors_pos[2] != minz)) {
  205. gradient_info->neighbors_pos[0] = minx;
  206. gradient_info->neighbors_pos[1] = miny;
  207. gradient_info->neighbors_pos[2] = minz;
  208. gradient_info->initialized = TRUE;
  209. /* just to be sure, we check that at least one voxel is inside */
  210. if (maxx < 0 || minx >= region->cols ||
  211. maxy < 0 || miny >= region->rows ||
  212. maxz < 0 || minz >= region->depths)
  213. return -1;
  214. /* these if's are here to handle edge cases
  215. min is changed to represent the min coords of the 4x4x4 array
  216. from which the gradient will be computed
  217. shift is relative position of the neighbors within this 4x4x4 array */
  218. if (minx == 0 || minx == -1) {
  219. xshift = minx;
  220. minx = 0;
  221. }
  222. else if (maxx >= region->cols - 1) {
  223. minx = maxx < region->cols ? maxx - 3 : maxx - 4;
  224. xshift = maxx < region->cols ? 2 : 3;
  225. }
  226. else {
  227. minx -= 1;
  228. xshift = 1;
  229. }
  230. if (miny == 0 || miny == -1) {
  231. yshift = miny;
  232. miny = 0;
  233. }
  234. else if (maxy >= region->rows - 1) {
  235. miny = maxy < region->rows ? maxy - 3 : maxy - 4;
  236. yshift = maxy < region->rows ? 2 : 3;
  237. }
  238. else {
  239. miny -= 1;
  240. yshift = 1;
  241. }
  242. if (minz == 0 || minz == -1) {
  243. zshift = minz;
  244. minz = 0;
  245. }
  246. else if (maxz >= region->depths - 1) {
  247. minz = maxz < region->depths ? maxz - 3 : maxz - 4;
  248. zshift = maxz < region->depths ? 2 : 3;
  249. }
  250. else {
  251. minz -= 1;
  252. zshift = 1;
  253. }
  254. /* get the 4x4x4 block of the array */
  255. Rast3d_get_block(gradient_info->scalar_map, minx, miny, minz,
  256. 4, 4, 4, array.array, DCELL_TYPE);
  257. Rast3d_gradient_double(&array, step, &grad_x, &grad_y, &grad_z);
  258. grad_xyz[0] = &grad_x;
  259. grad_xyz[1] = &grad_y;
  260. grad_xyz[2] = &grad_z;
  261. /* go through x, y, z and all 8 neighbors and store their value
  262. if the voxel is outside, add 0 (weight) */
  263. for (i = 0; i < 3; i++) {
  264. count = 0;
  265. for (d = 0; d < 2; d++)
  266. for (r = 1; r > -1; r--)
  267. for (c = 0; c < 2; c++) {
  268. if (d + zshift < 0 || d + zshift > 3 ||
  269. r + yshift < 0 || r + yshift > 3 ||
  270. c + xshift < 0 || c + xshift > 3)
  271. gradient_info->neighbors_values[i * 8 + count] =
  272. 0;
  273. else
  274. gradient_info->neighbors_values[i * 8 + count] =
  275. RASTER3D_ARRAY_ACCESS(grad_xyz[i], c + xshift,
  276. r + yshift, d + zshift);
  277. count++;
  278. }
  279. }
  280. }
  281. get_relative_coords_for_interp(region, north, east, top, &rel_x, &rel_y, &rel_z);
  282. trilinear_interpolation(gradient_info->neighbors_values,
  283. rel_x, rel_y, rel_z, interpolated);
  284. *vel_x = interpolated[0];
  285. *vel_y = interpolated[1];
  286. *vel_z = interpolated[2];
  287. return 0;
  288. }