|
@@ -0,0 +1,713 @@
|
|
|
+#include <stdlib.h>
|
|
|
+#include <math.h>
|
|
|
+#include <grass/raster.h>
|
|
|
+#include <grass/glocale.h>
|
|
|
+#include "local_proto.h"
|
|
|
+
|
|
|
+double mfd_pow(double base)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ double result = base;
|
|
|
+
|
|
|
+ for (i = 2; i <= c_fac; i++) {
|
|
|
+ result *= base;
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+static int continue_stream(CELL stream_id, int r_max, int c_max,
|
|
|
+ int *stream_no)
|
|
|
+{
|
|
|
+ CELL curr_stream, stream_nbr, old_stream;
|
|
|
+ int r_nbr, c_nbr;
|
|
|
+ int asp_r[9] = { 0, -1, -1, -1, 0, 1, 1, 1, 0 };
|
|
|
+ int asp_c[9] = { 0, 1, 0, -1, -1, -1, 0, 1, 1 };
|
|
|
+ int stream_node_step = 1000;
|
|
|
+ ASP_FLAG af;
|
|
|
+
|
|
|
+ G_debug(3, "continue stream");
|
|
|
+
|
|
|
+ cseg_get(&stream, &curr_stream, r_max, c_max);
|
|
|
+
|
|
|
+ if (curr_stream <= 0) {
|
|
|
+ /* no confluence, just continue */
|
|
|
+ G_debug(3, "no confluence, just continue stream");
|
|
|
+ curr_stream = stream_id;
|
|
|
+ cseg_put(&stream, &curr_stream, r_max, c_max);
|
|
|
+ seg_get(&aspflag, (char *)&af, r_max, c_max);
|
|
|
+ FLAG_SET(af.flag, STREAMFLAG);
|
|
|
+ seg_put(&aspflag, (char *)&af, r_max, c_max);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ G_debug(3, "confluence");
|
|
|
+
|
|
|
+ /* new confluence */
|
|
|
+ if (stream_node[curr_stream].r != r_max ||
|
|
|
+ stream_node[curr_stream].c != c_max) {
|
|
|
+ size_t new_size;
|
|
|
+
|
|
|
+ G_debug(3, "new confluence");
|
|
|
+ /* set new stream id */
|
|
|
+ (*stream_no)++;
|
|
|
+ /* add stream node */
|
|
|
+ if (*stream_no >= n_alloc_nodes - 1) {
|
|
|
+ n_alloc_nodes += stream_node_step;
|
|
|
+ stream_node =
|
|
|
+ (struct snode *)G_realloc(stream_node,
|
|
|
+ n_alloc_nodes *
|
|
|
+ sizeof(struct snode));
|
|
|
+ }
|
|
|
+ stream_node[*stream_no].r = r_max;
|
|
|
+ stream_node[*stream_no].c = c_max;
|
|
|
+ stream_node[*stream_no].id = *stream_no;
|
|
|
+ stream_node[*stream_no].n_trib = 0;
|
|
|
+ stream_node[*stream_no].n_trib_total = 0;
|
|
|
+ stream_node[*stream_no].n_alloc = 0;
|
|
|
+ stream_node[*stream_no].trib = NULL;
|
|
|
+ n_stream_nodes++;
|
|
|
+
|
|
|
+ /* debug */
|
|
|
+ if (n_stream_nodes != *stream_no)
|
|
|
+ G_warning(_("BUG: stream_no %d and n_stream_nodes %lld out of sync"),
|
|
|
+ *stream_no, n_stream_nodes);
|
|
|
+
|
|
|
+ stream_node[*stream_no].n_alloc += 2;
|
|
|
+ new_size = stream_node[*stream_no].n_alloc * sizeof(int);
|
|
|
+ stream_node[*stream_no].trib =
|
|
|
+ (int *)G_realloc(stream_node[*stream_no].trib, new_size);
|
|
|
+
|
|
|
+ /* add the two tributaries */
|
|
|
+ G_debug(3, "add tributaries");
|
|
|
+ stream_node[*stream_no].trib[stream_node[*stream_no].n_trib++] =
|
|
|
+ curr_stream;
|
|
|
+ stream_node[*stream_no].trib[stream_node[*stream_no].n_trib++] =
|
|
|
+ stream_id;
|
|
|
+
|
|
|
+ /* update stream IDs downstream */
|
|
|
+ G_debug(3, "update stream IDs downstream");
|
|
|
+ r_nbr = r_max;
|
|
|
+ c_nbr = c_max;
|
|
|
+ old_stream = curr_stream;
|
|
|
+ curr_stream = *stream_no;
|
|
|
+ cseg_put(&stream, &curr_stream, r_nbr, c_nbr);
|
|
|
+ seg_get(&aspflag, (char *)&af, r_nbr, c_nbr);
|
|
|
+
|
|
|
+ while (af.asp > 0) {
|
|
|
+ r_nbr = r_nbr + asp_r[(int)af.asp];
|
|
|
+ c_nbr = c_nbr + asp_c[(int)af.asp];
|
|
|
+ cseg_get(&stream, &stream_nbr, r_nbr, c_nbr);
|
|
|
+ if (stream_nbr != old_stream)
|
|
|
+ af.asp = -1;
|
|
|
+ else {
|
|
|
+ cseg_put(&stream, &curr_stream, r_nbr, c_nbr);
|
|
|
+ seg_get(&aspflag, (char *)&af, r_nbr, c_nbr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ /* stream node already existing here */
|
|
|
+ G_debug(3, "existing confluence");
|
|
|
+ /* add new tributary to stream node */
|
|
|
+ if (stream_node[curr_stream].n_trib >=
|
|
|
+ stream_node[curr_stream].n_alloc) {
|
|
|
+ size_t new_size;
|
|
|
+
|
|
|
+ stream_node[curr_stream].n_alloc += 2;
|
|
|
+ new_size = stream_node[curr_stream].n_alloc * sizeof(int);
|
|
|
+ stream_node[curr_stream].trib =
|
|
|
+ (int *)G_realloc(stream_node[curr_stream].trib, new_size);
|
|
|
+ }
|
|
|
+
|
|
|
+ stream_node[curr_stream].trib[stream_node[curr_stream].n_trib++] =
|
|
|
+ stream_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ G_debug(3, "%d tribs", stream_node[curr_stream].n_trib);
|
|
|
+ if (stream_node[curr_stream].n_trib == 1)
|
|
|
+ G_warning(_("BUG: stream node %d has only 1 tributary: %d"), curr_stream,
|
|
|
+ stream_node[curr_stream].trib[0]);
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * accumulate surface flow
|
|
|
+ */
|
|
|
+int do_accum(double d8cut)
|
|
|
+{
|
|
|
+ int r, c, dr, dc;
|
|
|
+ CELL ele_val, *ele_nbr;
|
|
|
+ DCELL value, *wat_nbr;
|
|
|
+ struct Cell_head window;
|
|
|
+ int mfd_cells, astar_not_set;
|
|
|
+ double *dist_to_nbr, *weight, sum_weight, max_weight;
|
|
|
+ double dx, dy;
|
|
|
+ int r_nbr, c_nbr, ct_dir, np_side;
|
|
|
+ int is_worked;
|
|
|
+ double prop;
|
|
|
+ int edge;
|
|
|
+ int asp_r[9] = { 0, -1, -1, -1, 0, 1, 1, 1, 0 };
|
|
|
+ int asp_c[9] = { 0, 1, 0, -1, -1, -1, 0, 1, 1 };
|
|
|
+ int nextdr[8] = { 1, -1, 0, 0, -1, 1, 1, -1 };
|
|
|
+ int nextdc[8] = { 0, 0, -1, 1, 1, -1, 1, -1 };
|
|
|
+ GW_LARGE_INT workedon, killer;
|
|
|
+ char *flag_nbr;
|
|
|
+ POINT astarpoint;
|
|
|
+ WAT_ALT wa;
|
|
|
+ ASP_FLAG af, af_nbr;
|
|
|
+
|
|
|
+ G_message(_("Calculating flow accumulation..."));
|
|
|
+
|
|
|
+ /* distances to neighbours */
|
|
|
+ dist_to_nbr = (double *)G_malloc(sides * sizeof(double));
|
|
|
+ weight = (double *)G_malloc(sides * sizeof(double));
|
|
|
+ flag_nbr = (char *)G_malloc(sides * sizeof(char));
|
|
|
+ wat_nbr = (DCELL *)G_malloc(sides * sizeof(DCELL));
|
|
|
+ ele_nbr = (CELL *)G_malloc(sides * sizeof(CELL));
|
|
|
+
|
|
|
+ G_get_set_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);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* distribute and accumulate */
|
|
|
+ for (killer = 0; killer < n_points; killer++) {
|
|
|
+
|
|
|
+ G_percent(killer, n_points, 1);
|
|
|
+
|
|
|
+ seg_get(&astar_pts, (char *)&astarpoint, 0, killer);
|
|
|
+ r = astarpoint.r;
|
|
|
+ c = astarpoint.c;
|
|
|
+
|
|
|
+ seg_get(&aspflag, (char *)&af, r, c);
|
|
|
+
|
|
|
+ /* do not distribute flow along edges or out of real depressions */
|
|
|
+ if (af.asp <= 0) {
|
|
|
+ FLAG_UNSET(af.flag, WORKEDFLAG);
|
|
|
+ seg_put(&aspflag, (char *)&af, r, c);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (af.asp) {
|
|
|
+ dr = r + asp_r[abs((int)af.asp)];
|
|
|
+ dc = c + asp_c[abs((int)af.asp)];
|
|
|
+ }
|
|
|
+
|
|
|
+ seg_get(&watalt, (char *)&wa, r, c);
|
|
|
+ value = wa.wat;
|
|
|
+
|
|
|
+ /* WORKEDFLAG has been set during A* Search
|
|
|
+ * reversed meaning here: 0 = done, 1 = not yet done */
|
|
|
+ FLAG_UNSET(af.flag, WORKEDFLAG);
|
|
|
+ seg_put(&aspflag, (char *)&af, r, c);
|
|
|
+
|
|
|
+ /***************************************/
|
|
|
+ /* get weights for flow distribution */
|
|
|
+ /***************************************/
|
|
|
+
|
|
|
+ max_weight = 0;
|
|
|
+ sum_weight = 0;
|
|
|
+ np_side = -1;
|
|
|
+ mfd_cells = 0;
|
|
|
+ astar_not_set = 1;
|
|
|
+ ele_val = wa.ele;
|
|
|
+ edge = 0;
|
|
|
+ /* this loop is needed to get the sum of weights */
|
|
|
+ for (ct_dir = 0; ct_dir < sides; ct_dir++) {
|
|
|
+ /* get r_nbr, c_nbr for neighbours */
|
|
|
+ r_nbr = r + nextdr[ct_dir];
|
|
|
+ c_nbr = c + nextdc[ct_dir];
|
|
|
+ weight[ct_dir] = -1;
|
|
|
+ wat_nbr[ct_dir] = 0;
|
|
|
+ ele_nbr[ct_dir] = 0;
|
|
|
+
|
|
|
+ /* check that neighbour is within region */
|
|
|
+ if (r_nbr >= 0 && r_nbr < nrows && c_nbr >= 0 && c_nbr < ncols) {
|
|
|
+
|
|
|
+ seg_get(&aspflag, (char *)&af_nbr, r_nbr, c_nbr);
|
|
|
+ flag_nbr[ct_dir] = af_nbr.flag;
|
|
|
+ if ((edge = FLAG_GET(flag_nbr[ct_dir], NULLFLAG)))
|
|
|
+ break;
|
|
|
+ seg_get(&watalt, (char *)&wa, r_nbr, c_nbr);
|
|
|
+ wat_nbr[ct_dir] = wa.wat;
|
|
|
+ ele_nbr[ct_dir] = wa.ele;
|
|
|
+
|
|
|
+ /* WORKEDFLAG has been set during A* Search
|
|
|
+ * reversed meaning here: 0 = done, 1 = not yet done */
|
|
|
+ is_worked = FLAG_GET(flag_nbr[ct_dir], WORKEDFLAG) == 0;
|
|
|
+ if (is_worked == 0) {
|
|
|
+ if (ele_nbr[ct_dir] <= ele_val) {
|
|
|
+ if (ele_nbr[ct_dir] < ele_val) {
|
|
|
+ weight[ct_dir] =
|
|
|
+ mfd_pow((ele_val -
|
|
|
+ ele_nbr[ct_dir]) / dist_to_nbr[ct_dir]);
|
|
|
+ }
|
|
|
+ if (ele_nbr[ct_dir] == ele_val) {
|
|
|
+ weight[ct_dir] =
|
|
|
+ mfd_pow(0.5 / dist_to_nbr[ct_dir]);
|
|
|
+ }
|
|
|
+ sum_weight += weight[ct_dir];
|
|
|
+ mfd_cells++;
|
|
|
+
|
|
|
+ if (weight[ct_dir] > max_weight) {
|
|
|
+ max_weight = weight[ct_dir];
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dr == r_nbr && dc == c_nbr) {
|
|
|
+ astar_not_set = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (dr == r_nbr && dc == c_nbr)
|
|
|
+ np_side = ct_dir;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ edge = 1;
|
|
|
+ if (edge)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* do not distribute flow along edges, this causes artifacts */
|
|
|
+ if (edge) {
|
|
|
+ G_debug(3, "edge");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* honour A * path
|
|
|
+ * mfd_cells == 0: fine, SFD along A * path
|
|
|
+ * mfd_cells == 1 && astar_not_set == 0: fine, SFD along A * path
|
|
|
+ * mfd_cells > 0 && astar_not_set == 1: A * path not included, add to mfd_cells
|
|
|
+ */
|
|
|
+
|
|
|
+ /************************************/
|
|
|
+ /* distribute and accumulate flow */
|
|
|
+ /************************************/
|
|
|
+
|
|
|
+ /* MFD, A * path not included, add to mfd_cells */
|
|
|
+ if (mfd_cells > 0 && astar_not_set == 1) {
|
|
|
+ mfd_cells++;
|
|
|
+ sum_weight += max_weight;
|
|
|
+ weight[np_side] = max_weight;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* use SFD (D8) if d8cut threshold exceeded */
|
|
|
+ if (fabs(value) > d8cut)
|
|
|
+ mfd_cells = 0;
|
|
|
+
|
|
|
+ if (mfd_cells > 1) {
|
|
|
+ prop = 0.0;
|
|
|
+ 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];
|
|
|
+
|
|
|
+ /* check that neighbour is within region */
|
|
|
+ if (r_nbr >= 0 && r_nbr < nrows && c_nbr >= 0 &&
|
|
|
+ c_nbr < ncols && weight[ct_dir] > -0.5) {
|
|
|
+ is_worked = FLAG_GET(flag_nbr[ct_dir], WORKEDFLAG) == 0;
|
|
|
+ if (is_worked == 0) {
|
|
|
+
|
|
|
+ weight[ct_dir] = weight[ct_dir] / sum_weight;
|
|
|
+ /* check everything sums up to 1.0 */
|
|
|
+ prop += weight[ct_dir];
|
|
|
+
|
|
|
+ wa.wat = wat_nbr[ct_dir] + value * weight[ct_dir];
|
|
|
+ wa.ele = ele_nbr[ct_dir];
|
|
|
+ seg_put(&watalt, (char *)&wa, r_nbr, c_nbr);
|
|
|
+ }
|
|
|
+ else if (ct_dir == np_side) {
|
|
|
+ /* check for consistency with A * path */
|
|
|
+ workedon++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (fabs(prop - 1.0) > 5E-6f) {
|
|
|
+ G_warning(_("MFD: cumulative proportion of flow distribution not 1.0 but %f"),
|
|
|
+ prop);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /* get out of depression in SFD mode */
|
|
|
+ else {
|
|
|
+ wa.wat = wat_nbr[np_side] + value;
|
|
|
+ wa.ele = ele_nbr[np_side];
|
|
|
+ seg_put(&watalt, (char *)&wa, dr, dc);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ G_percent(1, 1, 2);
|
|
|
+
|
|
|
+ G_free(dist_to_nbr);
|
|
|
+ G_free(weight);
|
|
|
+ G_free(wat_nbr);
|
|
|
+ G_free(ele_nbr);
|
|
|
+ G_free(flag_nbr);
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * extracts streams for threshold, accumulation is provided
|
|
|
+ */
|
|
|
+int extract_streams(double threshold, double mont_exp, int internal_acc)
|
|
|
+{
|
|
|
+ int r, c, dr, dc;
|
|
|
+ CELL is_swale, ele_val, *ele_nbr;
|
|
|
+ DCELL value, valued, *wat_nbr;
|
|
|
+ struct Cell_head window;
|
|
|
+ int mfd_cells, stream_cells, swale_cells;
|
|
|
+ double *dist_to_nbr;
|
|
|
+ double dx, dy;
|
|
|
+ int r_nbr, c_nbr, r_max, c_max, ct_dir, np_side, max_side;
|
|
|
+ int is_worked;
|
|
|
+ double max_acc;
|
|
|
+ int edge, flat;
|
|
|
+ int asp_r[9] = { 0, -1, -1, -1, 0, 1, 1, 1, 0 };
|
|
|
+ int asp_c[9] = { 0, 1, 0, -1, -1, -1, 0, 1, 1 };
|
|
|
+ int nextdr[8] = { 1, -1, 0, 0, -1, 1, 1, -1 };
|
|
|
+ int nextdc[8] = { 0, 0, -1, 1, 1, -1, 1, -1 };
|
|
|
+ /* sides */
|
|
|
+ /*
|
|
|
+ * | 7 | 1 | 4 |
|
|
|
+ * | 2 | | 3 |
|
|
|
+ * | 5 | 0 | 6 |
|
|
|
+ */
|
|
|
+ GW_LARGE_INT workedon, killer;
|
|
|
+ int stream_no = 0, stream_node_step = 1000;
|
|
|
+ double slope, diag;
|
|
|
+ char *flag_nbr;
|
|
|
+ POINT astarpoint;
|
|
|
+ WAT_ALT wa;
|
|
|
+ ASP_FLAG af, af_nbr;
|
|
|
+
|
|
|
+ G_message(_("Extracting streams..."));
|
|
|
+
|
|
|
+ /* init stream nodes */
|
|
|
+ n_alloc_nodes = stream_node_step;
|
|
|
+ stream_node =
|
|
|
+ (struct snode *)G_malloc(n_alloc_nodes * sizeof(struct snode));
|
|
|
+ n_stream_nodes = 0;
|
|
|
+
|
|
|
+ /* init outlet nodes */
|
|
|
+ n_alloc_outlets = stream_node_step;
|
|
|
+ outlets =
|
|
|
+ (POINT *)G_malloc(n_alloc_outlets * sizeof(POINT));
|
|
|
+ n_outlets = 0;
|
|
|
+
|
|
|
+ /* distances to neighbours */
|
|
|
+ dist_to_nbr = (double *)G_malloc(sides * sizeof(double));
|
|
|
+ flag_nbr = (char *)G_malloc(sides * sizeof(char));
|
|
|
+ wat_nbr = (DCELL *)G_malloc(sides * sizeof(DCELL));
|
|
|
+ ele_nbr = (CELL *)G_malloc(sides * sizeof(CELL));
|
|
|
+
|
|
|
+ G_get_set_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);
|
|
|
+ }
|
|
|
+
|
|
|
+ diag = sqrt(2);
|
|
|
+
|
|
|
+ workedon = 0;
|
|
|
+
|
|
|
+ /* extract streams */
|
|
|
+ for (killer = 0; killer < n_points; killer++) {
|
|
|
+ G_percent(killer, n_points, 1);
|
|
|
+
|
|
|
+ seg_get(&astar_pts, (char *)&astarpoint, 0, killer);
|
|
|
+ r = astarpoint.r;
|
|
|
+ c = astarpoint.c;
|
|
|
+
|
|
|
+ seg_get(&aspflag, (char *)&af, r, c);
|
|
|
+ /* internal acc: SET, external acc: UNSET */
|
|
|
+ if (internal_acc)
|
|
|
+ FLAG_SET(af.flag, WORKEDFLAG);
|
|
|
+ else
|
|
|
+ FLAG_UNSET(af.flag, WORKEDFLAG);
|
|
|
+ seg_put(&aspflag, (char *)&af, r, c);
|
|
|
+
|
|
|
+ /* do not distribute flow along edges */
|
|
|
+ if (af.asp <= 0) {
|
|
|
+ G_debug(3, "edge");
|
|
|
+ is_swale = FLAG_GET(af.flag, STREAMFLAG);
|
|
|
+ if (is_swale) {
|
|
|
+ G_debug(2, "edge outlet");
|
|
|
+ /* add outlet point */
|
|
|
+ if (n_outlets >= n_alloc_outlets) {
|
|
|
+ n_alloc_outlets += stream_node_step;
|
|
|
+ outlets =
|
|
|
+ (POINT *)G_realloc(outlets,
|
|
|
+ n_alloc_outlets *
|
|
|
+ sizeof(POINT));
|
|
|
+ }
|
|
|
+ outlets[n_outlets].r = r;
|
|
|
+ outlets[n_outlets].c = c;
|
|
|
+ n_outlets++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (af.asp == 0) {
|
|
|
+ /* can only happen with real depressions */
|
|
|
+ if (!have_depressions)
|
|
|
+ G_fatal_error(_("Bug in stream extraction"));
|
|
|
+ G_debug(2, "bottom of real depression");
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (af.asp) {
|
|
|
+ dr = r + asp_r[abs((int)af.asp)];
|
|
|
+ dc = c + asp_c[abs((int)af.asp)];
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ /* can only happen with real depressions,
|
|
|
+ * but should not get to here */
|
|
|
+ dr = r;
|
|
|
+ dc = c;
|
|
|
+ }
|
|
|
+
|
|
|
+ r_nbr = r_max = dr;
|
|
|
+ c_nbr = c_max = dc;
|
|
|
+
|
|
|
+ seg_get(&watalt, (char *)&wa, r, c);
|
|
|
+ value = wa.wat;
|
|
|
+
|
|
|
+ /**********************************/
|
|
|
+ /* find main drainage direction */
|
|
|
+ /**********************************/
|
|
|
+
|
|
|
+ max_acc = -1;
|
|
|
+ max_side = np_side = -1;
|
|
|
+ mfd_cells = 0;
|
|
|
+ stream_cells = 0;
|
|
|
+ swale_cells = 0;
|
|
|
+ ele_val = wa.ele;
|
|
|
+ edge = 0;
|
|
|
+ flat = 1;
|
|
|
+ /* find main drainage direction */
|
|
|
+ for (ct_dir = 0; ct_dir < sides; ct_dir++) {
|
|
|
+ /* get r_nbr, c_nbr for neighbours */
|
|
|
+ r_nbr = r + nextdr[ct_dir];
|
|
|
+ c_nbr = c + nextdc[ct_dir];
|
|
|
+ wat_nbr[ct_dir] = 0;
|
|
|
+ ele_nbr[ct_dir] = 0;
|
|
|
+ flag_nbr[ct_dir] = 0;
|
|
|
+
|
|
|
+ /* check that neighbour is within region */
|
|
|
+ if (r_nbr >= 0 && r_nbr < nrows && c_nbr >= 0 && c_nbr < ncols) {
|
|
|
+
|
|
|
+ if (dr == r_nbr && dc == c_nbr)
|
|
|
+ np_side = ct_dir;
|
|
|
+
|
|
|
+ seg_get(&aspflag, (char *)&af_nbr, r_nbr, c_nbr);
|
|
|
+ flag_nbr[ct_dir] = af_nbr.flag;
|
|
|
+ if ((edge = FLAG_GET(flag_nbr[ct_dir], NULLFLAG)))
|
|
|
+ break;
|
|
|
+ seg_get(&watalt, (char *)&wa, r_nbr, c_nbr);
|
|
|
+ wat_nbr[ct_dir] = wa.wat;
|
|
|
+ ele_nbr[ct_dir] = wa.ele;
|
|
|
+
|
|
|
+ /* check for swale cells */
|
|
|
+ is_swale = FLAG_GET(flag_nbr[ct_dir], STREAMFLAG);
|
|
|
+ if (is_swale)
|
|
|
+ swale_cells++;
|
|
|
+
|
|
|
+ /* check for stream cells */
|
|
|
+ valued = fabs(wat_nbr[ct_dir]);
|
|
|
+ /* check all upstream neighbours */
|
|
|
+ if (valued >= threshold && ct_dir != np_side &&
|
|
|
+ ele_nbr[ct_dir] > ele_val)
|
|
|
+ stream_cells++;
|
|
|
+
|
|
|
+ is_worked = FLAG_GET(flag_nbr[ct_dir], WORKEDFLAG);
|
|
|
+ if (!internal_acc)
|
|
|
+ is_worked = is_worked == 0;
|
|
|
+
|
|
|
+ if (is_worked == 0) {
|
|
|
+ if (ele_nbr[ct_dir] != ele_val)
|
|
|
+ flat = 0;
|
|
|
+ if (ele_nbr[ct_dir] <= ele_val) {
|
|
|
+
|
|
|
+ mfd_cells++;
|
|
|
+
|
|
|
+ /* set main drainage direction */
|
|
|
+ if (valued >= max_acc) {
|
|
|
+ max_acc = valued;
|
|
|
+ r_max = r_nbr;
|
|
|
+ c_max = c_nbr;
|
|
|
+ max_side = ct_dir;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (ct_dir == np_side && !edge) {
|
|
|
+ /* check for consistency with A * path */
|
|
|
+ workedon++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ edge = 1;
|
|
|
+ if (edge)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ is_swale = FLAG_GET(af.flag, STREAMFLAG);
|
|
|
+
|
|
|
+ /* do not continue streams along edges, these are artifacts */
|
|
|
+ if (edge) {
|
|
|
+ G_debug(3, "edge");
|
|
|
+ if (is_swale) {
|
|
|
+ G_debug(2, "edge outlet");
|
|
|
+ /* add outlet point */
|
|
|
+ if (n_outlets >= n_alloc_outlets) {
|
|
|
+ n_alloc_outlets += stream_node_step;
|
|
|
+ outlets =
|
|
|
+ (POINT *)G_realloc(outlets,
|
|
|
+ n_alloc_outlets *
|
|
|
+ sizeof(POINT));
|
|
|
+ }
|
|
|
+ outlets[n_outlets].r = r;
|
|
|
+ outlets[n_outlets].c = c;
|
|
|
+ n_outlets++;
|
|
|
+ if (af.asp > 0) {
|
|
|
+ af.asp = -1 * drain[r - r_nbr + 1][c - c_nbr + 1];
|
|
|
+ seg_put(&aspflag, (char *)&af, r, c);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (np_side < 0)
|
|
|
+ G_fatal_error("np_side < 0");
|
|
|
+
|
|
|
+ /* set main drainage direction to A* path if possible */
|
|
|
+ if (mfd_cells > 0 && max_side != np_side) {
|
|
|
+ if (fabs(wat_nbr[np_side] >= max_acc)) {
|
|
|
+ max_acc = fabs(wat_nbr[np_side]);
|
|
|
+ r_max = dr;
|
|
|
+ c_max = dc;
|
|
|
+ max_side = np_side;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (mfd_cells == 0) {
|
|
|
+ flat = 0;
|
|
|
+ r_max = dr;
|
|
|
+ c_max = dc;
|
|
|
+ max_side = np_side;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* update aspect */
|
|
|
+ /* r_max == r && c_max == c should not happen */
|
|
|
+ if ((r_max != dr || c_max != dc) && (r_max != r || c_max != c)) {
|
|
|
+ af.asp = drain[r - r_max + 1][c - c_max + 1];
|
|
|
+ seg_put(&aspflag, (char *)&af, r, c);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**********************/
|
|
|
+ /* start new stream */
|
|
|
+ /**********************/
|
|
|
+
|
|
|
+ /* Montgomery's stream initiation acc * (tan(slope))^mont_exp */
|
|
|
+ /* uses whatever unit is accumulation */
|
|
|
+ if (mont_exp > 0) {
|
|
|
+ if (r_max == r && c_max == c)
|
|
|
+ G_warning
|
|
|
+ (_("Can't use Montgomery's method, no stream direction found"));
|
|
|
+ else {
|
|
|
+ slope = (double)(ele_val - ele_nbr[max_side]) / ele_scale;
|
|
|
+
|
|
|
+ if (max_side > 3)
|
|
|
+ slope /= diag;
|
|
|
+
|
|
|
+ value *= pow(fabs(slope), mont_exp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!is_swale && fabs(value) >= threshold && stream_cells < 1 &&
|
|
|
+ swale_cells < 1 && !flat) {
|
|
|
+ G_debug(2, "start new stream");
|
|
|
+ is_swale = ++stream_no;
|
|
|
+ cseg_put(&stream, &is_swale, r, c);
|
|
|
+ FLAG_SET(af.flag, STREAMFLAG);
|
|
|
+ seg_put(&aspflag, (char *)&af, r, c);
|
|
|
+ /* add stream node */
|
|
|
+ if (stream_no >= n_alloc_nodes - 1) {
|
|
|
+ n_alloc_nodes += stream_node_step;
|
|
|
+ stream_node =
|
|
|
+ (struct snode *)G_realloc(stream_node,
|
|
|
+ n_alloc_nodes *
|
|
|
+ sizeof(struct snode));
|
|
|
+ }
|
|
|
+ stream_node[stream_no].r = r;
|
|
|
+ stream_node[stream_no].c = c;
|
|
|
+ stream_node[stream_no].id = stream_no;
|
|
|
+ stream_node[stream_no].n_trib = 0;
|
|
|
+ stream_node[stream_no].n_trib_total = 0;
|
|
|
+ stream_node[stream_no].n_alloc = 0;
|
|
|
+ stream_node[stream_no].trib = NULL;
|
|
|
+ n_stream_nodes++;
|
|
|
+
|
|
|
+ /* debug */
|
|
|
+ if (n_stream_nodes != stream_no)
|
|
|
+ G_warning(_("BUG: stream_no %d and n_stream_nodes %lld out of sync"),
|
|
|
+ stream_no, n_stream_nodes);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*********************/
|
|
|
+ /* continue stream */
|
|
|
+ /*********************/
|
|
|
+
|
|
|
+ if (is_swale > 0) {
|
|
|
+ cseg_get(&stream, &is_swale, r, c);
|
|
|
+ if (r_max == r && c_max == c) {
|
|
|
+ /* can't continue stream, add outlet point
|
|
|
+ * r_max == r && c_max == c should not happen */
|
|
|
+ G_debug(1, "can't continue stream at r %d c %d", r, c);
|
|
|
+
|
|
|
+ if (n_outlets >= n_alloc_outlets) {
|
|
|
+ n_alloc_outlets += stream_node_step;
|
|
|
+ outlets =
|
|
|
+ (POINT *)G_malloc(n_alloc_outlets *
|
|
|
+ sizeof(POINT));
|
|
|
+ }
|
|
|
+ outlets[n_outlets].r = r;
|
|
|
+ outlets[n_outlets].c = c;
|
|
|
+ n_outlets++;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ continue_stream(is_swale, r_max, c_max, &stream_no);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ G_percent(1, 1, 2);
|
|
|
+ if (workedon)
|
|
|
+ G_warning(_("MFD: A * path already processed when setting drainage direction: %lld of %lld cells"),
|
|
|
+ workedon, n_points);
|
|
|
+
|
|
|
+ G_free(dist_to_nbr);
|
|
|
+ G_free(wat_nbr);
|
|
|
+ G_free(ele_nbr);
|
|
|
+ G_free(flag_nbr);
|
|
|
+
|
|
|
+ G_debug(1, "%lld outlets", n_outlets);
|
|
|
+ G_debug(1, "%lld nodes", n_stream_nodes);
|
|
|
+ G_debug(1, "%d streams", stream_no);
|
|
|
+
|
|
|
+ return 1;
|
|
|
+}
|