do_astar.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. #include "Gwater.h"
  2. #include "do_astar.h"
  3. #include <grass/gis.h>
  4. #include <grass/glocale.h>
  5. static double get_slope2(CELL, CELL, double);
  6. int do_astar(void)
  7. {
  8. int count;
  9. int upr, upc, r, c, ct_dir;
  10. CELL alt_val, alt_nbr[8];
  11. CELL is_in_list, is_worked, flat_is_done, nbr_flat_is_done;
  12. int index_doer, index_up;
  13. /* sides
  14. * |7|1|4|
  15. * |2| |3|
  16. * |5|0|6|
  17. */
  18. int nbr_ew[8] = { 0, 1, 2, 3, 1, 0, 0, 1 };
  19. int nbr_ns[8] = { 0, 1, 2, 3, 3, 2, 3, 2 };
  20. double dx, dy, dist_to_nbr[8], ew_res, ns_res;
  21. double slope[8];
  22. int skip_diag;
  23. CELL *alt_bak;
  24. G_message(_("SECTION 2: A* Search."));
  25. for (ct_dir = 0; ct_dir < sides; ct_dir++) {
  26. /* get r, c (upr, upc) for neighbours */
  27. upr = nextdr[ct_dir];
  28. upc = nextdc[ct_dir];
  29. /* account for rare cases when ns_res != ew_res */
  30. dy = ABS(upr) * window.ns_res;
  31. dx = ABS(upc) * window.ew_res;
  32. if (ct_dir < 4)
  33. dist_to_nbr[ct_dir] = dx + dy;
  34. else
  35. dist_to_nbr[ct_dir] = sqrt(dx * dx + dy * dy);
  36. }
  37. ew_res = window.ew_res;
  38. ns_res = window.ns_res;
  39. count = 0;
  40. first_astar = heap_index[1];
  41. first_cum = do_points;
  42. if (flat_flag) {
  43. alt_bak =
  44. (CELL *) G_malloc(sizeof(CELL) * size_array(&alt_seg, nrows, ncols));
  45. flat_done = flag_create(nrows, ncols);
  46. flat_is_done = 0;
  47. for (r = 0; r < nrows; r++) {
  48. for (c = 0; c < ncols; c++) {
  49. index_doer = SEG_INDEX(alt_seg, r, c);
  50. alt_bak[index_doer] = alt[index_doer];
  51. flag_unset(flat_done, r, c);
  52. }
  53. }
  54. }
  55. else {
  56. alt_bak = NULL;
  57. flat_done = NULL;
  58. flat_is_done = 1;
  59. }
  60. /* A* Search: search uphill, get downhill paths */
  61. while (heap_size > 0) {
  62. G_percent(count++, do_points, 1);
  63. /* get point with lowest elevation, in case of equal elevation
  64. * of following points, oldest point = point added earliest */
  65. index_doer = astar_pts[1];
  66. drop_pt();
  67. /* add astar points to sorted list for flow accumulation */
  68. astar_pts[first_cum] = index_doer;
  69. first_cum--;
  70. seg_index_rc(alt_seg, index_doer, &r, &c);
  71. G_debug(3, "A* Search: row %d, column %d, ", r, c);
  72. alt_val = alt[index_doer];
  73. if (flat_flag) {
  74. flat_is_done = FLAG_GET(flat_done, r, c);
  75. }
  76. /* check all neighbours, breadth first search */
  77. for (ct_dir = 0; ct_dir < sides; ct_dir++) {
  78. /* get r, c (upr, upc) for this neighbour */
  79. upr = r + nextdr[ct_dir];
  80. upc = c + nextdc[ct_dir];
  81. slope[ct_dir] = alt_nbr[ct_dir] = 0;
  82. /* check if r, c are within region */
  83. if (upr >= 0 && upr < nrows && upc >= 0 && upc < ncols) {
  84. index_up = SEG_INDEX(alt_seg, upr, upc);
  85. is_in_list = FLAG_GET(in_list, upr, upc);
  86. is_worked = FLAG_GET(worked, upr, upc);
  87. skip_diag = 0;
  88. alt_nbr[ct_dir] = alt[index_up];
  89. if (flat_flag && !is_in_list && !is_worked) {
  90. alt_val = alt_bak[index_doer];
  91. alt_nbr[ct_dir] = alt_bak[index_up];
  92. if (!flat_is_done && alt_nbr[ct_dir] == alt_val) {
  93. do_flatarea(index_doer, alt_val, alt_bak, alt);
  94. alt_nbr[ct_dir] = alt[index_up];
  95. flat_is_done = 1;
  96. nbr_flat_is_done = 1;
  97. }
  98. nbr_flat_is_done = FLAG_GET(flat_done, upr, upc);
  99. if (!nbr_flat_is_done) {
  100. /* use original ele values */
  101. alt_val = alt_bak[index_doer];
  102. alt_nbr[ct_dir] = alt_bak[index_up];
  103. }
  104. else {
  105. /* use modified ele values */
  106. alt_val = alt[index_doer];
  107. alt_nbr[ct_dir] = alt[index_up];
  108. }
  109. }
  110. /* avoid diagonal flow direction bias */
  111. if (!is_worked) {
  112. slope[ct_dir] =
  113. get_slope2(alt_val, alt_nbr[ct_dir],
  114. dist_to_nbr[ct_dir]);
  115. }
  116. if (!is_in_list) {
  117. if (ct_dir > 3 && slope[ct_dir] > 0) {
  118. if (slope[nbr_ew[ct_dir]] > 0) {
  119. /* slope to ew nbr > slope to center */
  120. if (slope[ct_dir] <
  121. get_slope2(alt_nbr[nbr_ew[ct_dir]],
  122. alt_nbr[ct_dir], ew_res))
  123. skip_diag = 1;
  124. }
  125. if (!skip_diag && slope[nbr_ns[ct_dir]] > 0) {
  126. /* slope to ns nbr > slope to center */
  127. if (slope[ct_dir] <
  128. get_slope2(alt_nbr[nbr_ns[ct_dir]],
  129. alt_nbr[ct_dir], ns_res))
  130. skip_diag = 1;
  131. }
  132. }
  133. }
  134. if (!skip_diag) {
  135. /* add neighbour as new point if not in the list */
  136. if (is_in_list == 0) {
  137. add_pt(upr, upc, alt_nbr[ct_dir]);
  138. /* set flow direction */
  139. asp[index_up] = drain[upr - r + 1][upc - c + 1];
  140. }
  141. else if (is_in_list && is_worked == 0) {
  142. /* neighbour is edge in list, not yet worked */
  143. if (asp[index_up] < 0) {
  144. asp[index_up] = drain[upr - r + 1][upc - c + 1];
  145. if (wat[index_doer] > 0)
  146. wat[index_doer] = -1.0 * wat[index_doer];
  147. }
  148. /* neighbour is inside real depression, not yet worked */
  149. else if (asp[index_up] == 0) {
  150. asp[index_up] = drain[upr - r + 1][upc - c + 1];
  151. }
  152. }
  153. }
  154. } /* end if in region */
  155. } /* end sides */
  156. FLAG_SET(worked, r, c);
  157. }
  158. G_percent(count, do_points, 1); /* finish it */
  159. if (mfd == 0)
  160. flag_destroy(worked);
  161. flag_destroy(in_list);
  162. G_free(heap_index);
  163. if (flat_flag) {
  164. for (r = 0; r < nrows; r++) {
  165. for (c = 0; c < ncols; c++) {
  166. index_doer = SEG_INDEX(alt_seg, r, c);
  167. alt[index_doer] = alt_bak[index_doer];
  168. }
  169. }
  170. G_free(alt_bak);
  171. flag_destroy(flat_done);
  172. }
  173. return 0;
  174. }
  175. /* compare two heap points */
  176. /* return 1 if a < b else 0 */
  177. static int cmp_pnt(CELL elea, CELL eleb, int addeda, int addedb)
  178. {
  179. if (elea == eleb) {
  180. return (addeda < addedb);
  181. }
  182. return (elea < eleb);
  183. }
  184. /* standard sift-up routine for d-ary min heap */
  185. static int sift_up(int start, CELL ele)
  186. {
  187. register int parent, child, child_idx, child_added;
  188. CELL elep;
  189. child = start;
  190. child_added = heap_index[child];
  191. child_idx = astar_pts[child];
  192. while (child > 1) {
  193. GET_PARENT(parent, child);
  194. elep = alt[astar_pts[parent]];
  195. /* child smaller */
  196. if (cmp_pnt(ele, elep, child_added, heap_index[parent])) {
  197. /* push parent point down */
  198. heap_index[child] = heap_index[parent];
  199. astar_pts[child] = astar_pts[parent];
  200. child = parent;
  201. }
  202. else
  203. /* no more sifting up, found new slot for child */
  204. break;
  205. }
  206. /* put point in new slot */
  207. if (child < start) {
  208. heap_index[child] = child_added;
  209. astar_pts[child] = child_idx;
  210. }
  211. return 0;
  212. }
  213. /* add point routine for min heap */
  214. int add_pt(int r, int c, CELL ele)
  215. {
  216. FLAG_SET(in_list, r, c);
  217. /* add point to next free position */
  218. heap_size++;
  219. if (heap_size > do_points)
  220. G_fatal_error(_("heapsize too large"));
  221. heap_index[heap_size] = nxt_avail_pt++;
  222. astar_pts[heap_size] = SEG_INDEX(alt_seg, r, c);
  223. /* sift up: move new point towards top of heap */
  224. sift_up(heap_size, ele);
  225. return 0;
  226. }
  227. /* drop point routine for min heap */
  228. int drop_pt(void)
  229. {
  230. register int child, childr, parent;
  231. CELL ele, eler;
  232. register int i;
  233. if (heap_size == 1) {
  234. heap_index[1] = -1;
  235. heap_size = 0;
  236. return 0;
  237. }
  238. /* start with root */
  239. parent = 1;
  240. /* sift down: move hole back towards bottom of heap */
  241. while (GET_CHILD(child, parent) <= heap_size) {
  242. /* select child with lower ele, if both are equal, older child
  243. * older child is older startpoint for flow path, important */
  244. ele = alt[astar_pts[child]];
  245. i = child + 3;
  246. for (childr = child + 1; childr <= heap_size && childr < i; childr++) {
  247. eler = alt[astar_pts[childr]];
  248. if (cmp_pnt(eler, ele, heap_index[childr], heap_index[child])) {
  249. child = childr;
  250. ele = eler;
  251. }
  252. }
  253. /* move hole down */
  254. heap_index[parent] = heap_index[child];
  255. astar_pts[parent] = astar_pts[child];
  256. parent = child;
  257. }
  258. /* hole is in lowest layer, move to heap end */
  259. if (parent < heap_size) {
  260. heap_index[parent] = heap_index[heap_size];
  261. astar_pts[parent] = astar_pts[heap_size];
  262. ele = alt[astar_pts[parent]];
  263. /* sift up last swapped point, only necessary if hole moved to heap end */
  264. sift_up(parent, ele);
  265. }
  266. /* the actual drop */
  267. heap_size--;
  268. return 0;
  269. }
  270. double
  271. get_slope(int r, int c, int downr, int downc, CELL ele, CELL downe)
  272. {
  273. double slope;
  274. if (r == downr)
  275. slope = (ele - downe) / window.ew_res;
  276. else if (c == downc)
  277. slope = (ele - downe) / window.ns_res;
  278. else
  279. slope = (ele - downe) / diag;
  280. if (slope < MIN_SLOPE)
  281. return (MIN_SLOPE);
  282. return (slope);
  283. }
  284. static double get_slope2(CELL ele, CELL up_ele, double dist)
  285. {
  286. if (ele >= up_ele)
  287. return 0.0;
  288. else
  289. return (double)(up_ele - ele) / dist;
  290. }