| 1 | /****************************************************************************
|
|---|
| 2 | *
|
|---|
| 3 | * MODULE: r.drain
|
|---|
| 4 | *
|
|---|
| 5 | * AUTHOR(S): Kewan Q. Khawaja <kewan techlogix.com>
|
|---|
| 6 | * Update to FP (2000): Pierre de Mouveaux <pmx@audiovu.com> <pmx@free.fr>
|
|---|
| 7 | * bugfix in FCELL, DCELL: Markus Neteler 12/2000
|
|---|
| 8 | * Rewritten by Roger Miller 7/2001 based on subroutines from
|
|---|
| 9 | * r.fill.dir and on the original r.drain.
|
|---|
| 10 | * 24 July 2004: WebValley 2004, error checking and vector points added by
|
|---|
| 11 | * Matteo Franchi Liceo Leonardo Da Vinci Trento
|
|---|
| 12 | * Roberto Flor ITC-irst
|
|---|
| 13 | * New code added by Colin Nielsen <colin.nielsen at gmail dot com> *
|
|---|
| 14 | * to use movement direction surface from r.walk and r.cost, and to
|
|---|
| 15 | * output vector paths 2/2009
|
|---|
| 16 | *
|
|---|
| 17 | * PURPOSE: This is the main program for tracing out the path that a
|
|---|
| 18 | * drop of water would take if released at a certain location
|
|---|
| 19 | * on an input elevation map.
|
|---|
| 20 | * COPYRIGHT: (C) 2000,2009 by the GRASS Development Team
|
|---|
| 21 | *
|
|---|
| 22 | * This program is free software under the GNU General Public
|
|---|
| 23 | * License (>=v2). Read the file COPYING that comes with GRASS
|
|---|
| 24 | * for details.
|
|---|
| 25 | *
|
|---|
| 26 | *****************************************************************************/
|
|---|
| 27 |
|
|---|
| 28 | #include <grass/config.h>
|
|---|
| 29 | #include <stdlib.h>
|
|---|
| 30 | #include <stdio.h>
|
|---|
| 31 | #include <string.h>
|
|---|
| 32 | #include <math.h>
|
|---|
| 33 | #include <float.h>
|
|---|
| 34 |
|
|---|
| 35 | /* for using the "open" statement */
|
|---|
| 36 | #include <sys/types.h>
|
|---|
| 37 | #include <sys/stat.h>
|
|---|
| 38 | #include <fcntl.h>
|
|---|
| 39 |
|
|---|
| 40 | /* for using the close statement */
|
|---|
| 41 | #include <unistd.h>
|
|---|
| 42 |
|
|---|
| 43 | #include <grass/gis.h>
|
|---|
| 44 | #include <grass/raster.h>
|
|---|
| 45 | #include <grass/glocale.h>
|
|---|
| 46 | #include <grass/vector.h>
|
|---|
| 47 |
|
|---|
| 48 | #define DEBUG
|
|---|
| 49 | #include "tinf.h"
|
|---|
| 50 | #include "local.h"
|
|---|
| 51 |
|
|---|
| 52 | /* should probably be updated to a pointer array & malloc/realloc as needed */
|
|---|
| 53 | #define POINTS_INCREMENT 1024
|
|---|
| 54 |
|
|---|
| 55 | /* define a data structure to hold the point data */
|
|---|
| 56 | struct point
|
|---|
| 57 | {
|
|---|
| 58 | int row;
|
|---|
| 59 | int col;
|
|---|
| 60 | struct point *next;
|
|---|
| 61 | double value;
|
|---|
| 62 | };
|
|---|
| 63 |
|
|---|
| 64 | int main(int argc, char **argv)
|
|---|
| 65 | {
|
|---|
| 66 |
|
|---|
| 67 | int fe, fd, dir_fd;
|
|---|
| 68 | int i, have_points = 0;
|
|---|
| 69 | int new_id;
|
|---|
| 70 | int nrows, ncols;
|
|---|
| 71 | int *points_row = NULL, *points_col = NULL, npoints;
|
|---|
| 72 | int increment_count;
|
|---|
| 73 | int cell_open(), cell_open_new();
|
|---|
| 74 | int map_id, dir_id;
|
|---|
| 75 | char map_name[GNAME_MAX], new_map_name[GNAME_MAX], dir_name[GNAME_MAX];
|
|---|
| 76 | char *tempfile1, *tempfile2, *tempfile3;
|
|---|
| 77 | struct History history;
|
|---|
| 78 |
|
|---|
| 79 | struct Cell_head window;
|
|---|
| 80 | struct Option *opt1, *opt2, *coordopt, *vpointopt, *opt3, *opt4;
|
|---|
| 81 | struct Flag *flag1, *flag2, *flag3, *flag4;
|
|---|
| 82 | struct GModule *module;
|
|---|
| 83 | int in_type;
|
|---|
| 84 | void *in_buf;
|
|---|
| 85 | void *dir_buf;
|
|---|
| 86 | CELL *out_buf;
|
|---|
| 87 | struct band3 bnd, bndC;
|
|---|
| 88 | struct metrics *m = NULL;
|
|---|
| 89 |
|
|---|
| 90 | struct point *list;
|
|---|
| 91 | struct point *thispoint;
|
|---|
| 92 | int ival, start_row, start_col, mode;
|
|---|
| 93 | off_t bsz;
|
|---|
| 94 | int costmode = 0;
|
|---|
| 95 | double east, north, val;
|
|---|
| 96 | struct point *drain(int, struct point *, int, int);
|
|---|
| 97 | struct point *drain_cost(int, struct point *, int, int);
|
|---|
| 98 | int bsort(int, struct point *);
|
|---|
| 99 |
|
|---|
| 100 | struct line_pnts *Points;
|
|---|
| 101 | struct line_cats *Cats;
|
|---|
| 102 | struct Map_info vout;
|
|---|
| 103 | int cat;
|
|---|
| 104 | double x, y;
|
|---|
| 105 |
|
|---|
| 106 | G_gisinit(argv[0]);
|
|---|
| 107 |
|
|---|
| 108 | module = G_define_module();
|
|---|
| 109 | G_add_keyword(_("raster"));
|
|---|
| 110 | G_add_keyword(_("hydrology"));
|
|---|
| 111 | G_add_keyword(_("cost surface"));
|
|---|
| 112 | module->description =
|
|---|
| 113 | _("Traces a flow through an elevation model or cost surface on a raster map.");
|
|---|
| 114 |
|
|---|
| 115 | opt1 = G_define_standard_option(G_OPT_R_INPUT);
|
|---|
| 116 | opt1->description = _("Name of input elevation or cost surface raster map");
|
|---|
| 117 |
|
|---|
| 118 | opt3 = G_define_standard_option(G_OPT_R_INPUT);
|
|---|
| 119 | opt3->key = "direction";
|
|---|
| 120 | opt3->label =
|
|---|
| 121 | _("Name of input movement direction map associated with the cost surface");
|
|---|
| 122 | opt3->description =
|
|---|
| 123 | _("Direction in degrees CCW from east");
|
|---|
| 124 | opt3->required = NO;
|
|---|
| 125 | opt3->guisection = _("Cost surface");
|
|---|
| 126 |
|
|---|
| 127 | opt2 = G_define_standard_option(G_OPT_R_OUTPUT);
|
|---|
| 128 |
|
|---|
| 129 | opt4 = G_define_standard_option(G_OPT_V_OUTPUT);
|
|---|
| 130 | opt4->key = "drain";
|
|---|
| 131 | opt4->required = NO;
|
|---|
| 132 | opt4->label =
|
|---|
| 133 | _("Name for output drain vector map");
|
|---|
| 134 | opt4->description = _("Recommended for cost surface made using knight's move");
|
|---|
| 135 |
|
|---|
| 136 | coordopt = G_define_standard_option(G_OPT_M_COORDS);
|
|---|
| 137 | coordopt->key = "start_coordinates";
|
|---|
| 138 | coordopt->multiple = YES;
|
|---|
| 139 | coordopt->description = _("Coordinates of starting point(s) (E,N)");
|
|---|
| 140 | coordopt->guisection = _("Start");
|
|---|
| 141 |
|
|---|
| 142 | vpointopt = G_define_standard_option(G_OPT_V_INPUTS);
|
|---|
| 143 | vpointopt->key = "start_points";
|
|---|
| 144 | vpointopt->required = NO;
|
|---|
| 145 | vpointopt->label = _("Name of starting vector points map(s)");
|
|---|
| 146 | vpointopt->description = NULL;
|
|---|
| 147 | vpointopt->guisection = _("Start");
|
|---|
| 148 |
|
|---|
| 149 | flag1 = G_define_flag();
|
|---|
| 150 | flag1->key = 'c';
|
|---|
| 151 | flag1->description = _("Copy input cell values on output");
|
|---|
| 152 | flag1->guisection = _("Path settings");
|
|---|
| 153 |
|
|---|
| 154 | flag2 = G_define_flag();
|
|---|
| 155 | flag2->key = 'a';
|
|---|
| 156 | flag2->description = _("Accumulate input values along the path");
|
|---|
| 157 | flag2->guisection = _("Path settings");
|
|---|
| 158 |
|
|---|
| 159 | flag3 = G_define_flag();
|
|---|
| 160 | flag3->key = 'n';
|
|---|
| 161 | flag3->description = _("Count cell numbers along the path");
|
|---|
| 162 | flag3->guisection = _("Path settings");
|
|---|
| 163 |
|
|---|
| 164 | flag4 = G_define_flag();
|
|---|
| 165 | flag4->key = 'd';
|
|---|
| 166 | flag4->description =
|
|---|
| 167 | _("The input raster map is a cost surface (direction surface must also be specified)");
|
|---|
| 168 | flag4->guisection = _("Cost surface");
|
|---|
| 169 |
|
|---|
| 170 | if (G_parser(argc, argv))
|
|---|
| 171 | exit(EXIT_FAILURE);
|
|---|
| 172 |
|
|---|
| 173 |
|
|---|
| 174 | strcpy(map_name, opt1->answer);
|
|---|
| 175 | strcpy(new_map_name, opt2->answer);
|
|---|
| 176 |
|
|---|
| 177 | if (flag4->answer) {
|
|---|
| 178 | costmode = 1;
|
|---|
| 179 | G_verbose_message(_("Directional drain selected... checking for direction raster map"));
|
|---|
| 180 | }
|
|---|
| 181 | else {
|
|---|
| 182 | G_verbose_message(_("Surface/Hydrology drain selected"));
|
|---|
| 183 | }
|
|---|
| 184 |
|
|---|
| 185 | if (costmode == 1) {
|
|---|
| 186 | if (!opt3->answer) {
|
|---|
| 187 | G_fatal_error(_("Direction raster map <%s> not specified, if direction flag is on, "
|
|---|
| 188 | "a direction raster must be given"), opt3->key);
|
|---|
| 189 | }
|
|---|
| 190 | strcpy(dir_name, opt3->answer);
|
|---|
| 191 | }
|
|---|
| 192 | if (costmode == 0) {
|
|---|
| 193 | if (opt3->answer) {
|
|---|
| 194 | G_fatal_error(_("Direction raster map <%s> should not be specified for Surface/Hydrology drains"),
|
|---|
| 195 | opt3->answer);
|
|---|
| 196 | }
|
|---|
| 197 | }
|
|---|
| 198 |
|
|---|
| 199 | if (opt4->answer) {
|
|---|
| 200 | if (0 > Vect_open_new(&vout, opt4->answer, 0)) {
|
|---|
| 201 | G_fatal_error(_("Unable to create vector map <%s>"),
|
|---|
| 202 | opt4->answer);
|
|---|
| 203 | }
|
|---|
| 204 | Vect_hist_command(&vout);
|
|---|
| 205 | }
|
|---|
| 206 | /* allocate cell buf for the map layer */
|
|---|
| 207 | in_type = Rast_map_type(map_name, "");
|
|---|
| 208 |
|
|---|
| 209 | /* set the pointers for multi-typed functions */
|
|---|
| 210 | set_func_pointers(in_type);
|
|---|
| 211 |
|
|---|
| 212 | if ((flag1->answer + flag2->answer + flag3->answer) > 1)
|
|---|
| 213 | G_fatal_error(_("Specify just one of the -c, -a and -n flags"));
|
|---|
| 214 |
|
|---|
| 215 | mode = 0;
|
|---|
| 216 | if (flag1->answer)
|
|---|
| 217 | mode = 1;
|
|---|
| 218 | if (flag2->answer)
|
|---|
| 219 | mode = 2;
|
|---|
| 220 | if (flag3->answer)
|
|---|
| 221 | mode = 3;
|
|---|
| 222 |
|
|---|
| 223 | /* get the window information */
|
|---|
| 224 | G_get_window(&window);
|
|---|
| 225 | nrows = Rast_window_rows();
|
|---|
| 226 | ncols = Rast_window_cols();
|
|---|
| 227 |
|
|---|
| 228 | /* calculate true cell resolution */
|
|---|
| 229 | m = (struct metrics *)G_malloc(nrows * sizeof(struct metrics));
|
|---|
| 230 | points_row = (int*)G_calloc(POINTS_INCREMENT, sizeof(int));
|
|---|
| 231 | points_col = (int*)G_calloc(POINTS_INCREMENT, sizeof(int));
|
|---|
| 232 |
|
|---|
| 233 | increment_count = 1;
|
|---|
| 234 | npoints = 0;
|
|---|
| 235 | if (coordopt->answer) {
|
|---|
| 236 | for (i = 0; coordopt->answers[i] != NULL; i += 2) {
|
|---|
| 237 | G_scan_easting(coordopt->answers[i], &east, G_projection());
|
|---|
| 238 | G_scan_northing(coordopt->answers[i + 1], &north, G_projection());
|
|---|
| 239 | start_col = (int)Rast_easting_to_col(east, &window);
|
|---|
| 240 | start_row = (int)Rast_northing_to_row(north, &window);
|
|---|
| 241 |
|
|---|
| 242 | if (start_row < 0 || start_row > nrows ||
|
|---|
| 243 | start_col < 0 || start_col > ncols) {
|
|---|
| 244 | G_warning(_("Starting point %d is outside the current region"),
|
|---|
| 245 | i + 1);
|
|---|
| 246 | continue;
|
|---|
| 247 | }
|
|---|
| 248 | points_row[npoints] = start_row;
|
|---|
| 249 | points_col[npoints] = start_col;
|
|---|
| 250 | npoints++;
|
|---|
| 251 | if (npoints == POINTS_INCREMENT * increment_count)
|
|---|
| 252 | {
|
|---|
| 253 | increment_count++;
|
|---|
| 254 | points_row = (int*)G_realloc(points_row, POINTS_INCREMENT * increment_count * sizeof(int));
|
|---|
| 255 | points_col = (int*)G_realloc(points_col, POINTS_INCREMENT * increment_count * sizeof(int));
|
|---|
| 256 | }
|
|---|
| 257 | have_points = 1;
|
|---|
| 258 | }
|
|---|
| 259 | }
|
|---|
| 260 | if (vpointopt->answers) {
|
|---|
| 261 | for (i = 0; vpointopt->answers[i] != NULL; i++) {
|
|---|
| 262 | struct Map_info In;
|
|---|
| 263 | struct bound_box box;
|
|---|
| 264 | int type;
|
|---|
| 265 |
|
|---|
| 266 | Points = Vect_new_line_struct();
|
|---|
| 267 | Cats = Vect_new_cats_struct();
|
|---|
| 268 |
|
|---|
| 269 | Vect_set_open_level(1); /* topology not required */
|
|---|
| 270 |
|
|---|
| 271 | if (1 > Vect_open_old(&In, vpointopt->answers[i], ""))
|
|---|
| 272 | G_fatal_error(_("Unable to open vector map <%s>"), vpointopt->answers[i]);
|
|---|
| 273 |
|
|---|
| 274 | G_message(_("Reading vector map <%s> with start points..."),
|
|---|
| 275 | Vect_get_full_name(&In));
|
|---|
| 276 |
|
|---|
| 277 | Vect_rewind(&In);
|
|---|
| 278 |
|
|---|
| 279 | Vect_region_box(&window, &box);
|
|---|
| 280 |
|
|---|
| 281 | while (1) {
|
|---|
| 282 | /* register line */
|
|---|
| 283 | type = Vect_read_next_line(&In, Points, Cats);
|
|---|
| 284 |
|
|---|
| 285 | /* Note: check for dead lines is not needed, because they are skipped by V1_read_next_line_nat() */
|
|---|
| 286 | if (type == -1) {
|
|---|
| 287 | G_warning(_("Unable to read vector map"));
|
|---|
| 288 | continue;
|
|---|
| 289 | }
|
|---|
| 290 | else if (type == -2) {
|
|---|
| 291 | break;
|
|---|
| 292 | }
|
|---|
| 293 | if (!Vect_point_in_box(Points->x[0], Points->y[0], 0, &box))
|
|---|
| 294 | continue;
|
|---|
| 295 |
|
|---|
| 296 | start_col = (int)Rast_easting_to_col(Points->x[0], &window);
|
|---|
| 297 | start_row = (int)Rast_northing_to_row(Points->y[0], &window);
|
|---|
| 298 |
|
|---|
| 299 | /* effectively just a duplicate check to G_site_in_region() ??? */
|
|---|
| 300 | if (start_row < 0 || start_row > nrows || start_col < 0 ||
|
|---|
| 301 | start_col > ncols)
|
|---|
| 302 | continue;
|
|---|
| 303 |
|
|---|
| 304 | points_row[npoints] = start_row;
|
|---|
| 305 | points_col[npoints] = start_col;
|
|---|
| 306 | npoints++;
|
|---|
| 307 | if (npoints == POINTS_INCREMENT * increment_count)
|
|---|
| 308 | {
|
|---|
| 309 | increment_count++;
|
|---|
| 310 | points_row = (int*)G_realloc(points_row, POINTS_INCREMENT * increment_count * sizeof(int));
|
|---|
| 311 | points_col = (int*)G_realloc(points_col, POINTS_INCREMENT * increment_count * sizeof(int));
|
|---|
| 312 | }
|
|---|
| 313 | have_points = 1;
|
|---|
| 314 | }
|
|---|
| 315 | Vect_close(&In);
|
|---|
| 316 |
|
|---|
| 317 | /* only catches maps out of range until something is found, not after */
|
|---|
| 318 | if (!have_points) {
|
|---|
| 319 | G_warning(_("Starting vector map <%s> contains no points in the current region"),
|
|---|
| 320 | vpointopt->answers[i]);
|
|---|
| 321 | }
|
|---|
| 322 | Vect_destroy_line_struct(Points);
|
|---|
| 323 | Vect_destroy_cats_struct(Cats);
|
|---|
| 324 | }
|
|---|
| 325 | }
|
|---|
| 326 | if (have_points == 0)
|
|---|
| 327 | G_fatal_error(_("No start/stop point(s) specified"));
|
|---|
| 328 |
|
|---|
| 329 | /* determine the drainage paths */
|
|---|
| 330 |
|
|---|
| 331 | /* allocate storage for the first point */
|
|---|
| 332 | thispoint = (struct point *)G_malloc(sizeof(struct point));
|
|---|
| 333 | list = thispoint;
|
|---|
| 334 | thispoint->next = NULL;
|
|---|
| 335 |
|
|---|
| 336 | G_begin_distance_calculations();
|
|---|
| 337 | {
|
|---|
| 338 | double e1, n1, e2, n2;
|
|---|
| 339 |
|
|---|
| 340 | e1 = window.east;
|
|---|
| 341 | n1 = window.north;
|
|---|
| 342 | e2 = e1 + window.ew_res;
|
|---|
| 343 | n2 = n1 - window.ns_res;
|
|---|
| 344 | for (i = 0; i < nrows; i++) {
|
|---|
| 345 | m[i].ew_res = G_distance(e1, n1, e2, n1);
|
|---|
| 346 | m[i].ns_res = G_distance(e1, n1, e1, n2);
|
|---|
| 347 | m[i].diag_res = G_distance(e1, n1, e2, n2);
|
|---|
| 348 | e2 = e1 + window.ew_res;
|
|---|
| 349 | n2 = n1 - window.ns_res;
|
|---|
| 350 | }
|
|---|
| 351 | }
|
|---|
| 352 |
|
|---|
| 353 | /* buffers for internal use */
|
|---|
| 354 | bndC.ns = ncols;
|
|---|
| 355 | bndC.sz = sizeof(CELL) * ncols;
|
|---|
| 356 | bndC.b[0] = G_calloc(ncols, sizeof(CELL));
|
|---|
| 357 | bndC.b[1] = G_calloc(ncols, sizeof(CELL));
|
|---|
| 358 | bndC.b[2] = G_calloc(ncols, sizeof(CELL));
|
|---|
| 359 |
|
|---|
| 360 | /* buffers for external use */
|
|---|
| 361 | bnd.ns = ncols;
|
|---|
| 362 | bnd.sz = ncols * bpe();
|
|---|
| 363 | bnd.b[0] = G_calloc(ncols, bpe());
|
|---|
| 364 | bnd.b[1] = G_calloc(ncols, bpe());
|
|---|
| 365 | bnd.b[2] = G_calloc(ncols, bpe());
|
|---|
| 366 |
|
|---|
| 367 | /* an input buffer */
|
|---|
| 368 | in_buf = get_buf();
|
|---|
| 369 |
|
|---|
| 370 | /* open the original map and get its file id */
|
|---|
| 371 | map_id = Rast_open_old(map_name, "");
|
|---|
| 372 |
|
|---|
| 373 | /* get some temp files */
|
|---|
| 374 | tempfile1 = G_tempfile();
|
|---|
| 375 | tempfile2 = G_tempfile();
|
|---|
| 376 |
|
|---|
| 377 | fe = open(tempfile1, O_RDWR | O_CREAT, 0666);
|
|---|
| 378 | fd = open(tempfile2, O_RDWR | O_CREAT, 0666);
|
|---|
| 379 |
|
|---|
| 380 | /* transfer the input map to a temp file */
|
|---|
| 381 | for (i = 0; i < nrows; i++) {
|
|---|
| 382 | get_row(map_id, in_buf, i);
|
|---|
| 383 | write(fe, in_buf, bnd.sz);
|
|---|
| 384 | }
|
|---|
| 385 | Rast_close(map_id);
|
|---|
| 386 |
|
|---|
| 387 | if (costmode == 1) {
|
|---|
| 388 | dir_buf = Rast_allocate_d_buf();
|
|---|
| 389 | dir_id = Rast_open_old(dir_name, "");
|
|---|
| 390 | tempfile3 = G_tempfile();
|
|---|
| 391 | dir_fd = open(tempfile3, O_RDWR | O_CREAT, 0666);
|
|---|
| 392 |
|
|---|
| 393 | for (i = 0; i < nrows; i++) {
|
|---|
| 394 | Rast_get_d_row(dir_id, dir_buf, i);
|
|---|
| 395 | write(dir_fd, dir_buf, ncols * sizeof(DCELL));
|
|---|
| 396 | }
|
|---|
| 397 | Rast_close(dir_id);
|
|---|
| 398 | }
|
|---|
| 399 |
|
|---|
| 400 | /* only necessary for non-dir drain */
|
|---|
| 401 | if (costmode == 0) {
|
|---|
| 402 | G_message(_("Calculating flow directions..."));
|
|---|
| 403 |
|
|---|
| 404 | /* fill one-cell pits and take a first stab at flow directions */
|
|---|
| 405 | filldir(fe, fd, nrows, &bnd, m);
|
|---|
| 406 |
|
|---|
| 407 | /* determine flow directions for more ambiguous cases */
|
|---|
| 408 | resolve(fd, nrows, &bndC);
|
|---|
| 409 | }
|
|---|
| 410 |
|
|---|
| 411 | /* free the buffers already used */
|
|---|
| 412 | G_free(bndC.b[0]);
|
|---|
| 413 | G_free(bndC.b[1]);
|
|---|
| 414 | G_free(bndC.b[2]);
|
|---|
| 415 |
|
|---|
| 416 | G_free(bnd.b[0]);
|
|---|
| 417 | G_free(bnd.b[1]);
|
|---|
| 418 | G_free(bnd.b[2]);
|
|---|
| 419 |
|
|---|
| 420 | /* determine the drainage paths */
|
|---|
| 421 |
|
|---|
| 422 | /* repeat for each starting point */
|
|---|
| 423 | for (i = 0; i < npoints; i++) {
|
|---|
| 424 | /* use the flow directions to determine the drainage path
|
|---|
| 425 | * results are compiled as a linked list of points in downstream order */
|
|---|
| 426 | thispoint->row = points_row[i];
|
|---|
| 427 | thispoint->col = points_col[i];
|
|---|
| 428 | thispoint->next = NULL;
|
|---|
| 429 | /* drain algorithm selection (dir or non-dir) */
|
|---|
| 430 | if (costmode == 0) {
|
|---|
| 431 | thispoint = drain(fd, thispoint, nrows, ncols);
|
|---|
| 432 | }
|
|---|
| 433 | if (costmode == 1) {
|
|---|
| 434 | thispoint = drain_cost(dir_fd, thispoint, nrows, ncols);
|
|---|
| 435 | }
|
|---|
| 436 | }
|
|---|
| 437 |
|
|---|
| 438 | /* do the output */
|
|---|
| 439 |
|
|---|
| 440 | if (mode == 0 || mode == 3) {
|
|---|
| 441 |
|
|---|
| 442 | /* Output will be a cell map */
|
|---|
| 443 | /* open a new file and allocate an output buffer */
|
|---|
| 444 | new_id = Rast_open_c_new(new_map_name);
|
|---|
| 445 | out_buf = Rast_allocate_c_buf();
|
|---|
| 446 |
|
|---|
| 447 | /* mark each cell */
|
|---|
| 448 | thispoint = list;
|
|---|
| 449 | while (thispoint->next != NULL) {
|
|---|
| 450 | thispoint->value = 1;
|
|---|
| 451 | thispoint = thispoint->next;
|
|---|
| 452 | }
|
|---|
| 453 |
|
|---|
| 454 | if (mode == 3) {
|
|---|
| 455 | /* number each cell downstream */
|
|---|
| 456 | thispoint = list;
|
|---|
| 457 | ival = 0;
|
|---|
| 458 | while (thispoint->next != NULL) {
|
|---|
| 459 | if (thispoint->row == INT_MAX) {
|
|---|
| 460 | ival = 0;
|
|---|
| 461 | thispoint = thispoint->next;
|
|---|
| 462 | continue;
|
|---|
| 463 | }
|
|---|
| 464 | thispoint->value += ival;
|
|---|
| 465 | ival = thispoint->value;
|
|---|
| 466 | thispoint = thispoint->next;
|
|---|
| 467 | }
|
|---|
| 468 | }
|
|---|
| 469 |
|
|---|
| 470 | /* build the output map */
|
|---|
| 471 | G_message(_("Writing output raster map..."));
|
|---|
| 472 | for (i = 0; i < nrows; i++) {
|
|---|
| 473 | G_percent(i, nrows, 2);
|
|---|
| 474 | Rast_set_c_null_value(out_buf, ncols);
|
|---|
| 475 | thispoint = list;
|
|---|
| 476 | while (thispoint->next != NULL) {
|
|---|
| 477 | if (thispoint->row == i)
|
|---|
| 478 | out_buf[thispoint->col] = (int)thispoint->value;
|
|---|
| 479 | thispoint = thispoint->next;
|
|---|
| 480 | }
|
|---|
| 481 | Rast_put_c_row(new_id, out_buf);
|
|---|
| 482 | }
|
|---|
| 483 | G_percent(1, 1, 1);
|
|---|
| 484 | }
|
|---|
| 485 | else { /* mode = 1 or 2 */
|
|---|
| 486 | /* Output will be of the same type as input */
|
|---|
| 487 | /* open a new file and allocate an output buffer */
|
|---|
| 488 | new_id = Rast_open_new(new_map_name, in_type);
|
|---|
| 489 | out_buf = get_buf();
|
|---|
| 490 | bsz = ncols * bpe();
|
|---|
| 491 |
|
|---|
| 492 | /* loop through each point in the list and store the map values */
|
|---|
| 493 | thispoint = list;
|
|---|
| 494 | while (thispoint->next != NULL) {
|
|---|
| 495 | if (thispoint->row == INT_MAX) {
|
|---|
| 496 | thispoint = thispoint->next;
|
|---|
| 497 | continue;
|
|---|
| 498 | }
|
|---|
| 499 | lseek(fe, (off_t) thispoint->row * bsz, SEEK_SET);
|
|---|
| 500 | read(fe, in_buf, bsz);
|
|---|
| 501 | memcpy(&thispoint->value, (char *)in_buf + bpe() * thispoint->col,
|
|---|
| 502 | bpe());
|
|---|
| 503 | thispoint = thispoint->next;
|
|---|
| 504 | }
|
|---|
| 505 |
|
|---|
| 506 | if (mode == 2) {
|
|---|
| 507 | /* accumulate the input map values downstream */
|
|---|
| 508 | thispoint = list;
|
|---|
| 509 | val = 0.;
|
|---|
| 510 | while (thispoint->next != NULL) {
|
|---|
| 511 | if (thispoint->row == INT_MAX) {
|
|---|
| 512 | val = 0.;
|
|---|
| 513 | thispoint = thispoint->next;
|
|---|
| 514 | continue;
|
|---|
| 515 | }
|
|---|
| 516 | sum(&thispoint->value, &val);
|
|---|
| 517 | memcpy(&val, &thispoint->value, bpe());
|
|---|
| 518 | thispoint = thispoint->next;
|
|---|
| 519 | }
|
|---|
| 520 | }
|
|---|
| 521 |
|
|---|
| 522 | /* build the output map */
|
|---|
| 523 | G_message(_("Writing raster map <%s>..."),
|
|---|
| 524 | new_map_name);
|
|---|
| 525 | for (i = 0; i < nrows; i++) {
|
|---|
| 526 | G_percent(i, nrows, 2);
|
|---|
| 527 | set_null_value(out_buf, ncols);
|
|---|
| 528 | thispoint = list;
|
|---|
| 529 | while (thispoint->next != NULL) {
|
|---|
| 530 | if (thispoint->row == i)
|
|---|
| 531 | memcpy((char *)out_buf + bpe() * thispoint->col,
|
|---|
| 532 | &(thispoint->value), bpe());
|
|---|
| 533 | thispoint = thispoint->next;
|
|---|
| 534 | }
|
|---|
| 535 | put_row(new_id, out_buf);
|
|---|
| 536 | }
|
|---|
| 537 | G_percent(1, 1, 1);
|
|---|
| 538 | }
|
|---|
| 539 |
|
|---|
| 540 | /* Output a vector path */
|
|---|
| 541 | if (opt4->answer) {
|
|---|
| 542 | Points = Vect_new_line_struct();
|
|---|
| 543 | Cats = Vect_new_cats_struct();
|
|---|
| 544 | /* Need to modify for multiple paths */
|
|---|
| 545 | thispoint = list;
|
|---|
| 546 | i = 1;
|
|---|
| 547 | while (thispoint->next != NULL) {
|
|---|
| 548 | if (thispoint->row == INT_MAX) {
|
|---|
| 549 | thispoint = thispoint->next;
|
|---|
| 550 | Vect_cat_set(Cats, 1, i);
|
|---|
| 551 | Vect_write_line(&vout, GV_LINE, Points, Cats);
|
|---|
| 552 | Vect_reset_line(Points);
|
|---|
| 553 | Vect_reset_cats(Cats);
|
|---|
| 554 | i++;
|
|---|
| 555 | continue;
|
|---|
| 556 | }
|
|---|
| 557 | if (Vect_cat_get(Cats, 1, &cat) == 0) {
|
|---|
| 558 | Vect_cat_set(Cats, 1, i);
|
|---|
| 559 | }
|
|---|
| 560 | /* Need to convert row and col to coordinates
|
|---|
| 561 | * y = cell_head.north - ((double) p->row + 0.5) * cell_head.ns_res;
|
|---|
| 562 | * x = cell_head.west + ((double) p->col + 0.5) * cell_head.ew_res;
|
|---|
| 563 | */
|
|---|
| 564 |
|
|---|
| 565 | x = window.west + ((double)thispoint->col + 0.5) * window.ew_res;
|
|---|
| 566 | y = window.north - ((double)thispoint->row + 0.5) * window.ns_res;
|
|---|
| 567 | Vect_append_point(Points, x, y, 0.0);
|
|---|
| 568 | thispoint = thispoint->next;
|
|---|
| 569 | } /* End while */
|
|---|
| 570 | Vect_build(&vout);
|
|---|
| 571 | Vect_close(&vout);
|
|---|
| 572 | }
|
|---|
| 573 |
|
|---|
| 574 | /* close files and free buffers */
|
|---|
| 575 | Rast_close(new_id);
|
|---|
| 576 |
|
|---|
| 577 | Rast_put_cell_title(new_map_name, "Surface flow trace");
|
|---|
| 578 |
|
|---|
| 579 | Rast_short_history(new_map_name, "raster", &history);
|
|---|
| 580 | Rast_command_history(&history);
|
|---|
| 581 | Rast_write_history(new_map_name, &history);
|
|---|
| 582 |
|
|---|
| 583 | close(fe);
|
|---|
| 584 | close(fd);
|
|---|
| 585 |
|
|---|
| 586 | unlink(tempfile1);
|
|---|
| 587 | unlink(tempfile2);
|
|---|
| 588 | G_free(in_buf);
|
|---|
| 589 | G_free(out_buf);
|
|---|
| 590 | if(points_row)
|
|---|
| 591 | G_free(points_row);
|
|---|
| 592 | if(points_col)
|
|---|
| 593 | G_free(points_col);
|
|---|
| 594 |
|
|---|
| 595 | if (costmode == 1) {
|
|---|
| 596 | close(dir_fd);
|
|---|
| 597 | unlink(tempfile3);
|
|---|
| 598 | G_free(dir_buf);
|
|---|
| 599 | }
|
|---|
| 600 |
|
|---|
| 601 | exit(EXIT_SUCCESS);
|
|---|
| 602 | }
|
|---|
| 603 |
|
|---|
| 604 | struct point *drain(int fd, struct point *list, int nrow, int ncol)
|
|---|
| 605 | {
|
|---|
| 606 | int go = 1, next_row, next_col;
|
|---|
| 607 | CELL direction;
|
|---|
| 608 | CELL *dir;
|
|---|
| 609 |
|
|---|
| 610 | dir = Rast_allocate_c_buf();
|
|---|
| 611 | next_row = list->row;
|
|---|
| 612 | next_col = list->col;
|
|---|
| 613 |
|
|---|
| 614 | /* begin loop */
|
|---|
| 615 | while (go) {
|
|---|
| 616 |
|
|---|
| 617 | /* find flow direction at this point */
|
|---|
| 618 | lseek(fd, (off_t) list->row * ncol * sizeof(CELL), SEEK_SET);
|
|---|
| 619 | read(fd, dir, ncol * sizeof(CELL));
|
|---|
| 620 | direction = *(dir + list->col);
|
|---|
| 621 | go = 0;
|
|---|
| 622 |
|
|---|
| 623 | /* identify next downstream cell */
|
|---|
| 624 | if (direction > 0 && direction < 256) {
|
|---|
| 625 |
|
|---|
| 626 | if (direction == 1 || direction == 2 || direction == 4)
|
|---|
| 627 | next_col += 1;
|
|---|
| 628 | else if (direction == 16 || direction == 32 || direction == 64)
|
|---|
| 629 | next_col -= 1;
|
|---|
| 630 |
|
|---|
| 631 | if (direction == 64 || direction == 128 || direction == 1)
|
|---|
| 632 | next_row -= 1;
|
|---|
| 633 | else if (direction == 4 || direction == 8 || direction == 16)
|
|---|
| 634 | next_row += 1;
|
|---|
| 635 |
|
|---|
| 636 | if (next_col >= 0 && next_col < ncol
|
|---|
| 637 | && next_row >= 0 && next_row < nrow) {
|
|---|
| 638 | /* allocate and fill the next point structure */
|
|---|
| 639 | list->next = (struct point *)G_malloc(sizeof(struct point));
|
|---|
| 640 | list = list->next;
|
|---|
| 641 | list->row = next_row;
|
|---|
| 642 | list->col = next_col;
|
|---|
| 643 | go = 1;
|
|---|
| 644 | }
|
|---|
| 645 | }
|
|---|
| 646 | } /* end while */
|
|---|
| 647 |
|
|---|
| 648 | /* allocate and fill the end-of-path flag */
|
|---|
| 649 | list->next = (struct point *)G_malloc(sizeof(struct point));
|
|---|
| 650 | list = list->next;
|
|---|
| 651 | list->row = INT_MAX;
|
|---|
| 652 |
|
|---|
| 653 | /* return a pointer to an empty structure */
|
|---|
| 654 | list->next = (struct point *)G_malloc(sizeof(struct point));
|
|---|
| 655 | list = list->next;
|
|---|
| 656 | list->next = NULL;
|
|---|
| 657 |
|
|---|
| 658 | G_free(dir);
|
|---|
| 659 |
|
|---|
| 660 | return list;
|
|---|
| 661 | }
|
|---|
| 662 |
|
|---|
| 663 | struct point *drain_cost(int dir_fd, struct point *list, int nrow, int ncol)
|
|---|
| 664 | {
|
|---|
| 665 | /*
|
|---|
| 666 | * The idea is that each cell of the direction surface has a value representing
|
|---|
| 667 | * the direction towards the next cell in the path. The direction is read from
|
|---|
| 668 | * the input raster, and a simple case/switch is used to determine which cell to
|
|---|
| 669 | * read next. This is repeated via a while loop until a null direction is found.
|
|---|
| 670 | */
|
|---|
| 671 |
|
|---|
| 672 | int neighbour, next_row, next_col, go = 1;
|
|---|
| 673 | DCELL direction;
|
|---|
| 674 | DCELL *dir_buf;
|
|---|
| 675 |
|
|---|
| 676 | dir_buf = Rast_allocate_d_buf();
|
|---|
| 677 |
|
|---|
| 678 | next_row = list->row;
|
|---|
| 679 | next_col = list->col;
|
|---|
| 680 |
|
|---|
| 681 | while (go) {
|
|---|
| 682 | go = 0;
|
|---|
| 683 | /* Directional algorithm
|
|---|
| 684 | * 1) read cell direction
|
|---|
| 685 | * 2) shift to cell in that direction
|
|---|
| 686 | */
|
|---|
| 687 | /* find the direction recorded at row,col */
|
|---|
| 688 | lseek(dir_fd, (off_t) list->row * ncol * sizeof(DCELL), SEEK_SET);
|
|---|
| 689 | read(dir_fd, dir_buf, ncol * sizeof(DCELL));
|
|---|
| 690 | direction = *(dir_buf + list->col);
|
|---|
| 691 | neighbour = direction * 10;
|
|---|
| 692 | if (G_verbose() > 2)
|
|---|
| 693 | G_message(_("direction read: %lf, neighbour found: %i"),
|
|---|
| 694 | direction, neighbour);
|
|---|
| 695 | switch (neighbour) {
|
|---|
| 696 | case 225: /* ENE */
|
|---|
| 697 | next_row = list->row - 1;
|
|---|
| 698 | next_col = list->col + 2;
|
|---|
| 699 | break;
|
|---|
| 700 | case 450: /* NE */
|
|---|
| 701 | next_row = list->row - 1;
|
|---|
| 702 | next_col = list->col + 1;
|
|---|
| 703 | break;
|
|---|
| 704 | case 675: /* NNE */
|
|---|
| 705 | next_row = list->row - 2;
|
|---|
| 706 | next_col = list->col + 1;
|
|---|
| 707 | break;
|
|---|
| 708 | case 900: /* N */
|
|---|
| 709 | next_row = list->row - 1;
|
|---|
| 710 | next_col = list->col;
|
|---|
| 711 | break;
|
|---|
| 712 | case 1125: /* NNW */
|
|---|
| 713 | next_row = list->row - 2;
|
|---|
| 714 | next_col = list->col - 1;
|
|---|
| 715 | break;
|
|---|
| 716 | case 1350: /* NW */
|
|---|
| 717 | next_col = list->col - 1;
|
|---|
| 718 | next_row = list->row - 1;
|
|---|
| 719 | break;
|
|---|
| 720 | case 1575: /* WNW */
|
|---|
| 721 | next_col = list->col - 2;
|
|---|
| 722 | next_row = list->row - 1;
|
|---|
| 723 | break;
|
|---|
| 724 | case 1800: /* W*/
|
|---|
| 725 | next_row = list->row;
|
|---|
| 726 | next_col = list->col - 1;
|
|---|
| 727 | break;
|
|---|
| 728 | case 2025: /* WSW */
|
|---|
| 729 | next_row = list->row + 1;
|
|---|
| 730 | next_col = list->col - 2;
|
|---|
| 731 | break;
|
|---|
| 732 | case 2250: /* SW */
|
|---|
| 733 | next_row = list->row + 1;
|
|---|
| 734 | next_col = list->col - 1;
|
|---|
| 735 | break;
|
|---|
| 736 | case 2475: /* SSW */
|
|---|
| 737 | next_row = list->row + 2;
|
|---|
| 738 | next_col = list->col - 1;
|
|---|
| 739 | break;
|
|---|
| 740 | case 2700: /* S */
|
|---|
| 741 | next_row = list->row + 1;
|
|---|
| 742 | next_col = list->col;
|
|---|
| 743 | break;
|
|---|
| 744 | case 2925: /* SSE */
|
|---|
| 745 | next_row = list->row + 2;
|
|---|
| 746 | next_col = list->col + 1;
|
|---|
| 747 | break;
|
|---|
| 748 | case 3150: /* SE */
|
|---|
| 749 | next_row = list->row + 1;
|
|---|
| 750 | next_col = list->col + 1;
|
|---|
| 751 | break;
|
|---|
| 752 | case 3375: /* ESE */
|
|---|
| 753 | next_row = list->row + 1;
|
|---|
| 754 | next_col = list->col + 2;
|
|---|
| 755 | break;
|
|---|
| 756 | case 3600: /* E */
|
|---|
| 757 | next_row = list->row;
|
|---|
| 758 | next_col = list->col + 1;
|
|---|
| 759 | break;
|
|---|
| 760 | /* default:
|
|---|
| 761 | break;
|
|---|
| 762 | Should probably add something here for the possibility of a non-direction map
|
|---|
| 763 | G_fatal_error(_("Invalid direction given (possibly not a direction map)")); */
|
|---|
| 764 | } /* end switch/case */
|
|---|
| 765 |
|
|---|
| 766 | if (next_col >= 0 && next_col < ncol && next_row >= 0 &&
|
|---|
| 767 | next_row < nrow) {
|
|---|
| 768 | list->next = (struct point *)G_malloc(sizeof(struct point));
|
|---|
| 769 | list = list->next;
|
|---|
| 770 | list->row = next_row;
|
|---|
| 771 | list->col = next_col;
|
|---|
| 772 | next_row = -1;
|
|---|
| 773 | next_col = -1;
|
|---|
| 774 | go = 1;
|
|---|
| 775 | }
|
|---|
| 776 |
|
|---|
| 777 | } /* end while */
|
|---|
| 778 |
|
|---|
| 779 | /* allocate and fill the end-of-path flag */
|
|---|
| 780 | list->next = (struct point *)G_malloc(sizeof(struct point));
|
|---|
| 781 | list = list->next;
|
|---|
| 782 | list->row = INT_MAX;
|
|---|
| 783 |
|
|---|
| 784 | /* return a pointer to an empty structure */
|
|---|
| 785 | list->next = (struct point *)G_malloc(sizeof(struct point));
|
|---|
| 786 | list = list->next;
|
|---|
| 787 | list->next = NULL;
|
|---|
| 788 |
|
|---|
| 789 | G_free(dir_buf);
|
|---|
| 790 |
|
|---|
| 791 | return list;
|
|---|
| 792 | }
|
|---|