123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- /****************************************************************************
- *
- * MODULE: r.carve
- *
- * AUTHOR(S): Original author Bill Brown, UIUC GIS Laboratory
- * Brad Douglas <rez touchofmadness com>
- *
- * PURPOSE: Takes vector stream data, converts it to 3D raster and
- * subtracts a specified depth
- *
- * COPYRIGHT: (C) 2006 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 <stdio.h>
- #include <string.h>
- #include <math.h>
- #include <grass/gis.h>
- #include <grass/raster.h>
- #include <grass/glocale.h>
- #include "enforce.h"
- #ifndef MAX
- # define MIN(a,b) ((a<b) ? a : b)
- # define MAX(a,b) ((a>b) ? a : b)
- #endif
- /* function prototypes */
- static void clear_bitmap(struct BM *bm);
- static int process_line(struct Map_info *Map, struct Map_info *outMap,
- void *rbuf, const int line, const struct parms *parm);
- static void traverse_line_flat(Point2 * pgpts, const int pt, const int npts);
- static void traverse_line_noflat(Point2 * pgpts, const double depth,
- const int pt, const int npts);
- static void set_min_point(void *buf, const int col, const int row,
- const double elev, const double depth,
- const RASTER_MAP_TYPE rtype);
- static double lowest_cell_near_point(void *data, const RASTER_MAP_TYPE rtype,
- const double px, const double py,
- const double rad);
- static void process_line_segment(const int npts, void *rbuf, Point2 * pgxypts,
- Point2 * pgpts, struct BM *bm,
- struct Map_info *outMap,
- const struct parms *parm);
- /******************************************************************
- * Returns 0 on success, -1 on error, 1 on warning, writing message
- * to errbuf for -1 or 1 */
- int enforce_downstream(int infd, int outfd,
- struct Map_info *Map, struct Map_info *outMap,
- struct parms *parm)
- {
- struct Cell_head wind;
- int retval = 0;
- int line, nlines;
- void *rbuf = NULL;
- /* width used here is actually distance to center of stream */
- parm->swidth /= 2;
- G_get_window(&wind);
- Vect_set_constraint_region(Map, wind.north, wind.south, wind.east,
- wind.west, wind.top, wind.bottom);
- /* allocate and clear memory for entire raster */
- rbuf =
- G_calloc(Rast_window_rows() * Rast_window_cols(),
- Rast_cell_size(parm->raster_type));
- /* first read whole elevation file into buf */
- read_raster(rbuf, infd, parm->raster_type);
- G_message(_("Processing lines... "));
- nlines = Vect_get_num_lines(Map);
- for (line = 1; line <= nlines; line++)
- retval = process_line(Map, outMap, rbuf, line, parm);
- /* write output raster map */
- write_raster(rbuf, outfd, parm->raster_type);
- G_free(rbuf);
- return retval;
- }
- static int process_line(struct Map_info *Map, struct Map_info *outMap,
- void *rbuf, const int line, const struct parms *parm)
- {
- int i, retval = 0;
- int do_warn = 0;
- int npts = 0;
- int in_out = 0;
- int first_in = -1;
- double totdist = 0.;
- struct Cell_head wind;
- static struct line_pnts *points = NULL;
- static struct line_cats *cats = NULL;
- static struct BM *bm = NULL;
- Point2 *pgpts, *pgxypts;
- PointGrp pg;
- PointGrp pgxy; /* copy any points in region to this one */
- G_get_window(&wind);
- if (!points)
- points = Vect_new_line_struct();
- if (!cats)
- cats = Vect_new_cats_struct();
- if (!(Vect_read_line(Map, points, cats, line) & GV_LINE))
- return 0;
- if (!bm)
- bm = BM_create(Rast_window_cols(), Rast_window_rows());
- clear_bitmap(bm);
- pg_init(&pg);
- pg_init(&pgxy);
- G_percent(line, Vect_get_num_lines(Map), 10);
- for (i = 0; i < points->n_points; i++) {
- Point2 pt, ptxy;
- double elev;
- int row = Rast_northing_to_row(points->y[i], &wind);
- int col = Rast_easting_to_col(points->x[i], &wind);
- /* rough clipping */
- if (row < 0 || row > Rast_window_rows() - 1 ||
- col < 0 || col > Rast_window_cols() - 1) {
- if (first_in != -1)
- in_out = 1;
- G_debug(1, "outside region - row:%d col:%d", row, col);
- continue;
- }
- if (first_in < 0)
- first_in = i;
- else if (in_out)
- do_warn = 1;
- elev = lowest_cell_near_point(rbuf, parm->raster_type, points->x[i],
- points->y[i], parm->swidth);
- ptxy[0] = points->x[i];
- ptxy[1] = points->y[i];
- pt[1] = elev;
- /* get distance from this point to previous point */
- if (i)
- totdist += G_distance(points->x[i - 1], points->y[i - 1],
- points->x[i], points->y[i]);
- pt[0] = totdist;
- pg_addpt(&pg, pt);
- pg_addpt(&pgxy, ptxy);
- npts++;
- }
- if (do_warn) {
- G_warning(_("Vect runs out of region and re-enters - "
- "this case is not yet implemented."));
- retval = 1;
- }
- /* now check to see if points go downslope(inorder) or upslope */
- if (pg_y_from_x(&pg, 0.0) > pg_y_from_x(&pg, totdist)) {
- pgpts = pg_getpoints(&pg);
- pgxypts = pg_getpoints(&pgxy);
- }
- else {
- /* pgpts is now high to low */
- pgpts = pg_getpoints_reversed(&pg);
- for (i = 0; i < npts; i++)
- pgpts[i][0] = totdist - pgpts[i][0];
- pgxypts = pg_getpoints_reversed(&pgxy);
- }
- for (i = 0; i < (npts - 1); i++) {
- if (parm->noflat)
- /* make sure there are no flat segments in line */
- traverse_line_noflat(pgpts, parm->sdepth, i, npts);
- else
- /* ok to have flat segments in line */
- traverse_line_flat(pgpts, i, npts);
- }
- process_line_segment(npts, rbuf, pgxypts, pgpts, bm, outMap, parm);
- return retval;
- }
- static void clear_bitmap(struct BM *bm)
- {
- int i, j;
- for (i = 0; i < Rast_window_rows(); i++)
- for (j = 0; j < Rast_window_cols(); j++)
- BM_set(bm, i, j, 0);
- }
- static void traverse_line_flat(Point2 * pgpts, const int pt, const int npts)
- {
- int j, k;
- if (pgpts[pt + 1][1] <= pgpts[pt][1])
- return;
- for (j = (pt + 2); j < npts; j++)
- if (pgpts[j][1] <= pgpts[pt][1])
- break;
- if (j == npts) {
- /* if we got to the end, level it out */
- for (j = (pt + 1); j < npts; j++)
- pgpts[j][1] = pgpts[pt][1];
- }
- else {
- /* linear interp between point pt and the next < */
- for (k = (pt + 1); k < j; k++)
- pgpts[k][1] = LINTERP(pgpts[j][1], pgpts[pt][1],
- (pgpts[j][0] - pgpts[k][0]) /
- (pgpts[j][0] - pgpts[pt][0]));
- }
- }
- static void traverse_line_noflat(Point2 * pgpts, const double depth,
- const int pt, const int npts)
- {
- int j, k;
- if (pgpts[pt + 1][1] < pgpts[pt][1])
- return;
- for (j = (pt + 2); j < npts; j++)
- if (pgpts[j][1] < pgpts[pt][1])
- break;
- if (j == npts) {
- /* if we got to the end, lower end by depth OR .01 */
- --j;
- pgpts[j][1] = pgpts[pt][1] - (depth > 0 ? depth : 0.01);
- }
- /* linear interp between point pt and the next < */
- for (k = (pt + 1); k < j; k++)
- pgpts[k][1] = LINTERP(pgpts[j][1], pgpts[pt][1],
- (pgpts[j][0] - pgpts[k][0]) /
- (pgpts[j][0] - pgpts[pt][0]));
- }
- /* sets value for a cell */
- static void set_min_point(void *data, const int col, const int row,
- const double elev, const double depth,
- const RASTER_MAP_TYPE rtype)
- {
- switch (rtype) {
- case CELL_TYPE:
- {
- CELL *cbuf = data;
- cbuf[row * Rast_window_cols() + col] =
- MIN(cbuf[row * Rast_window_cols() + col], elev) - (int)depth;
- }
- break;
- case FCELL_TYPE:
- {
- FCELL *fbuf = data;
- fbuf[row * Rast_window_cols() + col] =
- MIN(fbuf[row * Rast_window_cols() + col], elev) - depth;
- }
- break;
- case DCELL_TYPE:
- {
- DCELL *dbuf = data;
- dbuf[row * Rast_window_cols() + col] =
- MIN(dbuf[row * Rast_window_cols() + col], elev) - depth;
- }
- break;
- }
- }
- /* returns the lowest value cell within radius rad of px, py */
- static double lowest_cell_near_point(void *data, const RASTER_MAP_TYPE rtype,
- const double px, const double py,
- const double rad)
- {
- int r, row, col, row1, row2, col1, col2, rowoff, coloff;
- int rastcols, rastrows;
- double min;
- struct Cell_head wind;
- G_get_window(&wind);
- rastrows = Rast_window_rows();
- rastcols = Rast_window_cols();
- Rast_set_d_null_value(&min, 1);
- /* kludge - fix for lat_lon */
- rowoff = rad / wind.ns_res;
- coloff = rad / wind.ew_res;
- row = Rast_northing_to_row(py, &wind);
- col = Rast_easting_to_col(px, &wind);
- /* get bounding box of line segment */
- row1 = MAX(0, row - rowoff);
- row2 = MIN(rastrows - 1, row + rowoff);
- col1 = MAX(0, col - coloff);
- col2 = MIN(rastcols - 1, col + coloff);
- switch (rtype) {
- case CELL_TYPE:
- {
- CELL *cbuf = data;
- if (!(Rast_is_c_null_value(&cbuf[row1 * rastcols + col1])))
- min = cbuf[row1 * rastcols + col1];
- }
- break;
- case FCELL_TYPE:
- {
- FCELL *fbuf = data;
- if (!(Rast_is_f_null_value(&fbuf[row1 * rastcols + col1])))
- min = fbuf[row1 * rastcols + col1];
- }
- break;
- case DCELL_TYPE:
- {
- DCELL *dbuf = data;
- if (!(Rast_is_d_null_value(&dbuf[row1 * rastcols + col1])))
- min = dbuf[row1 * rastcols + col1];
- }
- break;
- }
- for (r = row1; r < row2; r++) {
- double cy = Rast_row_to_northing(r + 0.5, &wind);
- int c;
- for (c = col1; c < col2; c++) {
- double cx = Rast_col_to_easting(c + 0.5, &wind);
- if (G_distance(px, py, cx, cy) <= SQR(rad)) {
- switch (rtype) {
- case CELL_TYPE:
- {
- CELL *cbuf = data;
- if (Rast_is_d_null_value(&min)) {
- if (!(Rast_is_c_null_value(&cbuf[r * rastcols + c])))
- min = cbuf[r * rastcols + c];
- }
- else {
- if (!(Rast_is_c_null_value(&cbuf[r * rastcols + c])))
- if (cbuf[r * rastcols + c] < min)
- min = cbuf[r * rastcols + c];
- }
- }
- break;
- case FCELL_TYPE:
- {
- FCELL *fbuf = data;
- if (Rast_is_d_null_value(&min)) {
- if (!(Rast_is_f_null_value(&fbuf[r * rastcols + c])))
- min = fbuf[r * rastcols + c];
- }
- else {
- if (!(Rast_is_f_null_value(&fbuf[r * rastcols + c])))
- if (fbuf[r * rastcols + c] < min)
- min = fbuf[r * rastcols + c];
- }
- }
- break;
- case DCELL_TYPE:
- {
- DCELL *dbuf = data;
- if (Rast_is_d_null_value(&min)) {
- if (!(Rast_is_d_null_value(&dbuf[r * rastcols + c])))
- min = dbuf[r * rastcols + c];
- }
- else {
- if (!(Rast_is_d_null_value(&dbuf[r * rastcols + c])))
- if (dbuf[r * rastcols + c] < min)
- min = dbuf[r * rastcols + c];
- }
- }
- break;
- }
- }
- }
- }
- G_debug(3, "min:%.2lf", min);
- return min;
- }
- /* Now for each segment in the line, use distance from segment
- * to find beginning row from northernmost point, beginning
- * col from easternmost, ending row & col, then loop through
- * bounding box and use distance from segment to emboss
- * new elevations */
- static void process_line_segment(const int npts, void *rbuf,
- Point2 * pgxypts, Point2 * pgpts,
- struct BM *bm, struct Map_info *outMap,
- const struct parms *parm)
- {
- int i, row1, row2, col1, col2;
- int prevrow, prevcol;
- double cellx, celly, cy;
- struct Cell_head wind;
- struct line_pnts *points = Vect_new_line_struct();
- struct line_cats *cats = Vect_new_cats_struct();
- int rowoff, coloff;
- G_get_window(&wind);
- Vect_cat_set(cats, 1, 1);
- /* kludge - fix for lat_lon */
- rowoff = parm->swidth / wind.ns_res;
- coloff = parm->swidth / wind.ew_res;
- /* get prevrow and prevcol for iteration 0 of following loop */
- prevrow = Rast_northing_to_row(pgxypts[0][1], &wind);
- prevcol = Rast_easting_to_col(pgxypts[0][0], &wind);
- for (i = 1; i < npts; i++) {
- int c, r;
- int row = Rast_northing_to_row(pgxypts[i][1], &wind);
- int col = Rast_easting_to_col(pgxypts[i][0], &wind);
- /* get bounding box of line segment */
- row1 = MAX(0, MIN(row, prevrow) - rowoff);
- row2 = MIN(Rast_window_rows() - 1, MAX(row, prevrow) + rowoff);
- col1 = MAX(0, MIN(col, prevcol) - coloff);
- col2 = MIN(Rast_window_cols() - 1, MAX(col, prevcol) + coloff);
- for (r = row1; r <= row2; r++) {
- cy = Rast_row_to_northing(r + 0.5, &wind);
- for (c = col1; c <= col2; c++) {
- double distance;
- cellx = Rast_col_to_easting(c + 0.5, &wind);
- celly = cy; /* gets written over in distance2... */
- /* Thought about not going past endpoints (use
- * status to check) but then pieces end up missing
- * from outside corners - if it goes past ends,
- * should probably do some interp or will get flats.
- * Here we use a bitmap and only change cells once
- * on the way down */
- distance = sqrt(dig_distance2_point_to_line(cellx, celly, 0,
- pgxypts[i - 1][0],
- pgxypts[i - 1][1], 0,
- pgxypts[i][0], pgxypts[i][1],
- 0, 0, &cellx, &celly, NULL,
- NULL, NULL));
- if (distance <= parm->swidth && !BM_get(bm, c, r)) {
- double dist, elev;
- Vect_reset_line(points);
- dist = G_distance(pgxypts[i][0], pgxypts[i][1],
- cellx, celly);
- elev = LINTERP(pgpts[i][1], pgpts[i - 1][1],
- (dist / (pgpts[i][0] - pgpts[i - 1][0])));
- BM_set(bm, c, r, 1);
- /* TODO - may want to use a function for the
- * cross section of stream */
- set_min_point(rbuf, c, r, elev, parm->sdepth,
- parm->raster_type);
- /* Add point to output vector map */
- if (parm->outvect->answer) {
- Vect_append_point(points, cellx, celly,
- elev - parm->sdepth);
- Vect_write_line(outMap, GV_POINT, points, cats);
- }
- }
- }
- }
- prevrow = row;
- prevcol = col;
- }
- }
|