| 1 |
|
|---|
| 2 | /****************************************************************************
|
|---|
| 3 | *
|
|---|
| 4 | * MODULE: r.stream.extract
|
|---|
| 5 | * AUTHOR(S): Markus Metz <markus.metz.giswork gmail.com>
|
|---|
| 6 | * PURPOSE: Hydrological analysis
|
|---|
| 7 | * Extracts stream networks from accumulation raster with
|
|---|
| 8 | * given threshold
|
|---|
| 9 | * COPYRIGHT: (C) 1999-2014 by the GRASS Development Team
|
|---|
| 10 | *
|
|---|
| 11 | * This program is free software under the GNU General Public
|
|---|
| 12 | * License (>=v2). Read the file COPYING that comes with GRASS
|
|---|
| 13 | * for details.
|
|---|
| 14 | *
|
|---|
| 15 | *****************************************************************************/
|
|---|
| 16 | #include <stdlib.h>
|
|---|
| 17 | #include <string.h>
|
|---|
| 18 | #include <float.h>
|
|---|
| 19 | #include <math.h>
|
|---|
| 20 | #include <grass/raster.h>
|
|---|
| 21 | #include <grass/glocale.h>
|
|---|
| 22 | #include "local_proto.h"
|
|---|
| 23 |
|
|---|
| 24 | /* global variables */
|
|---|
| 25 | int nrows, ncols;
|
|---|
| 26 | GW_LARGE_INT n_search_points, n_points, nxt_avail_pt;
|
|---|
| 27 | GW_LARGE_INT heap_size;
|
|---|
| 28 | GW_LARGE_INT n_stream_nodes, n_alloc_nodes;
|
|---|
| 29 | POINT *outlets;
|
|---|
| 30 | struct snode *stream_node;
|
|---|
| 31 | GW_LARGE_INT n_outlets, n_alloc_outlets;
|
|---|
| 32 | char drain[3][3] = { {7, 6, 5}, {8, 0, 4}, {1, 2, 3} };
|
|---|
| 33 | char sides;
|
|---|
| 34 | int c_fac;
|
|---|
| 35 | int ele_scale;
|
|---|
| 36 | int have_depressions;
|
|---|
| 37 |
|
|---|
| 38 | SSEG search_heap;
|
|---|
| 39 | SSEG astar_pts;
|
|---|
| 40 | SSEG watalt, aspflag;
|
|---|
| 41 | /* BSEG bitflags, asp; */
|
|---|
| 42 | CSEG stream;
|
|---|
| 43 |
|
|---|
| 44 | CELL *astar_order;
|
|---|
| 45 |
|
|---|
| 46 | int main(int argc, char *argv[])
|
|---|
| 47 | {
|
|---|
| 48 | struct
|
|---|
| 49 | {
|
|---|
| 50 | struct Option *ele, *acc, *depression;
|
|---|
| 51 | struct Option *threshold, *d8cut;
|
|---|
| 52 | struct Option *mont_exp;
|
|---|
| 53 | struct Option *min_stream_length;
|
|---|
| 54 | struct Option *memory;
|
|---|
| 55 | } input;
|
|---|
| 56 | struct
|
|---|
| 57 | {
|
|---|
| 58 | struct Option *stream_rast;
|
|---|
| 59 | struct Option *stream_vect;
|
|---|
| 60 | struct Option *dir_rast;
|
|---|
| 61 | } output;
|
|---|
| 62 | struct GModule *module;
|
|---|
| 63 | int ele_fd, acc_fd, depr_fd;
|
|---|
| 64 | double threshold, d8cut, mont_exp;
|
|---|
| 65 | int min_stream_length = 0, memory;
|
|---|
| 66 | int seg_cols, seg_rows;
|
|---|
| 67 | double seg2kb;
|
|---|
| 68 | int num_open_segs, num_open_array_segs, num_seg_total;
|
|---|
| 69 | double memory_divisor, heap_mem, disk_space;
|
|---|
| 70 | const char *mapset;
|
|---|
| 71 |
|
|---|
| 72 | G_gisinit(argv[0]);
|
|---|
| 73 |
|
|---|
| 74 | /* Set description */
|
|---|
| 75 | module = G_define_module();
|
|---|
| 76 | G_add_keyword(_("raster"));
|
|---|
| 77 | G_add_keyword(_("hydrology"));
|
|---|
| 78 | G_add_keyword(_("stream network"));
|
|---|
| 79 | module->description = _("Performs stream network extraction.");
|
|---|
| 80 |
|
|---|
| 81 | input.ele = G_define_standard_option(G_OPT_R_ELEV);
|
|---|
| 82 |
|
|---|
| 83 | input.acc = G_define_standard_option(G_OPT_R_INPUT);
|
|---|
| 84 | input.acc->key = "accumulation";
|
|---|
| 85 | input.acc->label = _("Name of input accumulation raster map");
|
|---|
| 86 | input.acc->required = NO;
|
|---|
| 87 | input.acc->description =
|
|---|
| 88 | _("Stream extraction will use provided accumulation instead of calculating it anew");
|
|---|
| 89 | input.acc->guisection = _("Input maps");
|
|---|
| 90 |
|
|---|
| 91 | input.depression = G_define_standard_option(G_OPT_R_INPUT);
|
|---|
| 92 | input.depression->key = "depression";
|
|---|
| 93 | input.depression->label = _("Name of input raster map with real depressions");
|
|---|
| 94 | input.depression->required = NO;
|
|---|
| 95 | input.depression->description =
|
|---|
| 96 | _("Streams will not be routed out of real depressions");
|
|---|
| 97 | input.depression->guisection = _("Input maps");
|
|---|
| 98 |
|
|---|
| 99 | input.threshold = G_define_option();
|
|---|
| 100 | input.threshold->key = "threshold";
|
|---|
| 101 | input.threshold->label = _("Minimum flow accumulation for streams");
|
|---|
| 102 | input.threshold->description = _("Must be > 0");
|
|---|
| 103 | input.threshold->required = YES;
|
|---|
| 104 | input.threshold->type = TYPE_DOUBLE;
|
|---|
| 105 |
|
|---|
| 106 | input.d8cut = G_define_option();
|
|---|
| 107 | input.d8cut->key = "d8cut";
|
|---|
| 108 | input.d8cut->label = _("Use SFD above this threshold");
|
|---|
| 109 | input.d8cut->description =
|
|---|
| 110 | _("If accumulation is larger than d8cut, SFD is used instead of MFD."
|
|---|
| 111 | " Applies only if no accumulation map is given.");
|
|---|
| 112 | input.d8cut->required = NO;
|
|---|
| 113 | input.d8cut->answer = "infinity";
|
|---|
| 114 | input.d8cut->type = TYPE_DOUBLE;
|
|---|
| 115 |
|
|---|
| 116 | input.mont_exp = G_define_option();
|
|---|
| 117 | input.mont_exp->key = "mexp";
|
|---|
| 118 | input.mont_exp->type = TYPE_DOUBLE;
|
|---|
| 119 | input.mont_exp->required = NO;
|
|---|
| 120 | input.mont_exp->answer = "0";
|
|---|
| 121 | input.mont_exp->label =
|
|---|
| 122 | _("Montgomery exponent for slope, disabled with 0");
|
|---|
| 123 | input.mont_exp->description =
|
|---|
| 124 | _("Montgomery: accumulation is multiplied with pow(slope,mexp) and then compared with threshold");
|
|---|
| 125 |
|
|---|
| 126 | input.min_stream_length = G_define_option();
|
|---|
| 127 | input.min_stream_length->key = "stream_length";
|
|---|
| 128 | input.min_stream_length->type = TYPE_INTEGER;
|
|---|
| 129 | input.min_stream_length->required = NO;
|
|---|
| 130 | input.min_stream_length->answer = "0";
|
|---|
| 131 | input.min_stream_length->label =
|
|---|
| 132 | _("Delete stream segments shorter than stream_length cells");
|
|---|
| 133 | input.min_stream_length->description =
|
|---|
| 134 | _("Applies only to first-order stream segments (springs/stream heads)");
|
|---|
| 135 |
|
|---|
| 136 | input.memory = G_define_option();
|
|---|
| 137 | input.memory->key = "memory";
|
|---|
| 138 | input.memory->type = TYPE_INTEGER;
|
|---|
| 139 | input.memory->required = NO;
|
|---|
| 140 | input.memory->answer = "300";
|
|---|
| 141 | input.memory->label = _("Maximum memory to be used (in MB)");
|
|---|
| 142 | input.memory->description = _("Cache size for raster rows");
|
|---|
| 143 |
|
|---|
| 144 | output.stream_rast = G_define_standard_option(G_OPT_R_OUTPUT);
|
|---|
| 145 | output.stream_rast->key = "stream_raster";
|
|---|
| 146 | output.stream_rast->description =
|
|---|
| 147 | _("Name for output raster map with unique stream ids");
|
|---|
| 148 | output.stream_rast->required = NO;
|
|---|
| 149 | output.stream_rast->guisection = _("Output maps");
|
|---|
| 150 |
|
|---|
| 151 | output.stream_vect = G_define_standard_option(G_OPT_V_OUTPUT);
|
|---|
| 152 | output.stream_vect->key = "stream_vector";
|
|---|
| 153 | output.stream_vect->description =
|
|---|
| 154 | _("Name for output vector map with unique stream ids");
|
|---|
| 155 | output.stream_vect->required = NO;
|
|---|
| 156 | output.stream_vect->guisection = _("Output maps");
|
|---|
| 157 |
|
|---|
| 158 | output.dir_rast = G_define_standard_option(G_OPT_R_OUTPUT);
|
|---|
| 159 | output.dir_rast->key = "direction";
|
|---|
| 160 | output.dir_rast->description =
|
|---|
| 161 | _("Name for output raster map with flow direction");
|
|---|
| 162 | output.dir_rast->required = NO;
|
|---|
| 163 | output.dir_rast->guisection = _("Output maps");
|
|---|
| 164 |
|
|---|
| 165 | if (G_parser(argc, argv))
|
|---|
| 166 | exit(EXIT_FAILURE);
|
|---|
| 167 |
|
|---|
| 168 | /***********************/
|
|---|
| 169 | /* check options */
|
|---|
| 170 | /***********************/
|
|---|
| 171 |
|
|---|
| 172 | /* input maps exist ? */
|
|---|
| 173 | if (!G_find_raster(input.ele->answer, ""))
|
|---|
| 174 | G_fatal_error(_("Raster map <%s> not found"), input.ele->answer);
|
|---|
| 175 |
|
|---|
| 176 | if (input.acc->answer) {
|
|---|
| 177 | if (!G_find_raster(input.acc->answer, ""))
|
|---|
| 178 | G_fatal_error(_("Raster map <%s> not found"), input.acc->answer);
|
|---|
| 179 | }
|
|---|
| 180 |
|
|---|
| 181 | if (input.depression->answer) {
|
|---|
| 182 | if (!G_find_raster(input.depression->answer, ""))
|
|---|
| 183 | G_fatal_error(_("Raster map <%s> not found"), input.depression->answer);
|
|---|
| 184 | have_depressions = 1;
|
|---|
| 185 | }
|
|---|
| 186 | else
|
|---|
| 187 | have_depressions = 0;
|
|---|
| 188 |
|
|---|
| 189 | /* threshold makes sense */
|
|---|
| 190 | threshold = atof(input.threshold->answer);
|
|---|
| 191 | if (threshold <= 0)
|
|---|
| 192 | G_fatal_error(_("Threshold must be > 0 but is %f"), threshold);
|
|---|
| 193 |
|
|---|
| 194 | /* d8cut */
|
|---|
| 195 | if (strcmp(input.d8cut->answer, "infinity") == 0) {
|
|---|
| 196 | d8cut = DBL_MAX;
|
|---|
| 197 | }
|
|---|
| 198 | else {
|
|---|
| 199 | d8cut = atof(input.d8cut->answer);
|
|---|
| 200 | if (d8cut < 0)
|
|---|
| 201 | G_fatal_error(_("d8cut must be positive or zero but is %f"),
|
|---|
| 202 | d8cut);
|
|---|
| 203 | }
|
|---|
| 204 |
|
|---|
| 205 | /* Montgomery stream initiation */
|
|---|
| 206 | if (input.mont_exp->answer) {
|
|---|
| 207 | mont_exp = atof(input.mont_exp->answer);
|
|---|
| 208 | if (mont_exp < 0)
|
|---|
| 209 | G_fatal_error(_("Montgomery exponent must be positive or zero but is %f"),
|
|---|
| 210 | mont_exp);
|
|---|
| 211 | if (mont_exp > 3)
|
|---|
| 212 | G_warning(_("Montgomery exponent is %f, recommended range is 0.0 - 3.0"),
|
|---|
| 213 | mont_exp);
|
|---|
| 214 | }
|
|---|
| 215 | else
|
|---|
| 216 | mont_exp = 0;
|
|---|
| 217 |
|
|---|
| 218 | /* Minimum stream segment length */
|
|---|
| 219 | if (input.min_stream_length->answer) {
|
|---|
| 220 | min_stream_length = atoi(input.min_stream_length->answer);
|
|---|
| 221 | if (min_stream_length < 0)
|
|---|
| 222 | G_fatal_error(_("Minimum stream length must be positive or zero but is %d"),
|
|---|
| 223 | min_stream_length);
|
|---|
| 224 | }
|
|---|
| 225 | else
|
|---|
| 226 | min_stream_length = 0;
|
|---|
| 227 |
|
|---|
| 228 | if (input.memory->answer) {
|
|---|
| 229 | memory = atoi(input.memory->answer);
|
|---|
| 230 | if (memory <= 0)
|
|---|
| 231 | G_fatal_error(_("Memory must be positive but is %d"),
|
|---|
| 232 | memory);
|
|---|
| 233 | }
|
|---|
| 234 | else
|
|---|
| 235 | memory = 300;
|
|---|
| 236 |
|
|---|
| 237 | /* Check for some output map */
|
|---|
| 238 | if ((output.stream_rast->answer == NULL)
|
|---|
| 239 | && (output.stream_vect->answer == NULL)
|
|---|
| 240 | && (output.dir_rast->answer == NULL)) {
|
|---|
| 241 | G_fatal_error(_("At least one output raster maps must be specified"));
|
|---|
| 242 | }
|
|---|
| 243 |
|
|---|
| 244 | /*********************/
|
|---|
| 245 | /* preparation */
|
|---|
| 246 | /*********************/
|
|---|
| 247 |
|
|---|
| 248 | /* open input maps */
|
|---|
| 249 | mapset = G_find_raster2(input.ele->answer, "");
|
|---|
| 250 | ele_fd = Rast_open_old(input.ele->answer, mapset);
|
|---|
| 251 |
|
|---|
| 252 | if (input.acc->answer) {
|
|---|
| 253 | mapset = G_find_raster2(input.acc->answer, "");
|
|---|
| 254 | acc_fd = Rast_open_old(input.acc->answer, mapset);
|
|---|
| 255 | }
|
|---|
| 256 | else
|
|---|
| 257 | acc_fd = -1;
|
|---|
| 258 |
|
|---|
| 259 | if (input.depression->answer) {
|
|---|
| 260 | mapset = G_find_raster2(input.depression->answer, "");
|
|---|
| 261 | depr_fd = Rast_open_old(input.depression->answer, mapset);
|
|---|
| 262 | }
|
|---|
| 263 | else
|
|---|
| 264 | depr_fd = -1;
|
|---|
| 265 |
|
|---|
| 266 | /* set global variables */
|
|---|
| 267 | nrows = Rast_window_rows();
|
|---|
| 268 | ncols = Rast_window_cols();
|
|---|
| 269 | sides = 8; /* not a user option */
|
|---|
| 270 | c_fac = 5; /* not a user option, MFD covergence factor 5 gives best results */
|
|---|
| 271 |
|
|---|
| 272 | /* segment structures */
|
|---|
| 273 | seg_rows = seg_cols = 64;
|
|---|
| 274 | seg2kb = seg_rows * seg_cols / 1024.;
|
|---|
| 275 |
|
|---|
| 276 | /* balance segment files */
|
|---|
| 277 | /* elevation + accumulation: * 2 */
|
|---|
| 278 | memory_divisor = sizeof(WAT_ALT) * 2;
|
|---|
| 279 | disk_space = sizeof(WAT_ALT);
|
|---|
| 280 | /* stream ids: / 2 */
|
|---|
| 281 | memory_divisor += sizeof(int) / 2.;
|
|---|
| 282 | disk_space += sizeof(int);
|
|---|
| 283 |
|
|---|
| 284 | /* aspect and flags: * 2 */
|
|---|
| 285 | memory_divisor += sizeof(ASP_FLAG) * 4;
|
|---|
| 286 | disk_space += sizeof(ASP_FLAG);
|
|---|
| 287 |
|
|---|
| 288 | /* astar_points: / 16 */
|
|---|
| 289 | /* ideally only a few but large segments */
|
|---|
| 290 | memory_divisor += sizeof(POINT) / 16.;
|
|---|
| 291 | disk_space += sizeof(POINT);
|
|---|
| 292 | /* heap points: / 4 */
|
|---|
| 293 | memory_divisor += sizeof(HEAP_PNT) / 4.;
|
|---|
| 294 | disk_space += sizeof(HEAP_PNT);
|
|---|
| 295 |
|
|---|
| 296 | /* KB -> MB */
|
|---|
| 297 | memory_divisor *= seg2kb / 1024.;
|
|---|
| 298 | disk_space *= seg2kb / 1024.;
|
|---|
| 299 |
|
|---|
| 300 | num_open_segs = memory / memory_divisor;
|
|---|
| 301 | heap_mem = num_open_segs * seg2kb * sizeof(HEAP_PNT) /
|
|---|
| 302 | (4. * 1024.);
|
|---|
| 303 | num_seg_total = (ncols / seg_cols + 1) * (nrows / seg_rows + 1);
|
|---|
| 304 | if (num_open_segs > num_seg_total) {
|
|---|
| 305 | heap_mem += (num_open_segs - num_seg_total) * memory_divisor;
|
|---|
| 306 | heap_mem -= (num_open_segs - num_seg_total) * seg2kb *
|
|---|
| 307 | sizeof(HEAP_PNT) / (4. * 1024.);
|
|---|
| 308 | num_open_segs = num_seg_total;
|
|---|
| 309 | }
|
|---|
| 310 | if (num_open_segs < 16) {
|
|---|
| 311 | num_open_segs = 16;
|
|---|
| 312 | heap_mem = num_open_segs * seg2kb * sizeof(HEAP_PNT) /
|
|---|
| 313 | (4. * 1024.);
|
|---|
| 314 | }
|
|---|
| 315 | G_verbose_message(_("%.2f%% of data are kept in memory"),
|
|---|
| 316 | 100. * num_open_segs / num_seg_total);
|
|---|
| 317 | disk_space *= num_seg_total;
|
|---|
| 318 | if (disk_space < 1024.0)
|
|---|
| 319 | G_verbose_message(_("Will need up to %.2f MB of disk space"), disk_space);
|
|---|
| 320 | else
|
|---|
| 321 | G_verbose_message(_("Will need up to %.2f GB (%.0f MB) of disk space"),
|
|---|
| 322 | disk_space / 1024.0, disk_space);
|
|---|
| 323 |
|
|---|
| 324 | /* open segment files */
|
|---|
| 325 | G_verbose_message(_("Creating temporary files..."));
|
|---|
| 326 | seg_open(&watalt, nrows, ncols, seg_rows, seg_cols, num_open_segs * 2,
|
|---|
| 327 | sizeof(WAT_ALT), 1);
|
|---|
| 328 | if (num_open_segs * 2 > num_seg_total)
|
|---|
| 329 | heap_mem += (num_open_segs * 2 - num_seg_total) * seg2kb *
|
|---|
| 330 | sizeof(WAT_ALT) / 1024.;
|
|---|
| 331 | cseg_open(&stream, seg_rows, seg_cols, num_open_segs / 2.);
|
|---|
| 332 |
|
|---|
| 333 | seg_open(&aspflag, nrows, ncols, seg_rows, seg_cols, num_open_segs * 2,
|
|---|
| 334 | sizeof(ASP_FLAG), 1);
|
|---|
| 335 | /*
|
|---|
| 336 | bseg_open(&asp, seg_rows, seg_cols, num_open_segs);
|
|---|
| 337 | bseg_open(&bitflags, seg_rows, seg_cols, num_open_segs * 4);
|
|---|
| 338 | */
|
|---|
| 339 |
|
|---|
| 340 | if (num_open_segs * 4 > num_seg_total)
|
|---|
| 341 | heap_mem += (num_open_segs * 4 - num_seg_total) * seg2kb / 1024.;
|
|---|
| 342 |
|
|---|
| 343 | /* load maps */
|
|---|
| 344 | if (load_maps(ele_fd, acc_fd) < 0)
|
|---|
| 345 | G_fatal_error(_("Unable to load input raster map(s)"));
|
|---|
| 346 | else if (!n_points)
|
|---|
| 347 | G_fatal_error(_("No non-NULL cells in input raster map(s)"));
|
|---|
| 348 |
|
|---|
| 349 | G_debug(1, "open segments for A* points");
|
|---|
| 350 | /* columns per segment */
|
|---|
| 351 | seg_cols = seg_rows * seg_rows;
|
|---|
| 352 | num_seg_total = n_points / seg_cols;
|
|---|
| 353 | if (n_points % seg_cols > 0)
|
|---|
| 354 | num_seg_total++;
|
|---|
| 355 | /* no need to have more segments open than exist */
|
|---|
| 356 | num_open_array_segs = num_open_segs / 16.;
|
|---|
| 357 | if (num_open_array_segs > num_seg_total)
|
|---|
| 358 | num_open_array_segs = num_seg_total;
|
|---|
| 359 | if (num_open_array_segs < 1)
|
|---|
| 360 | num_open_array_segs = 1;
|
|---|
| 361 |
|
|---|
| 362 | G_debug(1, "segment size for A* points: %d", seg_cols);
|
|---|
| 363 | seg_open(&astar_pts, 1, n_points, 1, seg_cols, num_open_array_segs,
|
|---|
| 364 | sizeof(POINT), 1);
|
|---|
| 365 |
|
|---|
| 366 | /* one-based d-ary search_heap with astar_pts */
|
|---|
| 367 | G_debug(1, "open segments for A* search heap");
|
|---|
| 368 |
|
|---|
| 369 | /* allowed memory for search heap in MB */
|
|---|
| 370 | G_debug(1, "heap memory %.2f MB", heap_mem);
|
|---|
| 371 | /* columns per segment */
|
|---|
| 372 | /* larger is faster */
|
|---|
| 373 | seg_cols = seg_rows * seg_rows * seg_rows;
|
|---|
| 374 | num_seg_total = n_points / seg_cols;
|
|---|
| 375 | if (n_points % seg_cols > 0)
|
|---|
| 376 | num_seg_total++;
|
|---|
| 377 | /* no need to have more segments open than exist */
|
|---|
| 378 | num_open_array_segs = (1 << 20) * heap_mem / (seg_cols * sizeof(HEAP_PNT));
|
|---|
| 379 | if (num_open_array_segs > num_seg_total)
|
|---|
| 380 | num_open_array_segs = num_seg_total;
|
|---|
| 381 | if (num_open_array_segs < 2)
|
|---|
| 382 | num_open_array_segs = 2;
|
|---|
| 383 |
|
|---|
| 384 | G_debug(1, "A* search heap open segments %d, total %d",
|
|---|
| 385 | num_open_array_segs, num_seg_total);
|
|---|
| 386 | G_debug(1, "segment size for heap points: %d", seg_cols);
|
|---|
| 387 | /* the search heap will not hold more than 5% of all points at any given time ? */
|
|---|
| 388 | /* chances are good that the heap will fit into one large segment */
|
|---|
| 389 | seg_open(&search_heap, 1, n_points + 1, 1, seg_cols,
|
|---|
| 390 | num_open_array_segs, sizeof(HEAP_PNT), 1);
|
|---|
| 391 |
|
|---|
| 392 | /********************/
|
|---|
| 393 | /* processing */
|
|---|
| 394 | /********************/
|
|---|
| 395 |
|
|---|
| 396 | /* initialize A* search */
|
|---|
| 397 | if (init_search(depr_fd) < 0)
|
|---|
| 398 | G_fatal_error(_("Unable to initialize search"));
|
|---|
| 399 |
|
|---|
| 400 | /* sort elevation and get initial stream direction */
|
|---|
| 401 | if (do_astar() < 0)
|
|---|
| 402 | G_fatal_error(_("Unable to sort elevation raster map values"));
|
|---|
| 403 | seg_close(&search_heap);
|
|---|
| 404 |
|
|---|
| 405 | if (acc_fd < 0) {
|
|---|
| 406 | /* accumulate surface flow */
|
|---|
| 407 | if (do_accum(d8cut) < 0)
|
|---|
| 408 | G_fatal_error(_("Unable to calculate flow accumulation"));
|
|---|
| 409 | }
|
|---|
| 410 |
|
|---|
| 411 | /* extract streams */
|
|---|
| 412 | if (extract_streams(threshold, mont_exp, acc_fd < 0) < 0)
|
|---|
| 413 | G_fatal_error(_("Unable to extract streams"));
|
|---|
| 414 |
|
|---|
| 415 | seg_close(&astar_pts);
|
|---|
| 416 | seg_close(&watalt);
|
|---|
| 417 |
|
|---|
| 418 | /* thin streams */
|
|---|
| 419 | if (thin_streams() < 0)
|
|---|
| 420 | G_fatal_error(_("Unable to thin streams"));
|
|---|
| 421 |
|
|---|
| 422 | /* delete short streams */
|
|---|
| 423 | if (min_stream_length) {
|
|---|
| 424 | if (del_streams(min_stream_length) < 0)
|
|---|
| 425 | G_fatal_error(_("Unable to delete short stream segments"));
|
|---|
| 426 | }
|
|---|
| 427 |
|
|---|
| 428 | /* write output maps */
|
|---|
| 429 | if (close_maps(output.stream_rast->answer, output.stream_vect->answer,
|
|---|
| 430 | output.dir_rast->answer) < 0)
|
|---|
| 431 | G_fatal_error(_("Unable to write output raster maps"));
|
|---|
| 432 |
|
|---|
| 433 | cseg_close(&stream);
|
|---|
| 434 | seg_close(&aspflag);
|
|---|
| 435 |
|
|---|
| 436 | exit(EXIT_SUCCESS);
|
|---|
| 437 | }
|
|---|