123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- #include <stdlib.h>
- #include <math.h>
- #include <grass/raster.h>
- #include <grass/glocale.h>
- #include "local_proto.h"
- #define GET_PARENT(c) ((((GW_LARGE_INT)(c) - 2) >> 3) + 1)
- #define GET_CHILD(p) (((GW_LARGE_INT)(p) << 3) - 6)
- HEAP_PNT heap_drop(void);
- static double get_slope(CELL, CELL, double);
- int do_astar(void)
- {
- int r, c, r_nbr, c_nbr, ct_dir;
- GW_LARGE_INT first_cum, count;
- int nextdr[8] = { 1, -1, 0, 0, -1, 1, 1, -1 };
- int nextdc[8] = { 0, 0, -1, 1, 1, -1, 1, -1 };
- CELL ele_val, ele_up, ele_nbr[8];
- WAT_ALT wa;
- ASP_FLAG af;
- char is_in_list, is_worked;
- HEAP_PNT heap_p;
- /* sides
- * |7|1|4|
- * |2| |3|
- * |5|0|6|
- */
- int nbr_ew[8] = { 0, 1, 2, 3, 1, 0, 0, 1 };
- int nbr_ns[8] = { 0, 1, 2, 3, 3, 2, 3, 2 };
- double dx, dy, dist_to_nbr[8], ew_res, ns_res;
- double slope[8];
- struct Cell_head window;
- int skip_diag;
- count = 0;
- first_cum = n_points;
- G_message(_("A* Search..."));
- Rast_get_window(&window);
- for (ct_dir = 0; ct_dir < sides; ct_dir++) {
- /* get r, c (r_nbr, c_nbr) for neighbours */
- r_nbr = nextdr[ct_dir];
- c_nbr = nextdc[ct_dir];
- /* account for rare cases when ns_res != ew_res */
- dy = abs(r_nbr) * window.ns_res;
- dx = abs(c_nbr) * window.ew_res;
- if (ct_dir < 4)
- dist_to_nbr[ct_dir] = dx + dy;
- else
- dist_to_nbr[ct_dir] = sqrt(dx * dx + dy * dy);
- }
- ew_res = window.ew_res;
- ns_res = window.ns_res;
-
- while (heap_size > 0) {
- G_percent(count++, n_points, 1);
- if (count > n_points)
- G_fatal_error(_("%lld surplus points"),
- heap_size);
- if (heap_size > n_points)
- G_fatal_error
- (_("Too many points in heap %lld, should be %lld"),
- heap_size, n_points);
- heap_p = heap_drop();
- r = heap_p.pnt.r;
- c = heap_p.pnt.c;
- ele_val = heap_p.ele;
- for (ct_dir = 0; ct_dir < sides; ct_dir++) {
- /* get r, c (r_nbr, c_nbr) for neighbours */
- r_nbr = r + nextdr[ct_dir];
- c_nbr = c + nextdc[ct_dir];
- slope[ct_dir] = -1;
- ele_nbr[ct_dir] = 0;
- skip_diag = 0;
- /* check that neighbour is within region */
- if (r_nbr < 0 || r_nbr >= nrows || c_nbr < 0 || c_nbr >= ncols)
- continue;
- seg_get(&aspflag, (char *)&af, r_nbr, c_nbr);
- is_in_list = FLAG_GET(af.flag, INLISTFLAG);
- is_worked = FLAG_GET(af.flag, WORKEDFLAG);
- if (!is_worked) {
- seg_get(&watalt, (char *)&wa, r_nbr, c_nbr);
- ele_nbr[ct_dir] = wa.ele;
- slope[ct_dir] = get_slope(ele_val, ele_nbr[ct_dir],
- dist_to_nbr[ct_dir]);
- }
- /* avoid diagonal flow direction bias */
- if (!is_in_list || (!is_worked && af.asp < 0)) {
- if (ct_dir > 3 && slope[ct_dir] > 0) {
- if (slope[nbr_ew[ct_dir]] >= 0) {
- /* slope to ew nbr > slope to center */
- if (slope[ct_dir] <
- get_slope(ele_nbr[nbr_ew[ct_dir]],
- ele_nbr[ct_dir], ew_res))
- skip_diag = 1;
- }
- if (!skip_diag && slope[nbr_ns[ct_dir]] >= 0) {
- /* slope to ns nbr > slope to center */
- if (slope[ct_dir] <
- get_slope(ele_nbr[nbr_ns[ct_dir]],
- ele_nbr[ct_dir], ns_res))
- skip_diag = 1;
- }
- }
- }
- if (!skip_diag) {
- if (!is_in_list) {
- ele_up = ele_nbr[ct_dir];
- af.asp = drain[r_nbr - r + 1][c_nbr - c + 1];
- heap_add(r_nbr, c_nbr, ele_up);
- FLAG_SET(af.flag, INLISTFLAG);
- seg_put(&aspflag, (char *)&af, r_nbr, c_nbr);
- }
- else if (!is_worked) {
- if (FLAG_GET(af.flag, EDGEFLAG)) {
- /* neighbour is edge in list, not yet worked */
- if (af.asp < 0 && slope[ct_dir] > 0) {
- /* adjust flow direction for edge cell */
- af.asp = drain[r_nbr - r + 1][c_nbr - c + 1];
- seg_put(&aspflag, (char *)&af, r_nbr, c_nbr);
- }
- }
- else if (FLAG_GET(af.flag, DEPRFLAG)) {
- G_debug(3, "real depression");
- /* neighbour is inside real depression, not yet worked */
- if (af.asp == 0 && ele_val <= ele_nbr[ct_dir]) {
- af.asp = drain[r_nbr - r + 1][c_nbr - c + 1];
- FLAG_UNSET(af.flag, DEPRFLAG);
- seg_put(&aspflag, (char *)&af, r_nbr, c_nbr);
- }
- }
- }
- }
- } /* end neighbours */
- /* add astar points to sorted list for flow accumulation and stream extraction */
- first_cum--;
- seg_put(&astar_pts, (char *)&heap_p.pnt, 0, first_cum);
- seg_get(&aspflag, (char *)&af, r, c);
- FLAG_SET(af.flag, WORKEDFLAG);
- seg_put(&aspflag, (char *)&af, r, c);
- } /* end A* search */
- G_percent(n_points, n_points, 1); /* finish it */
- return 1;
- }
- /*
- * compare function for heap
- * returns 1 if point1 < point2 else 0
- */
- static int heap_cmp(HEAP_PNT *a, HEAP_PNT *b)
- {
- if (a->ele < b->ele)
- return 1;
- else if (a->ele == b->ele) {
- if (a->added < b->added)
- return 1;
- }
- return 0;
- }
- static int sift_up(GW_LARGE_INT start, HEAP_PNT child_p)
- {
- GW_LARGE_INT parent, child;
- HEAP_PNT heap_p;
- child = start;
- while (child > 1) {
- parent = GET_PARENT(child);
- seg_get(&search_heap, (char *)&heap_p, 0, parent);
- /* push parent point down if child is smaller */
- if (heap_cmp(&child_p, &heap_p)) {
- seg_put(&search_heap, (char *)&heap_p, 0, child);
- child = parent;
- }
- else
- /* no more sifting up, found slot for child */
- break;
- }
- /* add child to heap */
- seg_put(&search_heap, (char *)&child_p, 0, child);
- return 0;
- }
- /*
- * add item to heap
- * returns heap_size
- */
- GW_LARGE_INT heap_add(int r, int c, CELL ele)
- {
- HEAP_PNT heap_p;
-
- /* add point to next free position */
- heap_size++;
- heap_p.added = nxt_avail_pt;
- heap_p.ele = ele;
- heap_p.pnt.r = r;
- heap_p.pnt.c = c;
- nxt_avail_pt++;
- /* sift up: move new point towards top of heap */
- sift_up(heap_size, heap_p);
- return heap_size;
- }
- /*
- * drop item from heap
- * returns heap size
- */
- HEAP_PNT heap_drop(void)
- {
- GW_LARGE_INT child, childr, parent;
- GW_LARGE_INT i;
- HEAP_PNT child_p, childr_p, last_p, root_p;
- seg_get(&search_heap, (char *)&last_p, 0, heap_size);
- seg_get(&search_heap, (char *)&root_p, 0, 1);
- if (heap_size == 1) {
- heap_size = 0;
- return root_p;
- }
- parent = 1;
- while ((child = GET_CHILD(parent)) < heap_size) {
- seg_get(&search_heap, (char *)&child_p, 0, child);
- if (child < heap_size) {
- childr = child + 1;
- i = child + 8;
- while (childr < heap_size && childr < i) {
- seg_get(&search_heap, (char *)&childr_p, 0, childr);
- if (heap_cmp(&childr_p, &child_p)) {
- child = childr;
- child_p = childr_p;
- }
- childr++;
- }
- }
- if (heap_cmp(&last_p, &child_p)) {
- break;
- }
- /* move hole down */
- seg_put(&search_heap, (char *)&child_p, 0, parent);
- parent = child;
- }
- /* fill hole */
- if (parent < heap_size) {
- seg_put(&search_heap, (char *)&last_p, 0, parent);
- }
- /* the actual drop */
- heap_size--;
- return root_p;
- }
- static double get_slope(CELL ele, CELL up_ele, double dist)
- {
- if (ele >= up_ele)
- return 0.0;
- else
- return (double)(up_ele - ele) / dist;
- }
|