main.c 14 KB

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