weightWindow.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /****************************************************************************
  2. *
  3. * MODULE: r.terraflow
  4. *
  5. * COPYRIGHT (C) 2007 Laura Toma
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. *****************************************************************************/
  18. #include <stdio.h>
  19. #include <assert.h>
  20. #include <math.h>
  21. #include "weightWindow.h"
  22. #include "direction.h"
  23. /* #define CHECK_WEIGHTS */
  24. /* enables printing weights as they are computed */
  25. /*
  26. Distribute flow to neighbors as in "The prediction of HIllslope
  27. Flow Paths for Distributed Hydrollogical Modeling using Digital
  28. Terrain Models" by Quinn, Chevallier, Planchon, in Hydrollogical
  29. Processes vol. 5, 1991
  30. */
  31. /***************************************************************/
  32. weightWindow::weightWindow(const float dx, const float dy) :
  33. cell_dx(dx), cell_dy(dy) {
  34. celldiag = sqrt(dx*dx + dy*dy);
  35. sumweight = sumcontour = 0;
  36. }
  37. /***************************************************************/
  38. /* initialize all weights to 0 */
  39. void
  40. weightWindow::init() {
  41. sumweight = sumcontour = (float)0;
  42. for (int l = 0;l < 9; l++) {
  43. weight.set(l,(float)0);
  44. }
  45. }
  46. /***************************************************************/
  47. /* set weight of neighbor (di,dj) equal to e_diff x
  48. computeContour/computeDist. This basically reduces to e_diff/2 (if
  49. not on diagonal) or e_diff/4 (if on diagonal).
  50. */
  51. void
  52. weightWindow::computeWeight(const short di, const short dj,
  53. const elevation_type elev_crt,
  54. const elevation_type elev_neighb) {
  55. /* NOTE: it is possible that elev_neighb is EDGE_NODATA. In this
  56. case, we just consider the value of EDGE_NODATA as an elevation,
  57. and push flow to it. Currently the value of EDGE_NODATA is -9998,
  58. and thus these cells will get most of the flow. */
  59. elevation_type e_diff = elev_crt - elev_neighb;
  60. assert(e_diff >= 0);
  61. if (di == 0 && dj == 0) {
  62. return;
  63. }
  64. double contour, flow;
  65. if (dj==0) {
  66. flow = 0.5;
  67. contour = cell_dy/2;
  68. } else if (di ==0) {
  69. flow = 0.5;
  70. contour = cell_dx/2;
  71. } else { /* on diagonal */
  72. flow = 0.25;
  73. contour = celldiag/4;
  74. }
  75. assert(contour > 0);
  76. /* at this point, 'flow' corresponds to the relative distance to the
  77. neighbor: 0.5 if horizontal/vertical, or 0.25 if diagonal. Diagonal
  78. points are further away. These are somewhat arbitrary; see paper.
  79. 'contour' is the length perpendicular to the flow toward the
  80. neighbor. */
  81. if (e_diff > 0) {
  82. flow *= e_diff;
  83. } else {
  84. /* NOTE: how much flow to distribute to neighbors which are
  85. at same height?? */
  86. flow *= 1.0/contour; /* NOTE: this may cause overflow if contour
  87. is v small */
  88. }
  89. weight.set(di, dj, flow);
  90. sumcontour += contour;
  91. sumweight += flow;
  92. }
  93. /***************************************************************/
  94. /* computes and returns the distance corresponding to this direction */
  95. double
  96. weightWindow::computeDist(const short di, const short dj) {
  97. double dist;
  98. if (di == 0 && dj == 0) {
  99. return 0;
  100. }
  101. if (dj==0) {
  102. dist = cell_dy;
  103. } else if (di ==0) {
  104. dist = cell_dx;
  105. } else { /* on diagonal */
  106. dist = celldiag;
  107. }
  108. assert(dist > 0);
  109. return dist;
  110. }
  111. /***************************************************************/
  112. /* computes and returns the contour corresponding to this direction */
  113. double
  114. weightWindow::computeContour(const short di, const short dj) {
  115. double contour;
  116. if (di == 0 && dj == 0) {
  117. return 0;
  118. }
  119. if (dj==0) {
  120. contour = cell_dy/2;
  121. } else if (di ==0) {
  122. contour = cell_dx/2;
  123. } else { /* on diagonal */
  124. contour = celldiag/4;
  125. }
  126. assert(contour > 0);
  127. return contour;
  128. }
  129. /***************************************************************/
  130. /* compute the tanB corresponding to the elevation window and neighbor
  131. di,dj. */
  132. double
  133. weightWindow::computeTanB(const short di,const short dj,
  134. const genericWindow<elevation_type>& elevwin) {
  135. assert(di != 0 || dj != 0);
  136. double dist = computeDist(di, dj);
  137. assert(dist > 0);
  138. return (elevwin.get() - elevwin.get(di, dj)) / dist;
  139. }
  140. /***************************************************************/
  141. void
  142. weightWindow::normalize() {
  143. if (sumweight > 0) {
  144. weight.scalarMultiply(1.0/sumweight);
  145. }
  146. }
  147. /***************************************************************/
  148. /* compute the weights of the neighbors of a cell given an elevation
  149. window and precomputed directions dir; if trustdir = 1 then trust
  150. directions; otherwise push to all downslope neighbors and use dir
  151. only for cells which do not have any downslope neighbors */
  152. /***************************************************************/
  153. void
  154. weightWindow::compute(const dimension_type i, const dimension_type j,
  155. const genericWindow<elevation_type>& elevwin,
  156. const direction_type dir,
  157. const int trustdir) {
  158. elevation_type elev_crt, elev_neighb;
  159. /* initialize all weights to 0 */
  160. init();
  161. elev_crt = elevwin.get();
  162. assert(!is_nodata(elev_crt));
  163. /* map direction to neighbors */
  164. directionWindow dirwin(dir);
  165. /* compute weights of the 8 neighbours */
  166. int skipit = 0;
  167. for (short di = -1; di <= 1; di++) {
  168. for (short dj = -1; dj <= 1; dj++) {
  169. /* grid coordinates and elevation of neighbour */
  170. elev_neighb = elevwin.get(di, dj);
  171. skipit = ((di ==0) && (dj==0));
  172. skipit |= (elev_crt < elev_neighb);
  173. /* skipit |= (elev_neighb == edge_nodata); ?? */
  174. if (!trustdir) {
  175. dirwin.correctDirection(di,dj,skipit, i,j, elev_crt,dir,elev_neighb);
  176. }
  177. /* if direction points to it then compute its weight */
  178. if (dirwin.get(di,dj) == true) {
  179. computeWeight(di,dj, elev_crt, elev_neighb);
  180. }
  181. } /* for dj */
  182. } /* for di */
  183. normalize(); /* normalize the weights */
  184. #ifdef CHECK_WEIGHTS
  185. cout <<"weights: [";
  186. for (int l=0;l<9;l++) cout << form("%3.2f ",weight.get(l));
  187. cout <<"]\n";
  188. #endif
  189. };
  190. /* Find the dominant direction. Set corresponding weight to 1, and
  191. sets all other weights to 0. Set sumweight and sumcontour.*/
  192. void
  193. weightWindow::makeD8(const dimension_type i, const dimension_type j,
  194. const genericWindow<elevation_type>& elevwin,
  195. const direction_type dir,
  196. const bool trustdir) {
  197. elevation_type elev_crt;
  198. short di,dj;
  199. elev_crt = elevwin.get();
  200. assert(!is_nodata(elev_crt));
  201. int maxi=0, maxj=0;
  202. double tanb, contour, maxtanb = -1, maxcontour = -1;
  203. /* map direction to neighbors */
  204. directionWindow dirwin(dir);
  205. /* compute biggest angle to a neighbor */
  206. for (di=-1; di <=1; di++) {
  207. for (dj = -1; dj <= 1; dj++) {
  208. if (dirwin.get(di,dj)) {
  209. tanb = computeTanB(di,dj, elevwin);
  210. contour = computeContour(di, dj);
  211. if (tanb > maxtanb) {
  212. maxtanb = tanb;
  213. maxi = di;
  214. maxj = dj;
  215. maxcontour = contour;
  216. }
  217. }
  218. }
  219. }
  220. /* at this point maxi and maxj must be set */
  221. assert((maxi != 0 || maxj != 0) && maxtanb >= 0);
  222. /* set weights corresponding to this direction */
  223. init(); /* initialize all weights to 0 */
  224. int maxindex = 3* (maxi + 1) + maxj+1;
  225. weight.set(maxindex,1); /* set maxweight to 1 */
  226. sumweight = 1;
  227. sumcontour = maxcontour;
  228. }