main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /*
  2. ** Original Algorithm: H. Mitasova, L. Mitas, J. Hofierka, M. Zlocha
  3. ** New GRASS Implementation: J. Caplan, M. Ruesink 1995
  4. **
  5. ** US Army Construction Engineering Research Lab, University of Illinois
  6. **
  7. ** Copyright J. Caplan, H. Mitasova, L. Mitas, M.Ruesink, J. Hofierka,
  8. ** M. Zlocha 1995
  9. **
  10. **This program is free software; you can redistribute it and/or
  11. **modify it under the terms of the GNU General Public License
  12. **as published by the Free Software Foundation; either version 2
  13. **of the License, or (at your option) any later version.
  14. **
  15. **This program is distributed in the hope that it will be useful,
  16. **but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. **MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. **GNU General Public License for more details.
  19. **
  20. **You should have received a copy of the GNU General Public License
  21. **along with this program; if not, write to the Free Software
  22. **Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. **
  24. ** version 13 for GRASS5.0
  25. ** FP related bugs in slope length output fixed by Helena oct. 1999)
  26. ** Update MN: commented line 387
  27. */
  28. #include <grass/gis.h>
  29. #include <grass/raster.h>
  30. #include <grass/glocale.h>
  31. #include "r.flow.h"
  32. #include "mem.h"
  33. #include "io.h"
  34. #include "aspect.h"
  35. #include "precomp.h"
  36. #define HORIZ 1 /* \ */
  37. #define VERT 0 /* | */
  38. #define EAST 1 /* | */
  39. #define WEST 0 /* |_ magic */
  40. #define NORTH 1 /* | numbers */
  41. #define SOUTH 0 /* | */
  42. #define ROW 1 /* | */
  43. #define COL 0 /* / */
  44. CELL v; /* address for segment retrieval macros */
  45. /* heap memory */
  46. struct Cell_head region; /* resolution and boundaries */
  47. struct Map_info fl; /* output vector file header */
  48. struct BM *bitbar; /* space-efficient barrier matrix */
  49. int lgfd; /* output length file descriptor */
  50. char string[1024]; /* space for strings */
  51. layer el, as, ds; /* elevation, aspect, density (accumulation) */
  52. double *ew_dist; /* east-west distances for rows */
  53. double *epsilon[2]; /* quantization errors for rows */
  54. /* command-line parameters */
  55. params parm;
  56. typedef struct
  57. {
  58. int row, col; /* current matrix address */
  59. }
  60. addr;
  61. typedef int bbox[2][2]; /* current bounding box */
  62. typedef struct
  63. {
  64. double x, y, z; /* exact earth coordinates */
  65. double theta; /* aspect */
  66. double r, c; /* cell matrix coordinates */
  67. }
  68. point;
  69. typedef struct
  70. {
  71. double *px, *py; /* x/y point arrays */
  72. int index; /* index of next point */
  73. }
  74. flowline;
  75. /***************************** CALCULATION ******************************/
  76. /*
  77. * height_angle_bounding_box: averages matrix values at sub between
  78. * floor(cut) and ceil(cut), based on proximity; adjusts bbox
  79. * globals r: o, z, parm
  80. * params w: p, b
  81. */
  82. static void
  83. height_angle_bounding_box(int sub, double cut, int horiz, point * p, bbox b)
  84. {
  85. int c, f = (int)cut;
  86. double r = cut - (double)f;
  87. double a1, a2, a, d;
  88. b[horiz][horiz] = sub - 1;
  89. b[horiz][!horiz] = sub + 1;
  90. b[!horiz][horiz] = f + 1;
  91. c = (b[!horiz][!horiz] = f - (r == 0.)) + 1;
  92. if (horiz) {
  93. a1 = (double)aspect(sub, f);
  94. a2 = (double)aspect(sub, c);
  95. p->z = (double)get(el, sub, f) * (1. - r) +
  96. (double)get(el, sub, c) * r;
  97. }
  98. else {
  99. a1 = (double)aspect(f, sub);
  100. a2 = (double)aspect(c, sub);
  101. p->z = (double)get(el, f, sub) * (1. - r) +
  102. (double)get(el, c, sub) * r;
  103. }
  104. if (!(a1 == UNDEF || a2 == UNDEF) &&
  105. !(Rast_is_d_null_value(&a1) || Rast_is_d_null_value(&a2)))
  106. /* if (!(Rast_is_d_null_value(&a1) || Rast_is_d_null_value(&a2))) */
  107. {
  108. if ((d = a1 - a2) >= D_PI || d <= -D_PI) {
  109. if (a2 > D_PI)
  110. a2 -= D2_PI;
  111. else
  112. a1 -= D2_PI;
  113. }
  114. a = r * a2 + (1. - r) * a1;
  115. p->theta = a + (a < 0.) * D2_PI;
  116. }
  117. else
  118. p->theta = UNDEF;
  119. return;
  120. }
  121. /*
  122. * sloping: returns elevation condition for continuing current line
  123. */
  124. #define sloping(z1, z2) (z1 > z2)
  125. /*
  126. * on_map: returns map boundary condition for continuing current line
  127. * globals r: region
  128. */
  129. static int on_map(int sub, double cut, int horiz)
  130. {
  131. return
  132. (sub >= 0 && cut >= 0.0 &&
  133. ((horiz && sub < region.rows && cut <= (double)(region.cols - 1)) ||
  134. (!horiz && sub < region.cols && cut <= (double)(region.rows - 1))));
  135. }
  136. /*
  137. * add_to_line: puts a new point on the end of the current flowline
  138. * globals r: parm
  139. * params w: f
  140. */
  141. static void add_to_line(point * p, flowline * f)
  142. {
  143. if (parm.flout) {
  144. f->px[f->index] = (double)p->x;
  145. f->py[f->index] = (double)p->y;
  146. }
  147. ++f->index;
  148. }
  149. /*
  150. * rectify: correct quantization problems (designed for speed, not elegance)
  151. */
  152. static double rectify(double delta, double bd[2], double e)
  153. {
  154. if (delta > 0.) {
  155. if (delta > bd[1] + e)
  156. return delta;
  157. }
  158. else {
  159. if (delta < bd[0] - e)
  160. return delta;
  161. }
  162. if (delta < bd[1] - e)
  163. if (delta > bd[0] + e)
  164. return delta;
  165. else
  166. return bd[0];
  167. else
  168. return bd[1];
  169. }
  170. /*
  171. * next_point: computes next point based on current point, z, and o
  172. * returns continuation condition
  173. * globals r: region, bitbar, parm
  174. * params w: p, a, l
  175. * globals w: density
  176. */
  177. static int next_point(point * p, /* current/next point */
  178. addr * a, /* current/next matrix address */
  179. bbox b, /* current/next bounding box */
  180. double *l /* current/eventual length */
  181. )
  182. {
  183. int sub;
  184. double cut;
  185. int horiz;
  186. int semi;
  187. double length, delta;
  188. double deltaz;
  189. double tangent;
  190. double oldz = p->z;
  191. double oldtheta = p->theta;
  192. double oldr = p->r;
  193. double oldc = p->c;
  194. addr ads;
  195. double bdy[2], bdx[2];
  196. ads = *a;
  197. bdy[SOUTH] = (double)(oldr - b[ROW][SOUTH]) * region.ns_res;
  198. bdy[NORTH] = (double)(oldr - b[ROW][NORTH]) * region.ns_res;
  199. bdx[WEST] = (double)(b[COL][WEST] - oldc) * ew_dist[ads.row];
  200. bdx[EAST] = (double)(b[COL][EAST] - oldc) * ew_dist[ads.row];
  201. semi = oldtheta < 90 || oldtheta >= 270;
  202. tangent = tan(oldtheta * DEG2RAD);
  203. if (oldtheta != 90 && oldtheta != 270 && /* north/south */
  204. (delta = (bdy[semi] * tangent)) < bdx[EAST] && delta > bdx[WEST]) {
  205. delta = rectify(delta, bdx, epsilon[HORIZ][ads.row]);
  206. p->x += delta;
  207. p->y += bdy[semi];
  208. p->r = (double)b[ROW][semi];
  209. p->c += delta / ew_dist[ads.row];
  210. a->row = b[ROW][semi];
  211. a->col = ROUND(p->c);
  212. sub = b[ROW][semi];
  213. cut = p->c;
  214. horiz = HORIZ;
  215. if (parm.lgout)
  216. length = hypot(delta, bdy[semi]);
  217. }
  218. else { /* east/west */
  219. semi = oldtheta < 180;
  220. if (oldtheta == 90 || oldtheta == 270)
  221. delta = 0;
  222. else {
  223. /* I don't know if this is right case.
  224. * Anyway, should be avoid from dividing by zero.
  225. * Any hydrologic idea?
  226. */
  227. if (tangent == 0.0)
  228. tangent = 0.000001;
  229. delta = bdx[semi] / tangent;
  230. }
  231. delta = rectify(delta, bdy, epsilon[VERT][ads.row]);
  232. p->y += delta;
  233. p->x += bdx[semi];
  234. p->r -= delta / region.ns_res;
  235. p->c = (double)b[COL][semi];
  236. a->row = ROUND(p->r);
  237. a->col = b[COL][semi];
  238. sub = b[COL][semi];
  239. cut = p->r;
  240. horiz = VERT;
  241. if (parm.lgout)
  242. length = hypot(bdx[semi], delta);
  243. }
  244. if (on_map(sub, cut, horiz) &&
  245. (height_angle_bounding_box(sub, cut, horiz, p, b),
  246. sloping(oldz, p->z)) &&
  247. !(parm.barin && BM_get(bitbar, a->col, a->row))) {
  248. if (parm.dsout && (ads.row != a->row || ads.col != a->col))
  249. put(ds, a->row, a->col, get(ds, a->row, a->col) + 1);
  250. /* if (parm.lgout)
  251. * *l += parm.l3d ? hypot(length, oldz - p->z) : length; this did not work, helena*/
  252. if (parm.lgout) {
  253. if (parm.l3d) {
  254. deltaz = oldz - p->z; /*fix by helena Dec. 06 */
  255. *l += hypot(length, deltaz);
  256. }
  257. else
  258. *l += length;
  259. }
  260. return 1;
  261. }
  262. return 0;
  263. }
  264. /*
  265. * calculate: create a flowline for each cell
  266. * globals r: region, bitbar, parm, lgfd
  267. */
  268. static void calculate(void)
  269. {
  270. point pts;
  271. addr ads;
  272. bbox bbs;
  273. flowline fls;
  274. int row, col;
  275. /* double x, y, length, xstep, ystep, roffset, coffset; */
  276. double x, y, length, xstep, ystep;
  277. FCELL *lg = Rast_allocate_f_buf();
  278. struct line_pnts *points = Vect_new_line_struct();
  279. struct line_cats *cats = Vect_new_cats_struct();
  280. int loopstep = (!parm.dsout && !parm.lgout && parm.flout) ? parm.skip : 1;
  281. G_important_message(_("Calculating..."));
  282. fls.px = (double *)G_calloc(parm.bound, sizeof(double));
  283. fls.py = (double *)G_calloc(parm.bound, sizeof(double));
  284. ystep = region.ns_res * (double)loopstep;
  285. /* FIXME - allow seed to be specified for repeatability */
  286. G_srand48_auto();
  287. for (row = 0, y = (double)region.north - (region.ns_res * .5);
  288. row < region.rows; row += loopstep, y -= ystep) {
  289. xstep = ew_dist[row] * (double)loopstep;
  290. G_percent(row, region.rows, 2);
  291. for (col = 0, x = (double)region.west + (ew_dist[row] * .5);
  292. col < region.cols; col += loopstep, x += xstep) {
  293. length = 0.0;
  294. fls.index = 0;
  295. if (!(parm.barin && BM_get(bitbar, col, row))) {
  296. #ifdef OFFSET
  297. /* disabled by helena June 2005 */
  298. roffset = parm.offset * (double)region.ew_res
  299. * ((2. * G_drand48()) - 1.);
  300. coffset = parm.offset * (double)region.ns_res
  301. * ((2. * G_drand48()) - 1.);
  302. #endif
  303. pts.x = x;
  304. pts.y = y;
  305. pts.z = (double)get(el, row, col);
  306. pts.theta = (double)aspect(row, col);
  307. pts.r = (double)row; /* + roffset; */
  308. pts.c = (double)col; /*+ coffset; */
  309. ads.row = row;
  310. ads.col = col;
  311. #ifdef OFFSET
  312. G_debug(3, "dx: %f x: %f %f row: %f %f\n",
  313. roffset, x, pts.x, (double)row, pts.r);
  314. G_debug(3, "dy: %f y: %f %f col: %f %f\n",
  315. roffset, y, pts.y, (double)col, pts.c);
  316. #endif
  317. bbs[ROW][SOUTH] = row + 1;
  318. bbs[ROW][NORTH] = row - 1;
  319. bbs[COL][WEST] = col - 1;
  320. bbs[COL][EAST] = col + 1;
  321. do
  322. add_to_line(&pts, &fls);
  323. while (fls.index <= parm.bound &&
  324. (pts.z != UNDEFZ && pts.theta >= 0 && pts.theta <= 360)
  325. &&
  326. /* (!Rast_is_d_null_value(&pts.z) && pts.theta != UNDEF) && */
  327. next_point(&pts, &ads, bbs, &length));
  328. }
  329. if (fls.index > 1 && parm.flout &&
  330. (loopstep == parm.skip ||
  331. !(row % parm.skip || col % parm.skip))) {
  332. Vect_copy_xyz_to_pnts(points, fls.px, fls.py, NULL,
  333. fls.index);
  334. Vect_write_line(&fl, GV_LINE, points, cats);
  335. }
  336. if (parm.lgout)
  337. lg[col] = (float)length;
  338. }
  339. if (parm.lgout)
  340. Rast_put_f_row(lgfd, lg);
  341. }
  342. G_percent (1, 1, 1);
  343. G_free(fls.px);
  344. G_free(fls.py);
  345. /* G_free (fls); *//* commented 19/10/99 MN */
  346. G_free(lg);
  347. Vect_destroy_line_struct(points);
  348. Vect_destroy_cats_struct(cats);
  349. if (parm.lgout)
  350. Rast_close(lgfd);
  351. }
  352. int main(int argc, char *argv[])
  353. {
  354. struct GModule *module;
  355. struct Option *pelevin, *paspin, *pbarin, *pskip, *pbound,
  356. *pflout, *plgout, *pdsout;
  357. struct Flag *fup, *flg, *fmem;
  358. int default_skip, larger, default_bound;
  359. #ifdef OFFSET
  360. char *default_offset_ans, *offset_opt;
  361. #endif
  362. char *default_skip_ans, *default_bound_ans, *skip_opt;
  363. struct History history;
  364. /* Initialize GIS engine */
  365. G_gisinit(argv[0]);
  366. module = G_define_module();
  367. G_add_keyword(_("raster"));
  368. G_add_keyword(_("hydrology"));
  369. module->label = _("Constructs flowlines.");
  370. module->description =
  371. _("Computes flowlines, flowpath lengths, "
  372. "and flowaccumulation (contributing areas) from a elevation raster "
  373. "map.");
  374. pelevin = G_define_standard_option(G_OPT_R_ELEV);
  375. paspin = G_define_standard_option(G_OPT_R_INPUT);
  376. paspin->key = "aspect";
  377. paspin->required = NO;
  378. paspin->description = _("Name of input aspect raster map");
  379. paspin->guisection = _("Input maps");
  380. pbarin = G_define_standard_option(G_OPT_R_INPUT);
  381. pbarin->key = "barrier";
  382. pbarin->required = NO;
  383. pbarin->description = _("Name of input barrier raster map");
  384. pbarin->guisection = _("Input maps");
  385. pskip = G_define_option();
  386. pskip->key = "skip";
  387. pskip->type = TYPE_INTEGER;
  388. pskip->required = NO;
  389. pskip->description = _("Number of cells between flowlines");
  390. pbound = G_define_option();
  391. pbound->key = "bound";
  392. pbound->type = TYPE_INTEGER;
  393. pbound->required = NO;
  394. pbound->description = _("Maximum number of segments per flowline");
  395. pflout = G_define_standard_option(G_OPT_V_OUTPUT);
  396. pflout->key = "flowline";
  397. pflout->required = NO;
  398. pflout->description = _("Name for output flow line vector map");
  399. pflout->guisection = _("Output maps");
  400. plgout = G_define_standard_option(G_OPT_R_OUTPUT);
  401. plgout->key = "flowlength";
  402. plgout->required = NO;
  403. plgout->description = _("Name for output flow path length raster map");
  404. plgout->guisection = _("Output maps");
  405. pdsout = G_define_standard_option(G_OPT_R_OUTPUT);
  406. pdsout->key = "flowaccumulation";
  407. pdsout->required = NO;
  408. pdsout->description = _("Name for output flow accumulation raster map");
  409. pdsout->guisection = _("Output maps");
  410. fup = G_define_flag();
  411. fup->key = 'u';
  412. fup->description =
  413. _("Compute upslope flowlines instead of default downhill flowlines");
  414. flg = G_define_flag();
  415. flg->key = '3';
  416. flg->description = _("3D lengths instead of 2D");
  417. fmem = G_define_flag();
  418. fmem->key = 'm';
  419. fmem->description = _("Use less memory, at a performance penalty");
  420. if (G_parser(argc, argv))
  421. exit(EXIT_FAILURE);
  422. G_get_set_window(&region);
  423. larger = ((region.cols < region.rows) ? region.rows : region.cols);
  424. default_skip = (larger < 50) ? 1 : (int)(larger / 50);
  425. default_skip_ans =
  426. G_calloc((int)log10((double)default_skip) + 2, sizeof(char));
  427. skip_opt = G_calloc((int)log10((double)larger) + 4, sizeof(char));
  428. sprintf(default_skip_ans, "%d", default_skip);
  429. sprintf(skip_opt, "1-%d", larger);
  430. default_bound = (int)(4. * hypot((double)region.rows,
  431. (double)region.cols));
  432. default_bound_ans =
  433. G_calloc((int)log10((double)default_bound) + 4, sizeof(char));
  434. sprintf(default_bound_ans, "0-%d", default_bound);
  435. #ifdef OFFSET
  436. /* below fix changed from 0.0 to 1.0 and its effect disabled in
  437. * calc.c, Helena June 2005 */
  438. default_offset = 1.0; /* fixed 20. May 2001 Helena */
  439. default_offset_ans =
  440. G_calloc((int)log10(default_offset) + 2, sizeof(char));
  441. sprintf(default_offset_ans, "%f", default_offset);
  442. offset_opt = G_calloc((int)log10(default_offset) + 4, sizeof(char));
  443. sprintf(offset_opt, "0.0-500.0");
  444. #endif
  445. if (!pskip->answer)
  446. pskip->answer = default_skip_ans;
  447. if (!pbound->answer)
  448. pbound->answer = default_bound_ans + 2;
  449. parm.elevin = pelevin->answer;
  450. parm.aspin = paspin->answer;
  451. parm.barin = pbarin->answer;
  452. parm.skip = atoi(pskip->answer);
  453. parm.bound = atoi(pbound->answer);
  454. parm.flout = pflout->answer;
  455. parm.lgout = plgout->answer;
  456. parm.dsout = pdsout->answer;
  457. parm.up = fup->answer;
  458. parm.l3d = flg->answer;
  459. parm.mem = fmem->answer;
  460. if (!pflout->answer && !plgout->answer && !pdsout->answer)
  461. G_fatal_error(_("You must select one or more output maps (flout, lgout, dsout)"));
  462. if (parm.seg)
  463. parm.mem = '\0';
  464. else if (parm.mem)
  465. parm.aspin = NULL;
  466. el.name = parm.elevin;
  467. as.name = (parm.aspin) ? parm.aspin : "internal aspects";
  468. ds.name = parm.dsout;
  469. el.row_offset = el.col_offset = 1;
  470. as.row_offset = as.col_offset = 0;
  471. ds.row_offset = ds.col_offset = 0;
  472. if ((G_projection() == PROJECTION_LL)) /* added MN 2005 */
  473. G_fatal_error(_("lat/long projection not supported by "
  474. "r.flow. Please use 'r.watershed' for calculating "
  475. "flow accumulation."));
  476. if (parm.flout || parm.dsout || parm.lgout) {
  477. open_output_files();
  478. allocate_heap();
  479. read_input_files();
  480. precompute();
  481. calculate();
  482. if (parm.dsout)
  483. write_density_file();
  484. close_files();
  485. deallocate_heap();
  486. }
  487. if (parm.dsout) {
  488. Rast_short_history(parm.dsout, "raster", &history);
  489. Rast_command_history(&history);
  490. Rast_write_history(parm.dsout, &history);
  491. }
  492. if (parm.lgout) {
  493. Rast_short_history(parm.lgout, "raster", &history);
  494. Rast_command_history(&history);
  495. Rast_write_history(parm.lgout, &history);
  496. }
  497. exit(EXIT_SUCCESS);
  498. }