do_astar.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. #include <stdlib.h>
  2. #include <math.h>
  3. #include <grass/raster.h>
  4. #include <grass/glocale.h>
  5. #include "local_proto.h"
  6. #define GET_PARENT(c) ((((c) - 2) >> 3) + 1)
  7. #define GET_CHILD(p) (((p) << 3) - 6)
  8. HEAP_PNT heap_drop(void);
  9. static double get_slope(CELL, CELL, double);
  10. int do_astar(void)
  11. {
  12. int r, c, r_nbr, c_nbr, ct_dir;
  13. GW_LARGE_INT first_cum, count;
  14. int nextdr[8] = { 1, -1, 0, 0, -1, 1, 1, -1 };
  15. int nextdc[8] = { 0, 0, -1, 1, 1, -1, 1, -1 };
  16. CELL ele_val, ele_up, ele_nbr[8];
  17. WAT_ALT wa;
  18. ASP_FLAG af;
  19. char is_in_list, is_worked;
  20. HEAP_PNT heap_p;
  21. /* sides
  22. * |7|1|4|
  23. * |2| |3|
  24. * |5|0|6|
  25. */
  26. int nbr_ew[8] = { 0, 1, 2, 3, 1, 0, 0, 1 };
  27. int nbr_ns[8] = { 0, 1, 2, 3, 3, 2, 3, 2 };
  28. double dx, dy, dist_to_nbr[8], ew_res, ns_res;
  29. double slope[8];
  30. struct Cell_head window;
  31. int skip_diag;
  32. count = 0;
  33. first_cum = n_points;
  34. G_message(_("A* Search..."));
  35. Rast_get_window(&window);
  36. for (ct_dir = 0; ct_dir < sides; ct_dir++) {
  37. /* get r, c (r_nbr, c_nbr) for neighbours */
  38. r_nbr = nextdr[ct_dir];
  39. c_nbr = nextdc[ct_dir];
  40. /* account for rare cases when ns_res != ew_res */
  41. dy = abs(r_nbr) * window.ns_res;
  42. dx = abs(c_nbr) * window.ew_res;
  43. if (ct_dir < 4)
  44. dist_to_nbr[ct_dir] = dx + dy;
  45. else
  46. dist_to_nbr[ct_dir] = sqrt(dx * dx + dy * dy);
  47. }
  48. ew_res = window.ew_res;
  49. ns_res = window.ns_res;
  50. while (heap_size > 0) {
  51. G_percent(count++, n_points, 1);
  52. if (count > n_points)
  53. G_fatal_error(_("BUG in A* Search: %lld surplus points"),
  54. heap_size);
  55. if (heap_size > n_points)
  56. G_fatal_error
  57. (_("BUG in A* Search: too many points in heap %lld, should be %lld"),
  58. heap_size, n_points);
  59. heap_p = heap_drop();
  60. r = heap_p.pnt.r;
  61. c = heap_p.pnt.c;
  62. ele_val = heap_p.ele;
  63. for (ct_dir = 0; ct_dir < sides; ct_dir++) {
  64. /* get r, c (r_nbr, c_nbr) for neighbours */
  65. r_nbr = r + nextdr[ct_dir];
  66. c_nbr = c + nextdc[ct_dir];
  67. slope[ct_dir] = ele_nbr[ct_dir] = 0;
  68. skip_diag = 0;
  69. /* check that neighbour is within region */
  70. if (r_nbr < 0 || r_nbr >= nrows || c_nbr < 0 || c_nbr >= ncols)
  71. continue;
  72. seg_get(&aspflag, (char *)&af, r_nbr, c_nbr);
  73. is_in_list = FLAG_GET(af.flag, INLISTFLAG);
  74. is_worked = FLAG_GET(af.flag, WORKEDFLAG);
  75. if (!is_worked) {
  76. seg_get(&watalt, (char *)&wa, r_nbr, c_nbr);
  77. ele_nbr[ct_dir] = wa.ele;
  78. slope[ct_dir] = get_slope(ele_val, ele_nbr[ct_dir],
  79. dist_to_nbr[ct_dir]);
  80. }
  81. /* avoid diagonal flow direction bias */
  82. if (!is_in_list) {
  83. if (ct_dir > 3 && slope[ct_dir] > 0) {
  84. if (slope[nbr_ew[ct_dir]] > 0) {
  85. /* slope to ew nbr > slope to center */
  86. if (slope[ct_dir] <
  87. get_slope(ele_nbr[nbr_ew[ct_dir]],
  88. ele_nbr[ct_dir], ew_res))
  89. skip_diag = 1;
  90. }
  91. if (!skip_diag && slope[nbr_ns[ct_dir]] > 0) {
  92. /* slope to ns nbr > slope to center */
  93. if (slope[ct_dir] <
  94. get_slope(ele_nbr[nbr_ns[ct_dir]],
  95. ele_nbr[ct_dir], ns_res))
  96. skip_diag = 1;
  97. }
  98. }
  99. }
  100. if (!skip_diag) {
  101. if (is_in_list == 0) {
  102. ele_up = ele_nbr[ct_dir];
  103. af.asp = drain[r_nbr - r + 1][c_nbr - c + 1];
  104. heap_add(r_nbr, c_nbr, ele_up);
  105. FLAG_SET(af.flag, INLISTFLAG);
  106. seg_put(&aspflag, (char *)&af, r_nbr, c_nbr);
  107. }
  108. else if (is_in_list && is_worked == 0) {
  109. if (FLAG_GET(af.flag, EDGEFLAG)) {
  110. /* neighbour is edge in list, not yet worked */
  111. if (af.asp < 0) {
  112. /* adjust flow direction for edge cell */
  113. af.asp = drain[r_nbr - r + 1][c_nbr - c + 1];
  114. seg_put(&aspflag, (char *)&af, r_nbr, c_nbr);
  115. }
  116. }
  117. else if (FLAG_GET(af.flag, DEPRFLAG)) {
  118. G_debug(3, "real depression");
  119. /* neighbour is inside real depression, not yet worked */
  120. if (af.asp == 0 && ele_val <= ele_nbr[ct_dir]) {
  121. af.asp = drain[r_nbr - r + 1][c_nbr - c + 1];
  122. FLAG_UNSET(af.flag, DEPRFLAG);
  123. seg_put(&aspflag, (char *)&af, r_nbr, c_nbr);
  124. }
  125. }
  126. }
  127. }
  128. } /* end neighbours */
  129. /* add astar points to sorted list for flow accumulation and stream extraction */
  130. first_cum--;
  131. seg_put(&astar_pts, (char *)&heap_p.pnt, 0, first_cum);
  132. seg_get(&aspflag, (char *)&af, r, c);
  133. FLAG_SET(af.flag, WORKEDFLAG);
  134. seg_put(&aspflag, (char *)&af, r, c);
  135. } /* end A* search */
  136. G_percent(n_points, n_points, 1); /* finish it */
  137. return 1;
  138. }
  139. /*
  140. * compare function for heap
  141. * returns 1 if point1 < point2 else 0
  142. */
  143. static int heap_cmp(HEAP_PNT *a, HEAP_PNT *b)
  144. {
  145. if (a->ele < b->ele)
  146. return 1;
  147. else if (a->ele == b->ele) {
  148. if (a->added < b->added)
  149. return 1;
  150. }
  151. return 0;
  152. }
  153. static int sift_up(GW_LARGE_INT start, HEAP_PNT child_p)
  154. {
  155. GW_LARGE_INT parent, child;
  156. HEAP_PNT heap_p;
  157. child = start;
  158. while (child > 1) {
  159. parent = GET_PARENT(child);
  160. seg_get(&search_heap, (char *)&heap_p, 0, parent);
  161. /* push parent point down if child is smaller */
  162. if (heap_cmp(&child_p, &heap_p)) {
  163. seg_put(&search_heap, (char *)&heap_p, 0, child);
  164. child = parent;
  165. }
  166. else
  167. /* no more sifting up, found slot for child */
  168. break;
  169. }
  170. /* add child to heap */
  171. seg_put(&search_heap, (char *)&child_p, 0, child);
  172. return 0;
  173. }
  174. /*
  175. * add item to heap
  176. * returns heap_size
  177. */
  178. GW_LARGE_INT heap_add(int r, int c, CELL ele)
  179. {
  180. HEAP_PNT heap_p;
  181. /* add point to next free position */
  182. heap_size++;
  183. heap_p.added = nxt_avail_pt;
  184. heap_p.ele = ele;
  185. heap_p.pnt.r = r;
  186. heap_p.pnt.c = c;
  187. nxt_avail_pt++;
  188. /* sift up: move new point towards top of heap */
  189. sift_up(heap_size, heap_p);
  190. return heap_size;
  191. }
  192. /*
  193. * drop item from heap
  194. * returns heap size
  195. */
  196. HEAP_PNT heap_drop(void)
  197. {
  198. GW_LARGE_INT child, childr, parent;
  199. GW_LARGE_INT i;
  200. HEAP_PNT child_p, childr_p, last_p, root_p;
  201. seg_get(&search_heap, (char *)&last_p, 0, heap_size);
  202. seg_get(&search_heap, (char *)&root_p, 0, 1);
  203. if (heap_size == 1) {
  204. heap_size = 0;
  205. return root_p;
  206. }
  207. parent = 1;
  208. while ((child = GET_CHILD(parent)) < heap_size) {
  209. seg_get(&search_heap, (char *)&child_p, 0, child);
  210. if (child < heap_size) {
  211. childr = child + 1;
  212. i = child + 8;
  213. while (childr < heap_size && childr < i) {
  214. seg_get(&search_heap, (char *)&childr_p, 0, childr);
  215. if (heap_cmp(&childr_p, &child_p)) {
  216. child = childr;
  217. child_p = childr_p;
  218. }
  219. childr++;
  220. }
  221. }
  222. if (heap_cmp(&last_p, &child_p)) {
  223. break;
  224. }
  225. /* move hole down */
  226. seg_put(&search_heap, (char *)&child_p, 0, parent);
  227. parent = child;
  228. }
  229. /* fill hole */
  230. if (parent < heap_size) {
  231. seg_put(&search_heap, (char *)&last_p, 0, parent);
  232. }
  233. /* the actual drop */
  234. heap_size--;
  235. return root_p;
  236. }
  237. static double get_slope(CELL ele, CELL up_ele, double dist)
  238. {
  239. if (ele >= up_ele)
  240. return 0.0;
  241. else
  242. return (double)(up_ele - ele) / dist;
  243. }