1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165 |
- /****************************************************************************
- *
- * MODULE: r.path
- *
- * AUTHOR(S): based on r.drain
- * Markus Metz
- *
- * PURPOSE: Tracing paths from starting points following
- * input directions.
- * COPYRIGHT: (C) 2017 by the GRASS Development Team
- *
- * This program is free software under the GNU General Public
- * License (>=v2). Read the file COPYING that comes with GRASS
- * for details.
- *
- *****************************************************************************/
- #include <grass/config.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <math.h>
- #include <float.h>
- /* for using the "open" statement */
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- /* for using the close statement */
- #include <unistd.h>
- #include <grass/gis.h>
- #include <grass/raster.h>
- #include <grass/vector.h>
- #include <grass/glocale.h>
- #include "local.h"
- #include "pavlrc.h"
- /* should probably be updated to a pointer array & malloc/realloc as needed */
- #define POINTS_INCREMENT 1024
- /* start points */
- struct point
- {
- int row;
- int col;
- double value;
- struct point *next;
- };
- /* stack points for bitmask directions */
- struct spoint
- {
- int row;
- int col;
- int dir;
- double value;
- struct spoint *next;
- };
- /* output path points */
- struct ppoint
- {
- int row;
- int col;
- double value;
- };
- /* managed list of output path points */
- struct point_list
- {
- struct ppoint *p;
- int n;
- int nalloc;
- };
- int dir_degree(int dir_fd, int val_fd, struct point *startp, struct Cell_head *window,
- struct Map_info *Out, struct point_list *pl, int out_mode);
- int dir_bitmask(int dir_fd, int val_fd, struct point *startp, struct Cell_head *window,
- struct Map_info *Out, struct point_list *pl, int out_mode);
- void pl_add(struct point_list *, struct ppoint *);
- /* comparing path points with qsort */
- int cmp_pp(const void *a, const void *b)
- {
- const struct ppoint *ap = (const struct ppoint *)a;
- const struct ppoint *bp = (const struct ppoint *)b;
- /* 1. ascending by row */
- if (ap->row != bp->row)
- return (ap->row - bp->row);
- /* 2. ascending by col */
- if (ap->col != bp->col)
- return (ap->col - bp->col);
- /* 3. descending by value */
- if (ap->value > bp->value)
- return -1;
- return (ap->value < bp->value);
- }
- int main(int argc, char **argv)
- {
- int fd, dir_fd, val_fd;
- int i, j, have_points = 0;
- int nrows, ncols;
- int npoints;
- int out_id, dir_id, dir_format;
- struct FPRange drange;
- DCELL dmin, dmax;
- char map_name[GNAME_MAX], out_name[GNAME_MAX], dir_name[GNAME_MAX];
- char *tempfile1, *tempfile2;
- struct History history;
- struct Cell_head window;
- struct
- {
- struct Option *dir;
- struct Option *format;
- struct Option *val;
- struct Option *coord;
- struct Option *vpoint;
- struct Option *rast;
- struct Option *vect;
- } opt;
- struct
- {
- struct Flag *copy;
- struct Flag *accum;
- struct Flag *count;
- } flag;
- struct GModule *module;
- void *dir_buf;
- struct point *head_start_pt = NULL;
- struct point *next_start_pt;
- struct point_list pl, *ppl;
- int start_row, start_col, out_mode;
- double east, north;
- struct line_pnts *Points;
- struct line_cats *Cats;
- struct Map_info vout, *pvout;
- char *desc = NULL;
- G_gisinit(argv[0]);
- module = G_define_module();
- G_add_keyword(_("raster"));
- G_add_keyword(_("hydrology"));
- G_add_keyword(_("cost surface"));
- module->description =
- _("Traces paths from starting points following input directions.");
- opt.dir = G_define_standard_option(G_OPT_R_INPUT);
- opt.dir->label = _("Name of input direction");
- opt.dir->description =
- _("Direction in degrees CCW from east, or bitmask encoded");
- opt.format = G_define_option();
- opt.format->type = TYPE_STRING;
- opt.format->key = "format";
- opt.format->label = _("Format of the input direction map");
- opt.format->required = YES;
- opt.format->options = "auto,degree,45degree,bitmask";
- opt.format->answer = "auto";
- G_asprintf(&desc,
- "auto;%s;degree;%s;45degree;%s;bitmask;%s",
- _("auto-detect direction format"),
- _("degrees CCW from East"),
- _("degrees CCW from East divided by 45 (e.g. r.watershed directions)"),
- _("bitmask encoded directions (e.g. r.cost -b)"));
- opt.format->descriptions = desc;
- opt.val = G_define_standard_option(G_OPT_R_INPUT);
- opt.val->key = "values";
- opt.val->label =
- _("Name of input raster values to be used for output");
- opt.val->required = NO;
- opt.rast = G_define_standard_option(G_OPT_R_OUTPUT);
- opt.rast->key = "raster_path";
- opt.rast->required = NO;
- opt.rast->label = _("Name for output raster path map");
-
- opt.vect = G_define_standard_option(G_OPT_V_OUTPUT);
- opt.vect->key = "vector_path";
- opt.vect->required = NO;
- opt.vect->label = _("Name for output vector path map");
-
- opt.coord = G_define_standard_option(G_OPT_M_COORDS);
- opt.coord->key = "start_coordinates";
- opt.coord->multiple = YES;
- opt.coord->description = _("Coordinates of starting point(s) (E,N)");
- opt.coord->guisection = _("Start");
- opt.vpoint = G_define_standard_option(G_OPT_V_INPUTS);
- opt.vpoint->key = "start_points";
- opt.vpoint->required = NO;
- opt.vpoint->label = _("Name of starting vector points map(s)");
- opt.vpoint->guisection = _("Start");
- flag.copy = G_define_flag();
- flag.copy->key = 'c';
- flag.copy->description = _("Copy input cell values on output");
- flag.copy->guisection = _("Path settings");
- flag.accum = G_define_flag();
- flag.accum->key = 'a';
- flag.accum->description = _("Accumulate input values along the path");
- flag.accum->guisection = _("Path settings");
- flag.count = G_define_flag();
- flag.count->key = 'n';
- flag.count->description = _("Count cell numbers along the path");
- flag.count->guisection = _("Path settings");
- G_option_required(opt.rast, opt.vect, NULL);
- G_option_exclusive(flag.copy, flag.accum, flag.count, NULL);
- G_option_requires_all(flag.copy, opt.rast, opt.val, NULL);
- G_option_requires_all(flag.accum, opt.rast, opt.val, NULL);
- G_option_requires_all(flag.count, opt.rast, NULL);
- if (G_parser(argc, argv))
- exit(EXIT_FAILURE);
- strcpy(dir_name, opt.dir->answer);
- *map_name = '\0';
- *out_name = '\0';
- if (opt.rast->answer) {
- strcpy(out_name, opt.rast->answer);
- if (opt.val->answer)
- strcpy(map_name, opt.val->answer);
- }
- pvout = NULL;
- if (opt.vect->answer) {
- if (0 > Vect_open_new(&vout, opt.vect->answer, 0)) {
- G_fatal_error(_("Unable to create vector map <%s>"),
- opt.vect->answer);
- }
- Vect_hist_command(&vout);
- pvout = &vout;
- }
- if (flag.copy->answer)
- out_mode = OUT_CPY;
- else if (flag.accum->answer)
- out_mode = OUT_ACC;
- else if (flag.count->answer)
- out_mode = OUT_CNT;
- else
- out_mode = OUT_PID;
- /* get the window information */
- G_get_window(&window);
- nrows = Rast_window_rows();
- ncols = Rast_window_cols();
- npoints = 0;
- /* TODO: use r.cost method to create a list of start points */
- if (opt.coord->answer) {
- for (i = 0; opt.coord->answers[i] != NULL; i += 2) {
- G_scan_easting(opt.coord->answers[i], &east, G_projection());
- G_scan_northing(opt.coord->answers[i + 1], &north, G_projection());
- start_col = (int)Rast_easting_to_col(east, &window);
- start_row = (int)Rast_northing_to_row(north, &window);
- if (start_row < 0 || start_row > nrows ||
- start_col < 0 || start_col > ncols) {
- G_warning(_("Starting point %d is outside the current region"),
- i + 1);
- continue;
- }
- npoints++;
- have_points = 1;
- next_start_pt =
- (struct point *)(G_malloc(sizeof(struct point)));
- next_start_pt->row = start_row;
- next_start_pt->col = start_col;
- next_start_pt->value = npoints;
- next_start_pt->next = head_start_pt;
- head_start_pt = next_start_pt;
- }
- }
- if (opt.vpoint->answers) {
- for (i = 0; opt.vpoint->answers[i] != NULL; i++) {
- struct Map_info In;
- struct bound_box box;
- int cat, type;
- Points = Vect_new_line_struct();
- Cats = Vect_new_cats_struct();
- Vect_set_open_level(1); /* topology not required */
- if (1 > Vect_open_old(&In, opt.vpoint->answers[i], ""))
- G_fatal_error(_("Unable to open vector map <%s>"), opt.vpoint->answers[i]);
- G_verbose_message(_("Reading vector map <%s> with start points..."),
- Vect_get_full_name(&In));
-
- Vect_rewind(&In);
- Vect_region_box(&window, &box);
- box.T = box.B = 0;
- while (1) {
- /* register line */
- type = Vect_read_next_line(&In, Points, Cats);
- /* Note: check for dead lines is not needed, because they are skipped by V1_read_next_line_nat() */
- if (type == -1) {
- G_warning(_("Unable to read vector map"));
- continue;
- }
- else if (type == -2) {
- break;
- }
- if (!Vect_point_in_box(Points->x[0], Points->y[0], 0, &box))
- continue;
- start_col = (int)Rast_easting_to_col(Points->x[0], &window);
- start_row = (int)Rast_northing_to_row(Points->y[0], &window);
- /* effectively just a duplicate check to G_site_in_region() ??? */
- if (start_row < 0 || start_row > nrows || start_col < 0 ||
- start_col > ncols)
- continue;
- npoints++;
- have_points = 1;
- next_start_pt =
- (struct point *)(G_malloc(sizeof(struct point)));
- next_start_pt->row = start_row;
- next_start_pt->col = start_col;
- Vect_cat_get(Cats, 1, &cat);
- next_start_pt->value = cat;
- next_start_pt->next = head_start_pt;
- head_start_pt = next_start_pt;
- }
- Vect_close(&In);
- /* only catches maps out of range until something is found, not after */
- if (!have_points) {
- G_warning(_("Starting vector map <%s> contains no points in the current region"),
- opt.vpoint->answers[i]);
- }
- Vect_destroy_line_struct(Points);
- Vect_destroy_cats_struct(Cats);
- }
- }
- if (have_points == 0)
- G_fatal_error(_("No start point(s) specified"));
- /* determine the drainage paths */
- /* get some temp files */
- val_fd = -1;
- tempfile1 = NULL;
- if (opt.val->answer && opt.rast->answer) {
- DCELL *map_buf;
- G_verbose_message(_("Reading raster values map <%s> ..."), map_name);
- tempfile1 = G_tempfile();
- val_fd = open(tempfile1, O_RDWR | O_CREAT, 0666);
- map_buf = Rast_allocate_d_buf();
- fd = Rast_open_old(map_name, "");
- /* transfer the values map to a temp file */
- for (i = 0; i < nrows; i++) {
- Rast_get_d_row(fd, map_buf, i);
- if (write(val_fd, map_buf, ncols * sizeof(DCELL)) !=
- ncols * sizeof(DCELL)) {
- G_fatal_error(_("Unable to write to tempfile"));
- }
- }
- Rast_close(fd);
- G_free(map_buf);
- }
- if (Rast_read_fp_range(dir_name, "", &drange) < 0)
- G_fatal_error(_("Unable to read range file"));
- Rast_get_fp_range_min_max(&drange, &dmin, &dmax);
- if (dmax <= 0)
- G_fatal_error(_("Invalid directions map <%s>"), dir_name);
- dir_format = -1;
- if (strcmp(opt.format->answer, "degree") == 0) {
- if (dmax > 360)
- G_fatal_error(_("Directional degrees can not be > 360"));
- dir_format = DIR_DEG;
- }
- else if (strcmp(opt.format->answer, "45degree") == 0) {
- if (dmax > 8)
- G_fatal_error(_("Directional degrees divided by 45 can not be > 8"));
- dir_format = DIR_DEG45;
- }
- else if (strcmp(opt.format->answer, "bitmask") == 0) {
- if (dmax > (1 << 16) - 1)
- G_fatal_error(_("Bitmask encoded directions can not be > %d"), (1 << 16) - 1);
- dir_format = DIR_BIT;
- }
- else if (strcmp(opt.format->answer, "auto") == 0) {
- if (dmax <= 8) {
- dir_format = DIR_DEG45;
- G_important_message(_("Input direction format assumed to be degrees CCW from East divided by 45"));
- }
- else if (dmax <= (1 << 8) - 1) {
- dir_format = DIR_BIT;
- G_important_message(_("Input direction format assumed to be bitmask encoded without Knight's move"));
- }
- else if (dmax <= 360) {
- dir_format = DIR_DEG;
- G_important_message(_("Input direction format assumed to be degrees CCW from East"));
- }
- else if (dmax <= (1 << 16) - 1) {
- dir_format = DIR_BIT;
- G_important_message(_("Input direction format assumed to be bitmask encoded with Knight's move"));
- }
- else
- G_fatal_error(_("Unable to detect format of input direction map <%s>"), dir_name);
- }
- if (dir_format <= 0)
- G_fatal_error(_("Invalid directions format '%s'"), opt.format->answer);
- G_verbose_message(_("Reading direction map <%s> ..."), dir_name);
- dir_id = Rast_open_old(dir_name, "");
- tempfile2 = G_tempfile();
- dir_fd = open(tempfile2, O_RDWR | O_CREAT, 0666);
- if (dir_format == DIR_BIT) {
- dir_buf = Rast_allocate_c_buf();
- for (i = 0; i < nrows; i++) {
- Rast_get_c_row(dir_id, dir_buf, i);
- if (write(dir_fd, dir_buf, ncols * sizeof(CELL)) !=
- ncols * sizeof(CELL)) {
- G_fatal_error(_("Unable to write to tempfile"));
- }
- }
- }
- else {
- dir_buf = Rast_allocate_d_buf();
- for (i = 0; i < nrows; i++) {
- Rast_get_d_row(dir_id, dir_buf, i);
- if (dir_format == DIR_DEG45) {
- DCELL *dp;
- dp = (DCELL *)dir_buf;
- for (j = 0; j < ncols; j++, dp++)
- *dp *= 45;
- }
- if (write(dir_fd, dir_buf, ncols * sizeof(DCELL)) !=
- ncols * sizeof(DCELL)) {
- G_fatal_error(_("Unable to write to tempfile"));
- }
- }
- }
- Rast_close(dir_id);
- G_free(dir_buf);
- ppl = NULL;
- if (opt.rast->answer) {
- /* raster output */
- pl.p = NULL;
- pl.n = 0;
- pl.nalloc = 0;
- ppl = &pl;
- }
- /* determine the drainage paths */
- /* repeat for each starting point */
- G_verbose_message(_("Processing start points..."));
- next_start_pt = head_start_pt;
- while (next_start_pt) {
- /* follow directions from start points to determine paths */
- /* path tracing algorithm selection */
- if (dir_format == DIR_BIT) {
- struct Map_info Tmp;
- if (pvout) {
- if (Vect_open_tmp_new(&Tmp, NULL, 0) < 0)
- G_fatal_error(_("Unable to create temporary vector map"));
- pvout = &Tmp;
- }
- if (!dir_bitmask(dir_fd, val_fd, next_start_pt, &window,
- pvout, ppl, out_mode)) {
- G_warning(_("No path at row %d, col %d"),
- next_start_pt->row, next_start_pt->col);
- }
- if (pvout) {
- Vect_build_partial(&Tmp, GV_BUILD_BASE);
- G_message(_("Breaking lines..."));
- Vect_break_lines(&Tmp, GV_LINE, NULL);
- Vect_copy_map_lines(&Tmp, &vout);
- Vect_set_release_support(&Tmp);
- Vect_close(&Tmp); /* temporary map is deleted automatically */
- pvout = &vout;
- }
- }
- else {
- if (!dir_degree(dir_fd, val_fd, next_start_pt, &window,
- pvout, ppl, out_mode)) {
- G_warning(_("No path at row %d, col %d"),
- next_start_pt->row, next_start_pt->col);
- }
- }
- next_start_pt = next_start_pt->next;
- }
- /* raster output */
- if (opt.rast->answer) {
- int row;
- if (pl.n > 1) {
- /* sort points */
- qsort(pl.p, pl.n, sizeof(struct ppoint), cmp_pp);
- /* remove duplicates */
- j = 1;
- for (i = 1; i < pl.n; i++) {
- if (pl.p[j - 1].row != pl.p[i].row ||
- pl.p[j - 1].col != pl.p[i].col) {
-
- pl.p[j].row = pl.p[i].row;
- pl.p[j].col = pl.p[i].col;
- pl.p[j].value = pl.p[i].value;
- j++;
- }
- }
- pl.n = j;
- }
- if (out_mode == OUT_PID || out_mode == OUT_CNT) {
- CELL *out_buf;
- /* Output will be a cell map */
- /* open a new file and allocate an output buffer */
- out_id = Rast_open_c_new(out_name);
- out_buf = Rast_allocate_c_buf();
- Rast_set_c_null_value(out_buf, window.cols);
- row = 0;
- /* build the output map */
- G_message(_("Writing output raster map..."));
- for (i = 0; i < pl.n; i++) {
- while (row < pl.p[i].row) {
- G_percent(row, nrows, 2);
- Rast_put_c_row(out_id, out_buf);
- Rast_set_c_null_value(out_buf, window.cols);
- row++;
- }
- out_buf[pl.p[i].col] = pl.p[i].value;
- }
- while (row < window.rows) {
- G_percent(row, nrows, 2);
- Rast_put_c_row(out_id, out_buf);
- Rast_set_c_null_value(out_buf, window.cols);
- row++;
- }
- G_percent(1, 1, 1);
- G_free(out_buf);
- }
- else { /* mode = OUT_CPY or OUT_ACC */
- DCELL *out_buf;
- /* Output could be of the same type as input */
- /* open a new file and allocate an output buffer */
- out_id = Rast_open_new(out_name, DCELL_TYPE);
- out_buf = Rast_allocate_d_buf();
- Rast_set_d_null_value(out_buf, window.cols);
- row = 0;
- /* build the output map */
- G_message(_("Writing output raster map..."));
- for (i = 0; i < pl.n; i++) {
- while (row < pl.p[i].row) {
- G_percent(row, nrows, 2);
- Rast_put_d_row(out_id, out_buf);
- Rast_set_d_null_value(out_buf, window.cols);
- row++;
- }
- out_buf[pl.p[i].col] = pl.p[i].value;
- }
- while (row < window.rows) {
- G_percent(row, nrows, 2);
- Rast_put_d_row(out_id, out_buf);
- Rast_set_d_null_value(out_buf, window.cols);
- row++;
- }
- G_percent(1, 1, 1);
- G_free(out_buf);
- }
- Rast_close(out_id);
- Rast_put_cell_title(out_name, "Path trace");
- Rast_short_history(out_name, "raster", &history);
- Rast_command_history(&history);
- Rast_write_history(out_name, &history);
- }
- /* vector output */
- if (opt.vect->answer) {
- Vect_build(&vout);
- Vect_close(&vout);
- }
- /* close files and free buffers */
- close(dir_fd);
- unlink(tempfile2);
- if (opt.val->answer && opt.rast->answer) {
- close(val_fd);
- unlink(tempfile1);
- }
- G_done_msg(" ");
- exit(EXIT_SUCCESS);
- }
- void pl_add(struct point_list *pl, struct ppoint *p)
- {
- if (pl->n == pl->nalloc) {
- pl->nalloc += POINTS_INCREMENT;
- pl->p = (struct ppoint *)G_realloc(pl->p, pl->nalloc * sizeof(struct ppoint));
- if (!pl->p)
- G_fatal_error(_("Unable to increase point list"));
- }
- pl->p[pl->n] = *p;
- pl->n++;
- }
- /* Jenson Domingue 1988, r.fill.dir:
- * bitmask encoded directions CW from North
- * value log2(value) direction
- * 1 0 NE
- * 2 1 E
- * 4 2 SE
- * 8 3 S
- * 16 4 SW
- * 32 5 W
- * 64 6 NW
- * 128 7 N
- * 256 8 NEN
- * 512 9 ENE
- * 1024 10 ESE
- * 2048 11 SES
- * 4096 12 SWS
- * 8192 13 WSW
- * 16384 14 WNW
- * 32768 15 NWN
- *
- *
- * 8 neighbours
- * 64 128 1
- * 32 x 2
- * 16 8 4
- *
- * log2(value) CW from North starting at NE
- * expanded for Knight's move
- *
- * 15 8
- * 14 6 7 0 9
- * 5 X 1
- * 13 4 3 2 10
- * 12 11
- */
- int dir_bitmask(int dir_fd, int val_fd, struct point *startp, struct Cell_head *window,
- struct Map_info *Out, struct point_list *pl, int out_mode)
- {
- int go = 1, next_row, next_col, next_dir;
- int npoints;
- int dir_row = -1, val_row = -1;
- CELL direction;
- CELL *dir_buf;
- DCELL *val_buf;
- struct spoint *stackp, *newp;
- struct pavlrc_table *visited;
- struct pavlrc ngbr_rc;
- struct ppoint pp;
- int is_stack;
- int cur_dir, i, npaths;
- struct line_pnts *Points;
- struct line_cats *Cats;
- double x, y;
- double value;
- int col_offset[16] = { 1, 1, 1, 0, -1, -1, -1, 0,
- 1, 2, 2, 1, -1, -2, -2, -1 };
- int row_offset[16] = { -1, 0, 1, 1, 1, 0, -1, -1,
- -2, -1, 1, 2, 2, 1, -1, -2 };
- dir_buf = Rast_allocate_c_buf();
- val_buf = NULL;
- stackp = (struct spoint *)G_malloc(sizeof(struct spoint));
- stackp->row = startp->row;
- stackp->col = startp->col;
- stackp->value = startp->value;
- value = startp->value;
- stackp->dir = -1;
- stackp->next = NULL;
- visited = pavlrc_create(NULL);
- ngbr_rc.row = stackp->row;
- ngbr_rc.col = stackp->col;
- pavlrc_insert(visited, &ngbr_rc);
- if (Out) {
- Points = Vect_new_line_struct();
- Cats = Vect_new_cats_struct();
- Vect_cat_set(Cats, 1, (int)stackp->value);
- }
- if (pl) {
- if (out_mode == OUT_CNT)
- value = 1;
- else if (out_mode == OUT_CPY || out_mode == OUT_ACC) {
- /* read input raster */
- val_buf = Rast_allocate_d_buf();
- if (val_row != stackp->row) {
- lseek(val_fd, (off_t) stackp->row * window->cols * sizeof(DCELL),
- SEEK_SET);
- if (read(val_fd, val_buf, window->cols * sizeof(DCELL)) !=
- window->cols * sizeof(DCELL)) {
- G_fatal_error(_("Unable to read from temp file"));
- }
- val_row = stackp->row;
- }
- value = val_buf[stackp->col];
- }
- stackp->value = value;
- }
- /* multi-path tracing:
- * multiple paths from the same starting point */
- npoints = 0;
- while (stackp) {
- is_stack = 1;
- stackp->dir++;
- next_row = stackp->row;
- next_col = stackp->col;
- value = stackp->value;
- /* begin loop */
- go = 1;
- while (go) {
- go = 0;
- /* find direction from this point */
- if (dir_row != next_row) {
- lseek(dir_fd, (off_t) next_row * window->cols * sizeof(CELL),
- SEEK_SET);
- if (read(dir_fd, dir_buf, window->cols * sizeof(CELL)) !=
- window->cols * sizeof(CELL)) {
- G_fatal_error(_("Unable to read from temp file"));
- }
- dir_row = next_row;
- }
- direction = *(dir_buf + next_col);
-
- if (direction <= 0 || Rast_is_c_null_value(&direction)) {
- /* end of current path: write out the result */
- if (Out && Points->n_points > 1) {
- Vect_write_line(Out, GV_LINE, Points, Cats);
- }
- /* leave this loop */
- if (is_stack) {
- newp = stackp->next;
- G_free(stackp);
- stackp = newp;
- }
- break;
- }
- /* start direction */
- cur_dir = 0;
- if (is_stack)
- cur_dir = stackp->dir;
-
- /* count paths going from current point and
- * get next direction as log2(direction) */
- next_dir = -1;
- npaths = 0;
- for (i = cur_dir; i < 16; i++) {
- if ((direction & (1 << i)) != 0) {
- npaths++;
- if (next_dir < 0)
- next_dir = i;
- }
- }
- if (is_stack) {
- if (npaths > 0) {
- stackp->dir = next_dir;
- /* start a new path */
- if (Out) {
- Vect_reset_line(Points);
- x = window->west + (next_col + 0.5) * window->ew_res;
- y = window->north - (next_row + 0.5) * window->ns_res;
- Vect_append_point(Points, x, y, 0.0);
- }
- if (pl) {
- value = stackp->value;
- pp.row = next_row;
- pp.col = next_col;
- pp.value = value;
- pl_add(pl, &pp);
- }
- npoints++;
- }
- else {
- /* stack point without path ? */
- if (stackp->dir == 0)
- G_warning(_("No path from row %d, col %d"),
- stackp->row, stackp->col);
- /* drop this point from the stack */
- G_debug(1, "drop point from stack");
- newp = stackp->next;
- G_free(stackp);
- stackp = newp;
- break;
- }
- }
- else { /* not a stack point */
- if (npaths == 0) {
- /* should not happen */
- G_fatal_error(_("Invalid direction %d"), direction);
- }
- if (npaths > 1) {
- /* write out the result */
- if (Out && Points->n_points > 1) {
- Vect_write_line(Out, GV_LINE, Points, Cats);
- }
- ngbr_rc.row = next_row;
- ngbr_rc.col = next_col;
- /* add it to the stack and leave this loop */
- G_debug(1, "add point to stack: row %d, col %d, dir %d",
- next_row, next_col, next_dir);
- newp = (struct spoint *)G_malloc(sizeof(struct spoint));
- newp->row = next_row;
- newp->col = next_col;
- newp->dir = next_dir - 1;
- newp->value = value;
- newp->next = stackp;
- stackp = newp;
- break;
- }
- }
- is_stack = 0;
- /* identify next downstream cell */
- next_row += row_offset[next_dir];
- next_col += col_offset[next_dir];
- G_debug(1, "next cell at row %d, col %d", next_row, next_col);
- if (next_col >= 0 && next_col < window->cols
- && next_row >= 0 && next_row < window->rows) {
-
- /* add point */
- if (Out) {
- x = window->west + (next_col + 0.5) * window->ew_res;
- y = window->north - (next_row + 0.5) * window->ns_res;
- Vect_append_point(Points, x, y, 0.0);
- }
- if (pl) {
- if (out_mode == OUT_CNT)
- value += 1;
- else if (out_mode == OUT_CPY || out_mode == OUT_ACC) {
- /* read input raster */
- if (val_row != next_row) {
- lseek(val_fd, (off_t) next_row * window->cols * sizeof(DCELL),
- SEEK_SET);
- if (read(val_fd, val_buf, window->cols * sizeof(DCELL)) !=
- window->cols * sizeof(DCELL)) {
- G_fatal_error(_("Unable to read from temp file"));
- }
- val_row = next_row;
- }
-
- if (out_mode == OUT_CPY)
- value = val_buf[next_col];
- else
- value += val_buf[next_col];
- }
- pp.row = next_row;
- pp.col = next_col;
- pp.value = value;
- pl_add(pl, &pp);
- }
- /* avoid circular paths
- * avoid tracing the same path segment several times:
- * a path can split and later on merge again,
- * then split again, merge again
- * path segments are identical from every merge point
- * to the next split point */
- ngbr_rc.row = next_row;
- ngbr_rc.col = next_col;
- if (!pavlrc_insert(visited, &ngbr_rc)) {
- go = 1;
- npoints++;
- }
- else {
- /* reached a merge point */
- /* write out the result */
- if (Out && Points->n_points > 1) {
- Vect_write_line(Out, GV_LINE, Points, Cats);
- }
- }
- }
- else {
- G_warning(_("Path is leaving the current region"));
- }
- } /* end while */
- }
- pavlrc_destroy(visited);
- G_free(dir_buf);
- if (val_buf)
- G_free(val_buf);
- if (Out) {
- Vect_destroy_line_struct(Points);
- Vect_destroy_cats_struct(Cats);
- }
- return (npoints > 1);
- }
- int dir_degree(int dir_fd, int val_fd, struct point *startp, struct Cell_head *window,
- struct Map_info *Out, struct point_list *pl, int out_mode)
- {
- /*
- * The idea is that each cell of the direction surface has a value representing
- * the direction towards the next cell in the path. The direction is read from
- * the input raster, and a simple case/switch is used to determine which cell to
- * read next. This is repeated via a while loop until a null direction is found.
- *
- * directions are degrees CCW from East with East = 360
- */
- int neighbour, next_row, next_col, go = 1;
- int npoints;
- int dir_row = -1, val_row = -1;
- DCELL direction;
- DCELL *dir_buf;
- DCELL *val_buf;
- double x, y;
- double value;
- struct ppoint pp;
- struct line_pnts *Points;
- struct line_cats *Cats;
- dir_buf = Rast_allocate_d_buf();
- val_buf = NULL;
- next_row = startp->row;
- next_col = startp->col;
- value = startp->value;
- if (Out) {
- Points = Vect_new_line_struct();
- Cats = Vect_new_cats_struct();
- Vect_cat_set(Cats, 1, (int)value);
- x = window->west + (next_col + 0.5) * window->ew_res;
- y = window->north - (next_row + 0.5) * window->ns_res;
- Vect_append_point(Points, x, y, 0.0);
- }
- if (pl) {
- if (out_mode == OUT_CNT)
- value = 1;
- else if (out_mode == OUT_CPY || out_mode == OUT_ACC) {
- /* read input raster */
- val_buf = Rast_allocate_d_buf();
- if (val_row != next_row) {
- lseek(val_fd, (off_t) next_row * window->cols * sizeof(DCELL),
- SEEK_SET);
- if (read(val_fd, val_buf, window->cols * sizeof(DCELL)) !=
- window->cols * sizeof(DCELL)) {
- G_fatal_error(_("Unable to read from temp file"));
- }
- val_row = next_row;
- }
- value = val_buf[next_col];
- }
- pp.row = next_row;
- pp.col = next_col;
- pp.value = value;
- pl_add(pl, &pp);
- }
- npoints = 1;
- while (go) {
- go = 0;
- /* Directional algorithm
- * 1) read cell direction
- * 2) shift to cell in that direction
- */
- /* find the direction recorded at row,col */
- if (dir_row != next_row) {
- lseek(dir_fd, (off_t) next_row * window->cols * sizeof(DCELL),
- SEEK_SET);
- if (read(dir_fd, dir_buf, window->cols * sizeof(DCELL)) !=
- window->cols * sizeof(DCELL)) {
- G_fatal_error(_("Unable to read from temp file"));
- }
- dir_row = next_row;
- }
- direction = *(dir_buf + next_col);
- neighbour = 0;
- if (!Rast_is_d_null_value(&direction)) {
- neighbour = direction * 10;
- G_debug(2, "direction read: %lf, neighbour found: %i",
- direction, neighbour);
- }
- switch (neighbour) {
- case 225: /* ENE */
- next_row -= 1;
- next_col += 2;
- break;
- case 450: /* NE */
- next_row -= 1;
- next_col += 1;
- break;
- case 675: /* NNE */
- next_row -= 2;
- next_col += 1;
- break;
- case 900: /* N */
- next_row -= 1;
- break;
- case 1125: /* NNW */
- next_row -= 2;
- next_col -= 1;
- break;
- case 1350: /* NW */
- next_col -= 1;
- next_row -= 1;
- break;
- case 1575: /* WNW */
- next_col -= 2;
- next_row -= 1;
- break;
- case 1800: /* W*/
- next_col -= 1;
- break;
- case 2025: /* WSW */
- next_row += 1;
- next_col -= 2;
- break;
- case 2250: /* SW */
- next_row += 1;
- next_col -= 1;
- break;
- case 2475: /* SSW */
- next_row += 2;
- next_col -= 1;
- break;
- case 2700: /* S */
- next_row += 1;
- break;
- case 2925: /* SSE */
- next_row += 2;
- next_col += 1;
- break;
- case 3150: /* SE */
- next_row += 1;
- next_col += 1;
- break;
- case 3375: /* ESE */
- next_row += 1;
- next_col += 2;
- break;
- case 3600: /* E */
- next_col += 1;
- break;
- default:
- /* end of path */
- next_row = -1;
- next_col = -1;
- break;
- } /* end switch/case */
- if (next_col >= 0 && next_col < window->cols && next_row >= 0 &&
- next_row < window->rows) {
- if (Out) {
- x = window->west + (next_col + 0.5) * window->ew_res;
- y = window->north - (next_row + 0.5) * window->ns_res;
- Vect_append_point(Points, x, y, 0.0);
- }
- if (pl) {
- if (out_mode == OUT_CNT)
- value += 1;
- else if (out_mode == OUT_CPY || out_mode == OUT_ACC) {
- /* read input raster */
- if (val_row != next_row) {
- lseek(val_fd, (off_t) next_row * window->cols * sizeof(DCELL),
- SEEK_SET);
- if (read(val_fd, val_buf, window->cols * sizeof(DCELL)) !=
- window->cols * sizeof(DCELL)) {
- G_fatal_error(_("Unable to read from temp file"));
- }
- val_row = next_row;
- }
-
- if (out_mode == OUT_CPY)
- value = val_buf[next_col];
- else
- value += val_buf[next_col];
- }
- pp.row = next_row;
- pp.col = next_col;
- pp.value = value;
- pl_add(pl, &pp);
- }
- go = 1;
- npoints++;
- }
- } /* end while */
- if (Out && Points->n_points > 1) {
- Vect_write_line(Out, GV_LINE, Points, Cats);
- }
- G_free(dir_buf);
- if (val_buf)
- G_free(val_buf);
- if (Out) {
- Vect_destroy_line_struct(Points);
- Vect_destroy_cats_struct(Cats);
- }
- return (npoints > 1);
- }
|