123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586 |
- /*
- ** Original Algorithm: H. Mitasova, L. Mitas, J. Hofierka, M. Zlocha
- ** New GRASS Implementation: J. Caplan, M. Ruesink 1995
- **
- ** US Army Construction Engineering Research Lab, University of Illinois
- **
- ** Copyright J. Caplan, H. Mitasova, L. Mitas, M.Ruesink, J. Hofierka,
- ** M. Zlocha 1995
- **
- **This program is free software; you can redistribute it and/or
- **modify it under the terms of the GNU General Public License
- **as published by the Free Software Foundation; either version 2
- **of the License, or (at your option) any later version.
- **
- **This program is distributed in the hope that it will be useful,
- **but WITHOUT ANY WARRANTY; without even the implied warranty of
- **MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- **GNU General Public License for more details.
- **
- **You should have received a copy of the GNU General Public License
- **along with this program; if not, write to the Free Software
- **Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- **
- ** version 13 for GRASS5.0
- ** FP related bugs in slope length output fixed by Helena oct. 1999)
- ** Update MN: commented line 387
- */
- #include <grass/gis.h>
- #include <grass/raster.h>
- #include <grass/glocale.h>
- #include "r.flow.h"
- #include "mem.h"
- #include "io.h"
- #include "aspect.h"
- #include "precomp.h"
- #define HORIZ 1 /* \ */
- #define VERT 0 /* | */
- #define EAST 1 /* | */
- #define WEST 0 /* |_ magic */
- #define NORTH 1 /* | numbers */
- #define SOUTH 0 /* | */
- #define ROW 1 /* | */
- #define COL 0 /* / */
- CELL v; /* address for segment retrieval macros */
- /* heap memory */
- struct Cell_head region; /* resolution and boundaries */
- struct Map_info fl; /* output vector file header */
- struct BM *bitbar; /* space-efficient barrier matrix */
- int lgfd; /* output length file descriptor */
- char string[1024]; /* space for strings */
- layer el, as, ds; /* elevation, aspect, density (accumulation) */
- double *ew_dist; /* east-west distances for rows */
- double *epsilon[2]; /* quantization errors for rows */
- /* command-line parameters */
- params parm;
- typedef struct
- {
- int row, col; /* current matrix address */
- }
- addr;
- typedef int bbox[2][2]; /* current bounding box */
- typedef struct
- {
- double x, y, z; /* exact earth coordinates */
- double theta; /* aspect */
- double r, c; /* cell matrix coordinates */
- }
- point;
- typedef struct
- {
- double *px, *py; /* x/y point arrays */
- int index; /* index of next point */
- }
- flowline;
- /***************************** CALCULATION ******************************/
- /*
- * height_angle_bounding_box: averages matrix values at sub between
- * floor(cut) and ceil(cut), based on proximity; adjusts bbox
- * globals r: o, z, parm
- * params w: p, b
- */
- static void
- height_angle_bounding_box(int sub, double cut, int horiz, point * p, bbox b)
- {
- int c, f = (int)cut;
- double r = cut - (double)f;
- double a1, a2, a, d;
- b[horiz][horiz] = sub - 1;
- b[horiz][!horiz] = sub + 1;
- b[!horiz][horiz] = f + 1;
- c = (b[!horiz][!horiz] = f - (r == 0.)) + 1;
- if (horiz) {
- a1 = (double)aspect(sub, f);
- a2 = (double)aspect(sub, c);
- p->z = (double)get(el, sub, f) * (1. - r) +
- (double)get(el, sub, c) * r;
- }
- else {
- a1 = (double)aspect(f, sub);
- a2 = (double)aspect(c, sub);
- p->z = (double)get(el, f, sub) * (1. - r) +
- (double)get(el, c, sub) * r;
- }
- if (!(a1 == UNDEF || a2 == UNDEF) &&
- !(Rast_is_d_null_value(&a1) || Rast_is_d_null_value(&a2)))
- /* if (!(Rast_is_d_null_value(&a1) || Rast_is_d_null_value(&a2))) */
- {
- if ((d = a1 - a2) >= D_PI || d <= -D_PI) {
- if (a2 > D_PI)
- a2 -= D2_PI;
- else
- a1 -= D2_PI;
- }
- a = r * a2 + (1. - r) * a1;
- p->theta = a + (a < 0.) * D2_PI;
- }
- else
- p->theta = UNDEF;
- return;
- }
- /*
- * sloping: returns elevation condition for continuing current line
- */
- #define sloping(z1, z2) (z1 > z2)
- /*
- * on_map: returns map boundary condition for continuing current line
- * globals r: region
- */
- static int on_map(int sub, double cut, int horiz)
- {
- return
- (sub >= 0 && cut >= 0.0 &&
- ((horiz && sub < region.rows && cut <= (double)(region.cols - 1)) ||
- (!horiz && sub < region.cols && cut <= (double)(region.rows - 1))));
- }
- /*
- * add_to_line: puts a new point on the end of the current flowline
- * globals r: parm
- * params w: f
- */
- static void add_to_line(point * p, flowline * f)
- {
- if (parm.flout) {
- f->px[f->index] = (double)p->x;
- f->py[f->index] = (double)p->y;
- }
- ++f->index;
- }
- /*
- * rectify: correct quantization problems (designed for speed, not elegance)
- */
- static double rectify(double delta, double bd[2], double e)
- {
- if (delta > 0.) {
- if (delta > bd[1] + e)
- return delta;
- }
- else {
- if (delta < bd[0] - e)
- return delta;
- }
- if (delta < bd[1] - e)
- if (delta > bd[0] + e)
- return delta;
- else
- return bd[0];
- else
- return bd[1];
- }
- /*
- * next_point: computes next point based on current point, z, and o
- * returns continuation condition
- * globals r: region, bitbar, parm
- * params w: p, a, l
- * globals w: density
- */
- static int next_point(point * p, /* current/next point */
- addr * a, /* current/next matrix address */
- bbox b, /* current/next bounding box */
- double *l /* current/eventual length */
- )
- {
- int sub;
- double cut;
- int horiz;
- int semi;
- double length, delta;
- double deltaz;
- double tangent;
- double oldz = p->z;
- double oldtheta = p->theta;
- double oldr = p->r;
- double oldc = p->c;
- addr ads;
- double bdy[2], bdx[2];
- ads = *a;
- bdy[SOUTH] = (double)(oldr - b[ROW][SOUTH]) * region.ns_res;
- bdy[NORTH] = (double)(oldr - b[ROW][NORTH]) * region.ns_res;
- bdx[WEST] = (double)(b[COL][WEST] - oldc) * ew_dist[ads.row];
- bdx[EAST] = (double)(b[COL][EAST] - oldc) * ew_dist[ads.row];
- semi = oldtheta < 90 || oldtheta >= 270;
- tangent = tan(oldtheta * DEG2RAD);
- if (oldtheta != 90 && oldtheta != 270 && /* north/south */
- (delta = (bdy[semi] * tangent)) < bdx[EAST] && delta > bdx[WEST]) {
- delta = rectify(delta, bdx, epsilon[HORIZ][ads.row]);
- p->x += delta;
- p->y += bdy[semi];
- p->r = (double)b[ROW][semi];
- p->c += delta / ew_dist[ads.row];
- a->row = b[ROW][semi];
- a->col = ROUND(p->c);
- sub = b[ROW][semi];
- cut = p->c;
- horiz = HORIZ;
- if (parm.lgout)
- length = hypot(delta, bdy[semi]);
- }
- else { /* east/west */
- semi = oldtheta < 180;
- if (oldtheta == 90 || oldtheta == 270)
- delta = 0;
- else {
- /* I don't know if this is right case.
- * Anyway, should be avoid from dividing by zero.
- * Any hydrologic idea?
- */
- if (tangent == 0.0)
- tangent = 0.000001;
- delta = bdx[semi] / tangent;
- }
- delta = rectify(delta, bdy, epsilon[VERT][ads.row]);
- p->y += delta;
- p->x += bdx[semi];
- p->r -= delta / region.ns_res;
- p->c = (double)b[COL][semi];
- a->row = ROUND(p->r);
- a->col = b[COL][semi];
- sub = b[COL][semi];
- cut = p->r;
- horiz = VERT;
- if (parm.lgout)
- length = hypot(bdx[semi], delta);
- }
- if (on_map(sub, cut, horiz) &&
- (height_angle_bounding_box(sub, cut, horiz, p, b),
- sloping(oldz, p->z)) &&
- !(parm.barin && BM_get(bitbar, a->col, a->row))) {
- if (parm.dsout && (ads.row != a->row || ads.col != a->col))
- put(ds, a->row, a->col, get(ds, a->row, a->col) + 1);
- /* if (parm.lgout)
- * *l += parm.l3d ? hypot(length, oldz - p->z) : length; this did not work, helena*/
- if (parm.lgout) {
- if (parm.l3d) {
- deltaz = oldz - p->z; /*fix by helena Dec. 06 */
- *l += hypot(length, deltaz);
- }
- else
- *l += length;
- }
- return 1;
- }
- return 0;
- }
- /*
- * calculate: create a flowline for each cell
- * globals r: region, bitbar, parm, lgfd
- */
- static void calculate(void)
- {
- point pts;
- addr ads;
- bbox bbs;
- flowline fls;
- int row, col;
- /* double x, y, length, xstep, ystep, roffset, coffset; */
- double x, y, length, xstep, ystep;
- FCELL *lg = Rast_allocate_f_buf();
- struct line_pnts *points = Vect_new_line_struct();
- struct line_cats *cats = Vect_new_cats_struct();
- int loopstep = (!parm.dsout && !parm.lgout && parm.flout) ? parm.skip : 1;
- G_important_message(_("Calculating..."));
- fls.px = (double *)G_calloc(parm.bound, sizeof(double));
- fls.py = (double *)G_calloc(parm.bound, sizeof(double));
- ystep = region.ns_res * (double)loopstep;
- /* FIXME - allow seed to be specified for repeatability */
- G_srand48_auto();
- for (row = 0, y = (double)region.north - (region.ns_res * .5);
- row < region.rows; row += loopstep, y -= ystep) {
- xstep = ew_dist[row] * (double)loopstep;
- G_percent(row, region.rows, 2);
- for (col = 0, x = (double)region.west + (ew_dist[row] * .5);
- col < region.cols; col += loopstep, x += xstep) {
- length = 0.0;
- fls.index = 0;
- if (!(parm.barin && BM_get(bitbar, col, row))) {
- #ifdef OFFSET
- /* disabled by helena June 2005 */
- roffset = parm.offset * (double)region.ew_res
- * ((2. * G_drand48()) - 1.);
- coffset = parm.offset * (double)region.ns_res
- * ((2. * G_drand48()) - 1.);
- #endif
- pts.x = x;
- pts.y = y;
- pts.z = (double)get(el, row, col);
- pts.theta = (double)aspect(row, col);
- pts.r = (double)row; /* + roffset; */
- pts.c = (double)col; /*+ coffset; */
- ads.row = row;
- ads.col = col;
- #ifdef OFFSET
- G_debug(3, "dx: %f x: %f %f row: %f %f\n",
- roffset, x, pts.x, (double)row, pts.r);
- G_debug(3, "dy: %f y: %f %f col: %f %f\n",
- roffset, y, pts.y, (double)col, pts.c);
- #endif
- bbs[ROW][SOUTH] = row + 1;
- bbs[ROW][NORTH] = row - 1;
- bbs[COL][WEST] = col - 1;
- bbs[COL][EAST] = col + 1;
- do
- add_to_line(&pts, &fls);
- while (fls.index <= parm.bound &&
- (pts.z != UNDEFZ && pts.theta >= 0 && pts.theta <= 360)
- &&
- /* (!Rast_is_d_null_value(&pts.z) && pts.theta != UNDEF) && */
- next_point(&pts, &ads, bbs, &length));
- }
- if (fls.index > 1 && parm.flout &&
- (loopstep == parm.skip ||
- !(row % parm.skip || col % parm.skip))) {
- Vect_copy_xyz_to_pnts(points, fls.px, fls.py, NULL,
- fls.index);
- Vect_write_line(&fl, GV_LINE, points, cats);
- }
- if (parm.lgout)
- lg[col] = (float)length;
- }
- if (parm.lgout)
- Rast_put_f_row(lgfd, lg);
- }
- G_percent (1, 1, 1);
-
- G_free(fls.px);
- G_free(fls.py);
- /* G_free (fls); *//* commented 19/10/99 MN */
- G_free(lg);
- Vect_destroy_line_struct(points);
- Vect_destroy_cats_struct(cats);
- if (parm.lgout)
- Rast_close(lgfd);
- }
- int main(int argc, char *argv[])
- {
- struct GModule *module;
- struct Option *pelevin, *paspin, *pbarin, *pskip, *pbound,
- *pflout, *plgout, *pdsout;
- struct Flag *fup, *flg, *fmem;
- int default_skip, larger, default_bound;
- #ifdef OFFSET
- char *default_offset_ans, *offset_opt;
- #endif
- char *default_skip_ans, *default_bound_ans, *skip_opt;
- struct History history;
- /* Initialize GIS engine */
- G_gisinit(argv[0]);
- module = G_define_module();
- G_add_keyword(_("raster"));
- G_add_keyword(_("hydrology"));
- module->label = _("Constructs flowlines.");
- module->description =
- _("Computes flowlines, flowpath lengths, "
- "and flowaccumulation (contributing areas) from a elevation raster "
- "map.");
- pelevin = G_define_standard_option(G_OPT_R_ELEV);
-
- paspin = G_define_standard_option(G_OPT_R_INPUT);
- paspin->key = "aspect";
- paspin->required = NO;
- paspin->description = _("Name of input aspect raster map");
- paspin->guisection = _("Input maps");
- pbarin = G_define_standard_option(G_OPT_R_INPUT);
- pbarin->key = "barrier";
- pbarin->required = NO;
- pbarin->description = _("Name of input barrier raster map");
- pbarin->guisection = _("Input maps");
- pskip = G_define_option();
- pskip->key = "skip";
- pskip->type = TYPE_INTEGER;
- pskip->required = NO;
- pskip->description = _("Number of cells between flowlines");
- pbound = G_define_option();
- pbound->key = "bound";
- pbound->type = TYPE_INTEGER;
- pbound->required = NO;
- pbound->description = _("Maximum number of segments per flowline");
- pflout = G_define_standard_option(G_OPT_V_OUTPUT);
- pflout->key = "flowline";
- pflout->required = NO;
- pflout->description = _("Name for output flow line vector map");
- pflout->guisection = _("Output maps");
- plgout = G_define_standard_option(G_OPT_R_OUTPUT);
- plgout->key = "flowlength";
- plgout->required = NO;
- plgout->description = _("Name for output flow path length raster map");
- plgout->guisection = _("Output maps");
- pdsout = G_define_standard_option(G_OPT_R_OUTPUT);
- pdsout->key = "flowaccumulation";
- pdsout->required = NO;
- pdsout->description = _("Name for output flow accumulation raster map");
- pdsout->guisection = _("Output maps");
- fup = G_define_flag();
- fup->key = 'u';
- fup->description =
- _("Compute upslope flowlines instead of default downhill flowlines");
- flg = G_define_flag();
- flg->key = '3';
- flg->description = _("3D lengths instead of 2D");
- fmem = G_define_flag();
- fmem->key = 'm';
- fmem->description = _("Use less memory, at a performance penalty");
- if (G_parser(argc, argv))
- exit(EXIT_FAILURE);
- G_get_set_window(®ion);
- larger = ((region.cols < region.rows) ? region.rows : region.cols);
- default_skip = (larger < 50) ? 1 : (int)(larger / 50);
- default_skip_ans =
- G_calloc((int)log10((double)default_skip) + 2, sizeof(char));
- skip_opt = G_calloc((int)log10((double)larger) + 4, sizeof(char));
- sprintf(default_skip_ans, "%d", default_skip);
- sprintf(skip_opt, "1-%d", larger);
- default_bound = (int)(4. * hypot((double)region.rows,
- (double)region.cols));
- default_bound_ans =
- G_calloc((int)log10((double)default_bound) + 4, sizeof(char));
- sprintf(default_bound_ans, "0-%d", default_bound);
- #ifdef OFFSET
- /* below fix changed from 0.0 to 1.0 and its effect disabled in
- * calc.c, Helena June 2005 */
- default_offset = 1.0; /* fixed 20. May 2001 Helena */
- default_offset_ans =
- G_calloc((int)log10(default_offset) + 2, sizeof(char));
- sprintf(default_offset_ans, "%f", default_offset);
- offset_opt = G_calloc((int)log10(default_offset) + 4, sizeof(char));
- sprintf(offset_opt, "0.0-500.0");
- #endif
- if (!pskip->answer)
- pskip->answer = default_skip_ans;
- if (!pbound->answer)
- pbound->answer = default_bound_ans + 2;
- parm.elevin = pelevin->answer;
- parm.aspin = paspin->answer;
- parm.barin = pbarin->answer;
- parm.skip = atoi(pskip->answer);
- parm.bound = atoi(pbound->answer);
- parm.flout = pflout->answer;
- parm.lgout = plgout->answer;
- parm.dsout = pdsout->answer;
- parm.up = fup->answer;
- parm.l3d = flg->answer;
- parm.mem = fmem->answer;
- if (!pflout->answer && !plgout->answer && !pdsout->answer)
- G_fatal_error(_("You must select one or more output maps (flout, lgout, dsout)"));
- if (parm.seg)
- parm.mem = '\0';
- else if (parm.mem)
- parm.aspin = NULL;
- el.name = parm.elevin;
- as.name = (parm.aspin) ? parm.aspin : "internal aspects";
- ds.name = parm.dsout;
- el.row_offset = el.col_offset = 1;
- as.row_offset = as.col_offset = 0;
- ds.row_offset = ds.col_offset = 0;
- if ((G_projection() == PROJECTION_LL)) /* added MN 2005 */
- G_fatal_error(_("lat/long projection not supported by "
- "r.flow. Please use 'r.watershed' for calculating "
- "flow accumulation."));
- if (parm.flout || parm.dsout || parm.lgout) {
- open_output_files();
- allocate_heap();
- read_input_files();
- precompute();
- calculate();
- if (parm.dsout)
- write_density_file();
- close_files();
- deallocate_heap();
- }
- if (parm.dsout) {
- Rast_short_history(parm.dsout, "raster", &history);
- Rast_command_history(&history);
- Rast_write_history(parm.dsout, &history);
- }
- if (parm.lgout) {
- Rast_short_history(parm.lgout, "raster", &history);
- Rast_command_history(&history);
- Rast_write_history(parm.lgout, &history);
- }
- exit(EXIT_SUCCESS);
- }
|