/**************************************************************************** * COPYRIGHT: (C) 2000 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. * *****************************************************************************/ /* This is the original attribution from r.drain */ /************************************************************************ * * * This is the main program for tracing out the path that a * * drop of water would take if released at a certain location * * on an input elevation map. The program was written by * * Kewan Q. Khawaja * * kewan@techlogix.com * * * * update to FP (2000): Pierre de Mouveaux * * bugfix in FCELL, DCELL: Markus Neteler 12/2000 * *************************************************************************/ /************************************************************************ * Rewritten by Roger Miller 7/2001 based on subroutines from * * r.fill.dir and on the original r.drain. * * * * 24 July 2004: WebValley 2004, error checking and vector points added by * * Matteo Franchi Liceo Leonardo Da Vinci Trento * * Roberto Flor ITC-irst * *************************************************************************/ #include #include #include #include #include /* for using the "open" statement */ #include #include #include /* for using the close statement */ #include #include #include #include #define DEBUG #include "tinf.h" #include "local.h" /* should probably be updated to a pointer array & malloc/realloc as needed */ #define MAX_POINTS 1024 /* define a data structure to hold the point data */ struct point { int row; int col; struct point *next; double value; }; int main(int argc, char **argv) { int fe, fd; int i, have_points = 0; int new_id; int nrows, ncols, points_row[MAX_POINTS], points_col[MAX_POINTS], npoints; int cell_open(), cell_open_new(); int map_id; char map_name[GNAME_MAX], *map_mapset, new_map_name[GNAME_MAX]; char *tempfile1, *tempfile2; char *search_mapset; struct Cell_head window; struct Option *opt1, *opt2, *coordopt, *vpointopt; struct Flag *flag1, *flag2, *flag3; struct GModule *module; int in_type; void *in_buf; CELL *out_buf; struct band3 bnd, bndC; struct metrics *m = NULL; struct point *list; struct point *thispoint; int ival, bsz, start_row, start_col, mode; double east, north, val; struct point *drain(int, struct point *, int, int); int bsort(int, struct point *); G_gisinit(argv[0]); module = G_define_module(); module->keywords = _("raster"); module->description = _("Traces a flow through an elevation model on a raster map layer."); opt1 = G_define_standard_option(G_OPT_R_INPUT); opt1->description = _("Name of existing raster map containing elevation surface"); opt2 = G_define_standard_option(G_OPT_R_OUTPUT); opt2->description = _("Output drain raster map"); coordopt = G_define_option(); coordopt->key = "coordinate"; coordopt->type = TYPE_STRING; coordopt->required = NO; coordopt->multiple = YES; coordopt->key_desc = "x,y"; coordopt->description = _("The E and N coordinates of starting point(s)"); vpointopt = G_define_option(); vpointopt->key = "vector_points"; vpointopt->type = TYPE_STRING; vpointopt->required = NO; vpointopt->multiple = YES; vpointopt->gisprompt= "old,vector,vector"; vpointopt->key_desc = "name"; vpointopt->description = _("Vector map(s) containing starting point(s)"); flag1 = G_define_flag(); flag1->key = 'c'; flag1->description = _("Copy input cell values on output"); flag2 = G_define_flag(); flag2->key = 'a'; flag2->description = _("Accumulate input values along the path"); flag3 = G_define_flag(); flag3->key = 'n'; flag3->description = _("Count cell numbers along the path"); if (G_parser(argc, argv)) exit(EXIT_FAILURE); strcpy(map_name, opt1->answer); strcpy(new_map_name, opt2->answer); /* get the name of the elevation map layer for filling */ map_mapset = G_find_cell(map_name, ""); if (!map_mapset) G_fatal_error(_("Could not access %s layer"), map_name); /* allocate cell buf for the map layer */ in_type = G_raster_map_type(map_name, map_mapset); /* set the pointers for multi-typed functions */ set_func_pointers(in_type); if( (flag1->answer + flag2->answer + flag3->answer) > 1 ) G_fatal_error(_("Specify just one of the -c, -a and -n flags")); mode = 0; if (flag1->answer) mode = 1; if (flag2->answer) mode = 2; if (flag3->answer) mode = 3; /* get the window information */ G_get_window(&window); nrows = G_window_rows(); ncols = G_window_cols(); /* calculate true cell resolution */ m = (struct metrics *)G_malloc(nrows * sizeof(struct metrics)); if (m == NULL) G_fatal_error(_("Metrics allocation")); npoints = 0; if (coordopt->answer) { for (i = 0; coordopt->answers[i] != NULL; i += 2) { G_scan_easting(coordopt->answers[i], &east, G_projection()); G_scan_northing(coordopt->answers[i + 1], &north, G_projection()); start_col = (int)G_easting_to_col(east, &window); start_row = (int)G_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; } points_row[npoints] = start_row; points_col[npoints] = start_col; npoints++; if(npoints >= MAX_POINTS) G_fatal_error(_("Too many start points.")); have_points = 1; } } if (vpointopt->answer) { for (i = 0; vpointopt->answers[i] != NULL; i++) { FILE *fp; /* struct start_pt *new_start_pt; */ Site *site = NULL; /* pointer to Site */ int dims, strs, dbls; RASTER_MAP_TYPE cat; search_mapset = G_find_sites(vpointopt->answers[i], ""); if (search_mapset == NULL) G_fatal_error(_("Vector map %s - not found"), vpointopt->answers[i]); fp = G_fopen_sites_old(vpointopt->answers[i], search_mapset); if (0 != G_site_describe( fp, &dims, &cat, &strs, &dbls)) G_fatal_error(_("Failed to guess site file format")); site = G_site_new_struct(cat, dims, strs, dbls); for (; (G_site_get(fp, site) != EOF);) { if (!G_site_in_region(site, &window)) continue; start_col = (int)G_easting_to_col(site->east, &window); start_row = (int)G_northing_to_row(site->north, &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; points_row[npoints] = start_row; points_col[npoints] = start_col; npoints++; if(npoints >= MAX_POINTS) G_fatal_error(_("Too many start points.")); have_points = 1; } /* 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."), vpointopt->answers[i]); } } } if (have_points == 0) G_fatal_error(_("No start/stop point(s) specified")); /* determine the drainage paths */ /* allocate storage for the first point */ thispoint = (struct point *)G_malloc(sizeof(struct point)); list = thispoint; thispoint->next = NULL; G_begin_distance_calculations(); { double e1, n1, e2, n2; e1 = window.east; n1 = window.north; e2 = e1 + window.ew_res; n2 = n1 - window.ns_res; for (i = 0; i < nrows; i++) { m[i].ew_res = G_distance(e1, n1, e2, n1); m[i].ns_res = G_distance(e1, n1, e1, n2); m[i].diag_res = G_distance(e1, n1, e2, n2); e2 = e1 + window.ew_res; n2 = n1 - window.ns_res; } } /* buffers for internal use */ bndC.ns = ncols; bndC.sz = sizeof(CELL) * ncols; bndC.b[0] = G_calloc(ncols, sizeof(CELL)); bndC.b[1] = G_calloc(ncols, sizeof(CELL)); bndC.b[2] = G_calloc(ncols, sizeof(CELL)); /* buffers for external use */ bnd.ns = ncols; bnd.sz = ncols * bpe(); bnd.b[0] = G_calloc(ncols, bpe()); bnd.b[1] = G_calloc(ncols, bpe()); bnd.b[2] = G_calloc(ncols, bpe()); /* an input buffer */ in_buf = get_buf(); /* open the original map and get its file id */ map_id = G_open_cell_old(map_name, map_mapset); /* get some temp files */ tempfile1 = G_tempfile(); tempfile2 = G_tempfile(); fe = open(tempfile1, O_RDWR | O_CREAT); fd = open(tempfile2, O_RDWR | O_CREAT); /* transfer the input map to a temp file */ for (i = 0; i < nrows; i++) { get_row(map_id, in_buf, i); write(fe, in_buf, bnd.sz); } G_close_cell(map_id); /* fill one-cell pits and take a first stab at flow directions */ filldir(fe, fd, nrows, &bnd, m); /* determine flow directions for more ambiguous cases */ resolve(fd, nrows, &bndC); /* free the buffers already used */ G_free (bndC.b[0]); G_free (bndC.b[1]); G_free (bndC.b[2]); G_free (bnd.b[0]); G_free (bnd.b[1]); G_free (bnd.b[2]); /* determine the drainage paths */ /* repeat for each starting point */ for (i = 0; i < npoints; i++) { /* use the flow directions to determine the drainage path * results are compiled as a linked list of points in downstream order */ thispoint->row = points_row[i]; thispoint->col = points_col[i]; thispoint->next = NULL; thispoint = drain(fd, thispoint, nrows, ncols); } /* do the output */ if (mode == 0 || mode == 3) { /* Output will be a cell map */ /* open a new file and allocate an output buffer */ new_id = G_open_cell_new(new_map_name); out_buf = G_allocate_c_raster_buf(); /* mark each cell */ thispoint = list; while (thispoint->next != NULL) { thispoint->value = 1; thispoint = thispoint->next; } if (mode == 3) { /* number each cell downstream */ thispoint = list; ival = 0; while (thispoint->next != NULL) { if (thispoint->row == INT_MAX) { ival = 0; thispoint = thispoint->next; continue; } thispoint->value += ival; ival = thispoint->value; thispoint = thispoint->next; } } /* build the output map */ for (i = 0; i < nrows; i++) { G_set_c_null_value(out_buf, ncols); thispoint = list; while (thispoint->next != NULL) { if (thispoint->row == i) out_buf[thispoint->col] = (int)thispoint->value; thispoint = thispoint->next; } G_put_c_raster_row(new_id, out_buf); } } else { /* mode = 1 or 2 */ /* Output will be of the same type as input */ /* open a new file and allocate an output buffer */ new_id = G_open_raster_new(new_map_name, in_type); out_buf = get_buf(); bsz = ncols * bpe(); /* loop through each point in the list and store the map values */ thispoint = list; while (thispoint->next != NULL) { if (thispoint->row == INT_MAX) { thispoint = thispoint->next; continue; } lseek(fe, thispoint->row * bsz, SEEK_SET); read(fe, in_buf, bsz); memcpy(&thispoint->value, (char *)in_buf + bpe() * thispoint->col, bpe()); thispoint = thispoint->next; } if (mode == 2) { /* accumulate the input map values downstream */ thispoint = list; val = 0.; while (thispoint->next != NULL) { if (thispoint->row == INT_MAX) { val = 0.; thispoint = thispoint->next; continue; } sum(&thispoint->value, &val); memcpy(&val, &thispoint->value, bpe()); thispoint = thispoint->next; } } /* build the output map */ for (i = 0; i < nrows; i++) { set_null_value(out_buf, ncols); thispoint = list; while (thispoint->next != NULL) { if (thispoint->row == i) memcpy((char *)out_buf + bpe() * thispoint->col, &(thispoint->value), bpe()); thispoint = thispoint->next; } put_row(new_id, out_buf); } } /* close files and free buffers */ G_close_cell(new_id); close(fe); close(fd); unlink(tempfile1); unlink(tempfile2); G_free(in_buf); G_free(out_buf); exit(EXIT_SUCCESS); } struct point *drain(int fd, struct point *list, int nrow, int ncol) { int go = 1, next_row, next_col; CELL direction; CELL *dir; dir = G_allocate_c_raster_buf(); next_row = list->row; next_col = list->col; /* begin loop */ while (go) { /* find flow direction at this point */ lseek(fd, list->row * ncol * sizeof(CELL), SEEK_SET); read(fd, dir, ncol * sizeof(CELL)); direction = *(dir + list->col); go = 0; /* identify next downstream cell */ if (direction > 0 && direction < 256) { if (direction == 1 || direction == 2 || direction == 4) next_col += 1; else if (direction == 16 || direction == 32 || direction == 64) next_col -= 1; if (direction == 64 || direction == 128 || direction == 1) next_row -= 1; else if (direction == 4 || direction == 8 || direction == 16) next_row += 1; if (next_col >= 0 && next_col < ncol && next_row >= 0 && next_row < nrow) { /* allocate and fill the next point structure */ list->next = (struct point *)G_malloc(sizeof(struct point)); list = list->next; list->row = next_row; list->col = next_col; go = 1; } } } /* end while */ /* allocate and fill the end-of-path flag */ list->next = (struct point *)G_malloc(sizeof(struct point)); list = list->next; list->row = INT_MAX; /* return a pointer to an empty structure */ list->next = (struct point *)G_malloc(sizeof(struct point)); list = list->next; list->next = NULL; G_free(dir); return list; }