[GRASS-SVN] r45833 - grass/branches/develbranch_6/vector/v.buffer
svn_grass at osgeo.org
svn_grass at osgeo.org
Sun Apr 3 08:42:51 EDT 2011
Author: mmetz
Date: 2011-04-03 05:42:51 -0700 (Sun, 03 Apr 2011)
New Revision: 45833
Modified:
grass/branches/develbranch_6/vector/v.buffer/main.c
Log:
fix bug for buffer column option
Modified: grass/branches/develbranch_6/vector/v.buffer/main.c
===================================================================
--- grass/branches/develbranch_6/vector/v.buffer/main.c 2011-04-03 12:42:04 UTC (rev 45832)
+++ grass/branches/develbranch_6/vector/v.buffer/main.c 2011-04-03 12:42:51 UTC (rev 45833)
@@ -4,10 +4,11 @@
* MODULE: v.buffer
*
* AUTHOR(S): Radim Blazek
+ * Markus Metz
*
- * PURPOSE: Vector buffer
+ * PURPOSE: Vector buffering
*
- * COPYRIGHT: (C) 2001 by the GRASS Development Team
+ * COPYRIGHT: (C) 2001-2011 by the GRASS Development Team
*
* This program is free software under the
* GNU General Public License (>=v2).
@@ -31,193 +32,81 @@
/* TODO: look at RET value and use, is it OK? */
#define RET 0.000000001 /* Representation error tolerance */
-/* returns shortest distance to nearest input feature within the buffer
- * or a number greater than buffer (2*buffer) if not found */
-double input_distance(struct Map_info *In, int type, double buffer, double x,
- double y)
+struct buf_contours
{
- int i;
- static struct ilist *List = NULL;
- static struct line_pnts *Points = NULL;
- double current_distance;
+ int inner_count;
+ int outer;
+ int *inner;
+};
- BOUND_BOX box;
-
- if (List == NULL)
- List = Vect_new_list();
- else
- Vect_reset_list(List);
-
- if (Points == NULL)
- Points = Vect_new_line_struct();
-
-
- /* Inside area ? */
- if (type & GV_AREA) {
- int area, centroid;
-
- /* inside */
- area = Vect_find_area(In, x, y);
- centroid = 0;
- if (area)
- centroid = Vect_get_area_centroid(In, area);
-
- G_debug(3, " area = %d centroid = %d", area, centroid);
- if (centroid)
- return 0.0;
- }
-
- /* ouside area, within buffer? */
- /* The centroid is in buffer if at least one point/line/boundary is in buffer distance */
- box.E = x + buffer;
- box.W = x - buffer;
- box.N = y + buffer;
- box.S = y - buffer;
- box.T = PORT_DOUBLE_MAX;
- box.B = -PORT_DOUBLE_MAX;
-
- Vect_select_lines_by_box(In, &box, GV_POINTS | GV_LINES, List);
- G_debug(3, " %d lines selected by box", List->n_values);
-
- current_distance = 2 * buffer;
- for (i = 0; i < List->n_values; i++) {
- int line, ltype;
- double dist;
-
- line = List->value[i];
-
- G_debug(3, " line[%d] = %d", i, line);
-
- ltype = Vect_read_line(In, Points, NULL, line);
-
- Vect_line_distance(Points, x, y, 0., 0, NULL, NULL, NULL, &dist, NULL,
- NULL);
- G_debug(3, " dist = %f", dist);
- if (dist > buffer)
- continue;
-
- /* lines */
- if (type & ltype) {
- if (dist < current_distance)
- current_distance = dist;
- }
-
- /* Areas */
- if ((type & GV_AREA) && ltype == GV_BOUNDARY) {
- int j, side[2], centr[2], area_in;
-
- Vect_get_line_areas(In, line, &side[0], &side[1]);
-
- for (j = 0; j < 2; j++) {
- centr[j] = 0;
-
- if (side[j] > 0)
- area_in = side[j];
- else /* island */
- area_in = Vect_get_isle_area(In, abs(side[j]));
-
- if (area_in > 0)
- centr[j] = Vect_get_area_centroid(In, area_in);
- }
-
- if (centr[0] || centr[1]) {
- if (dist < current_distance)
- current_distance = dist;
- }
- }
- }
- return current_distance;
-}
-
/*
- * Test if area in Out is in buffer.
+ * Test if area in Out is in buffer, using the x,y coords of the area centroid.
* Return: 1 in buffer
* 0 outside buffer
*/
-int area_in_buffer(struct Map_info *In, struct Map_info *Out,
- int area, int type, double buffer, double tolerance)
+int point_in_buffer(struct buf_contours *arr_bc, SPATIAL_INDEX *si,
+ struct Map_info *Buf, double x, double y)
{
- double dist;
- int ret, i;
+ int i, j, ret, flag = 0;
+ BOUND_BOX bbox;
static struct ilist *List = NULL;
static struct line_pnts *Points = NULL;
- double x, y;
-
- G_debug(3, " area_in_buffer(): area = %d", area);
-
+
if (List == NULL)
List = Vect_new_list();
-
if (Points == NULL)
Points = Vect_new_line_struct();
- /* Warning: because of tolerance, centroid in area calculated by Vect_get_point_in_area()
- * may fall into buffer (arc interpolated by polygon) even if area is outside!!! */
+ /* select outer contours overlapping with centroid (x, y) */
+ bbox.W = bbox.E = x;
+ bbox.N = bbox.S = y;
+ bbox.T = PORT_DOUBLE_MAX;
+ bbox.B = -PORT_DOUBLE_MAX;
- /* Because of finite number of decimal places in double representation, RET is used. */
+ Vect_spatial_index_select(si, &bbox, List);
- /* Test1: Centroid (the only reliable, I think).
- * There are 3 possible cases for the distance of centroid to nearest input feature:
- * 1) < (buffer - tolerance) => area inside the buffer
- * 2) > buffer => area outside the buffer
- * 3) > (buffer - tolerance) and < buffer (that means within the tolerance) => uncertain */
- ret = Vect_get_point_in_area(Out, area, &x, &y);
- if (ret == 0) { /* centroid OK */
- /* test the distance to nearest input feature */
- dist = input_distance(In, type, buffer, x, y);
- G_debug(3, " centroid %f, %f dist = %f", x, y, dist);
+ for (i = 0; i < List->n_values; i++) {
+ Vect_read_line(Buf, Points, NULL, arr_bc[List->value[i]].outer);
+ ret = Vect_point_in_poly(x, y, Points);
+ if (ret == 0)
+ continue;
- if (dist < (buffer - tolerance - RET)) {
- G_debug(3, " centroid in buffer -> area in buffer");
- return 1;
- }
- else if (dist > (buffer + RET)) {
- G_debug(3, " centroid outside buffer -> area outside buffer");
- return 0;
- }
- }
+ flag = 1; /* inside outer contour */
+ for (j = 0; j < arr_bc[List->value[i]].inner_count; j++) {
+ if (arr_bc[List->value[i]].inner[j] < 1)
+ continue;
- /* Test2: counterclockwise (ccw) boundary
- * Boundaries around features are written in ccw order, that means area inside buffer is on the
- * left side. Area bundaries are listed in cw order (ccw boundaries as negative).
- * So if any boundary in area list is negative, i.e. in cww order, i.e. buffer inside area,
- * whole area _must_ be in buffer. But, also areas without such boundary may be in buffer,
- * see below. */
- /* TODO: The problem!!!: unfortunately it is not true. After snap, break and remove duplicate
- * it may happen that ccw line appeareas in the area outside the buffer!!! */
- Vect_get_area_boundaries(Out, area, List);
-
- for (i = 0; i < List->n_values; i++) {
- if (List->value[i] < 0) {
- G_debug(3, " negative boundary -> area in buffer");
- return 1;
+ Vect_read_line(Buf, Points, NULL, arr_bc[List->value[i]].inner[j]);
+ ret = Vect_point_in_poly(x, y, Points);
+ if (ret != 0) { /* inside inner contour */
+ flag = 0;
+ break;
+ }
}
- }
- /* Test3: Input feature within area.
- * I believe, that if no negative boundary was found and area is in buffer,
- * the distance of the nearest input feature for at least one area vertex must be < (buffer-tolerance)
- * This test is left as last because it is slow (check each vertex). */
- Vect_get_area_points(Out, area, Points);
- for (i = 0; i < Points->n_points - 1; i++) {
- dist = input_distance(In, type, buffer, Points->x[i], Points->y[i]);
- G_debug(3, " vertex dist = %f", dist);
-
- if (dist < (buffer - tolerance - RET)) {
- G_debug(3, " vertex in buffer -> area in buffer");
+ if (flag) {
+ /* (x,y) is inside outer contour and outside inner contours of arr_bc[List->value[i]] */
return 1;
}
}
- /* Test4?: It may be that that Test3 does not cover all possible cases. If so, somebody must
- * find such example and send it to me */
-
- G_debug(3, " -> area outside buffer");
return 0;
}
-void stop(struct Map_info *In, struct Map_info *Out)
+void stop(struct Map_info *In, struct Map_info *Out, struct Map_info *Buf,
+ char *bufname, SPATIAL_INDEX *si, int debug)
{
+ Vect_spatial_index_destroy(si);
+
+ if (debug != DEBUG_NONE) {
+ Vect_build(Buf);
+ Vect_close(Buf);
+ }
+ else {
+ Vect_close(Buf);
+ Vect_delete(bufname);
+ }
+
Vect_close(In);
G_message(_("Rebuilding topology..."));
@@ -228,10 +117,10 @@
int main(int argc, char *argv[])
{
- struct Map_info In, Out;
+ struct Map_info In, Out, Buf;
struct line_pnts *Points, *BPoints;
struct line_cats *Cats, *BCats;
- char *mapset;
+ char *mapset, bufname[GNAME_MAX];
struct GModule *module;
struct Option *in_opt, *out_opt, *type_opt, *buffer_opt, *dist_opt,
*tolerance_opt, *bufcol_opt, *scale_opt, *debug_opt, *field_opt;
@@ -241,8 +130,12 @@
char *Areas, *Lines;
int field;
int dist_answer;
+ struct buf_contours *arr_bc;
+ int buffers_count = 0, line_id;
+ SPATIAL_INDEX si;
+ BOUND_BOX bbox;
- /* Attributes if sizecol is used */
+ /* Attributes if bufcol is used */
int i, nrec, ctype;
struct field_info *Fi;
dbDriver *Driver;
@@ -264,6 +157,7 @@
type_opt->answer = "point,line,area";
field_opt = G_define_standard_option(G_OPT_V_FIELD);
+ field_opt->answer = "-1";
buffer_opt = G_define_option();
buffer_opt->key = "buffer";
@@ -321,14 +215,16 @@
dist_answer = (buffer_opt->answer != NULL || dist_opt->answer != NULL);
if ((dist_answer && bufcol_opt->answer) ||
- (!(dist_opt->answer || bufcol_opt->answer)))
+ (!(dist_answer || bufcol_opt->answer)))
G_fatal_error("Select a buffer distance or column, but not both.");
+ /* fixed
if (bufcol_opt->answer)
G_warning(_("The bufcol option may contain bugs during the cleaning "
"step. If you encounter problems, use the debug "
"option or clean manually with v.clean tool=break; "
"v.category step=0; v.extract -d type=area"));
+ */
orig_tolerance = atof(tolerance_opt->answer);
tolerance = orig_tolerance;
@@ -340,13 +236,13 @@
if (dist_opt->answer)
buffer = fabs(atof(dist_opt->answer));
else if (buffer_opt->answer)
- buffer = fabs(atof(dist_opt->answer));
+ buffer = fabs(atof(buffer_opt->answer));
if (dist_answer) {
tolerance *= buffer;
- G_message(_("The tolerance in map units: %g"), tolerance);
+ G_verbose_message(_("The tolerance in map units: %g"), tolerance);
/* At least 8 points for circle. */
dtmp = 0.999 * buffer * (1 - cos(2 * PI / 8 / 2));
@@ -382,11 +278,39 @@
Vect_set_open_level(2);
Vect_open_old(&In, in_opt->answer, mapset);
+ /* allocate space for buffer ids */
+ nlines = nareas = 0;
+ if ((type & GV_POINTS) || (type & GV_LINES))
+ nlines += Vect_get_num_primitives(&In, type);
+ if (type & GV_AREA)
+ nareas = Vect_get_num_areas(&In);
+
+ if (nlines + nareas == 0) {
+ G_warning(_("No features available for buffering. "
+ "Check type option and features available in the input vector."));
+ exit(EXIT_SUCCESS);
+ }
+
+ buffers_count = 1;
+ arr_bc = G_malloc((nlines + nareas + 1) * sizeof(struct buf_contours));
+
+ Vect_spatial_index_init(&si);
+
Vect_set_fatal_error(GV_FATAL_PRINT);
if (0 > Vect_open_new(&Out, out_opt->answer, 0)) {
Vect_close(&In);
exit(EXIT_FAILURE);
}
+
+ /* open tmp vector for buffers, needed for cleaning */
+ sprintf(bufname, "%s_buffers", out_opt->answer);
+ if (0 > Vect_open_new(&Buf, bufname, 0)) {
+ Vect_close(&In);
+ Vect_close(&Out);
+ Vect_delete(out_opt->answer);
+ exit(EXIT_FAILURE);
+ }
+ Vect_build_partial(&Buf, GV_BUILD_BASE);
/* check and load attribute column data */
if (bufcol_opt->answer) {
@@ -439,13 +363,13 @@
/* Lines (and Points) */
if ((type & GV_POINTS) || (type & GV_LINES)) {
- int nlines, line, ltype;
+ int line, ltype, looped;
+ double pbuffer;
- nlines = Vect_get_num_lines(&In);
-
- G_message(_("Lines buffers... "));
+ G_message(_("Line buffers... "));
for (line = 1; line <= nlines; line++) {
int cat;
+ double area_size = 0, inner_size = 0;
G_debug(3, "line = %d", line);
G_percent(line, nlines, 2);
@@ -510,41 +434,114 @@
Vect_line_prune(Points);
/* looped line ? */
- if (Points->n_points > 1 && Points->x[0] == Points->x[Points->n_points - 1] &&
- Points->y[0] == Points->y[Points->n_points - 1]) {
- G_debug(0, "looped line");
- Vect_line_parallel(Points, buffer, tolerance, 1, BPoints);
- if (BPoints->n_points > 1)
+ looped = 0;
+ if (Points->n_points > 3) {
+ if (Points->x[0] == Points->x[Points->n_points - 1] &&
+ Points->y[0] == Points->y[Points->n_points - 1]) {
+
+ G_debug(2, "looped line");
+
+ /* determine correct sides for outer and inner contours */
+ dig_find_area_poly(Points, &area_size);
+ if (area_size == 0) {
+ G_warning("zero area size");
+ looped = 0;
+ }
+ else if (area_size > 0)
+ pbuffer = -buffer;
+ else
+ pbuffer = buffer;
+
+ looped = 1;
+ }
+ if (!looped) {
+ double dx = Points->x[0] - Points->x[Points->n_points - 1];
+ double dy = Points->y[0] - Points->y[Points->n_points - 1];
+ double dist = sqrt(dx * dx + dy * dy);
+
+ /* if the distance between endpoints is < 2 * buffer
+ * and at least one point is > 2 * buffer away from the endpoints,
+ * problems may occur
+ * break up the line if possible
+ * find point farthest away from end point
+ * if this point is > 2 * buffer away from end point, break
+ * first line from start point to this point
+ * second line from this point to end point */
+
+ looped = 0;
+ }
+ }
+
+ if (looped) {
+ Vect_line_parallel(Points, pbuffer, tolerance, 1, BPoints);
+ if (BPoints->n_points > 3) {
+ if (BPoints->x[0] != BPoints->x[BPoints->n_points - 1] ||
+ BPoints->x[0] != BPoints->x[BPoints->n_points - 1]) {
+ Vect_append_point(BPoints, BPoints->x[0], BPoints->y[0], 0);
+ }
Vect_write_line(&Out, GV_BOUNDARY, BPoints, BCats);
- Vect_line_parallel(Points, -buffer, tolerance, 1, BPoints);
- if (BPoints->n_points > 1)
- Vect_write_line(&Out, GV_BOUNDARY, BPoints, BCats);
+ line_id = Vect_write_line(&Buf, GV_BOUNDARY, BPoints, Cats);
+ /* add buffer to spatial index */
+ Vect_get_line_box(&Buf, line_id, &bbox);
+ Vect_spatial_index_add_item(&si, buffers_count, &bbox);
+ arr_bc[buffers_count].outer = line_id;
+ arr_bc[buffers_count].inner_count = 0;
+ }
+ else
+ G_fatal_error(_("Could not get outside buffer for line id %d"), line);
+
+ Vect_line_parallel(Points, -pbuffer, tolerance, 1, BPoints);
+ if (BPoints->n_points > 3) {
+ if (BPoints->x[0] != BPoints->x[BPoints->n_points - 1] ||
+ BPoints->x[0] != BPoints->x[BPoints->n_points - 1]) {
+ Vect_append_point(BPoints, BPoints->x[0], BPoints->y[0], 0);
+ }
+ dig_find_area_poly(BPoints, &inner_size);
+ /* area size of inner contour must be smaller than area size of original points */
+ if (fabs(inner_size) < fabs(area_size)) {
+ Vect_write_line(&Out, GV_BOUNDARY, BPoints, BCats);
+ line_id = Vect_write_line(&Buf, GV_BOUNDARY, BPoints, Cats);
+ arr_bc[buffers_count].inner = G_malloc(sizeof(int));
+ arr_bc[buffers_count].inner_count = 1;
+ arr_bc[buffers_count].inner[0] = line_id;
+ }
+ }
+ buffers_count++;
}
else {
Vect_line_buffer(Points, buffer, tolerance, BPoints);
Vect_write_line(&Out, GV_BOUNDARY, BPoints, BCats);
+ line_id = Vect_write_line(&Buf, GV_BOUNDARY, BPoints, Cats);
+
+ /* add buffer to spatial index */
+ Vect_get_line_box(&Buf, line_id, &bbox);
+ Vect_spatial_index_add_item(&si, buffers_count, &bbox);
+ arr_bc[buffers_count].outer = line_id;
+ arr_bc[buffers_count].inner_count = 0;
+ buffers_count++;
}
}
}
/* Areas */
if (type & GV_AREA) {
- int i, nareas, area, centroid, nisles, isle;
+ int i, area, centroid, nisles, isle, line_id;
- nareas = Vect_get_num_areas(&In);
-
- G_message(_("Areas buffers... "));
+ G_message(_("Area buffers... "));
for (area = 1; area <= nareas; area++) {
int cat;
G_percent(area, nareas, 2);
+
+ if (!(Vect_area_alive(&In, area)))
+ continue;
centroid = Vect_get_area_centroid(&In, area);
if (centroid == 0)
continue;
Vect_read_line(&In, NULL, Cats, centroid);
- if (!Vect_cat_get(Cats, field, &cat))
+ if (field > 0 && !Vect_cat_get(Cats, field, &cat))
continue;
if (bufcol_opt->answer) {
@@ -601,16 +598,26 @@
/* outer ring */
Vect_get_area_points(&In, area, Points);
+ Vect_line_prune(Points);
Vect_line_parallel(Points, -buffer, tolerance, 1, BPoints);
Vect_write_line(&Out, GV_BOUNDARY, BPoints, BCats);
+ line_id = Vect_write_line(&Buf, GV_BOUNDARY, BPoints, Cats);
+ /* add outer ring to spatial index */
+ Vect_get_line_box(&Buf, line_id, &bbox);
+ Vect_spatial_index_add_item(&si, buffers_count, &bbox);
+ arr_bc[buffers_count].outer = line_id;
+
/* islands */
nisles = Vect_get_area_num_isles(&In, area);
+ arr_bc[buffers_count].inner = G_malloc(nisles * sizeof(int));
+ arr_bc[buffers_count].inner_count = nisles;
for (i = 0; i < nisles; i++) {
- double l;
+ double l, isle_size, inner_size;
isle = Vect_get_area_isle(&In, area, i);
Vect_get_isle_points(&In, isle, Points);
+ Vect_line_prune(Points);
/* Check if the isle is big enough */
l = Vect_line_length(Points);
@@ -618,14 +625,29 @@
continue;
Vect_line_parallel(Points, -buffer, tolerance, 1, BPoints);
- if (BPoints->n_points > 1)
- Vect_write_line(&Out, GV_BOUNDARY, BPoints, BCats);
+ if (BPoints->n_points > 3) {
+ dig_find_area_poly(Points, &isle_size);
+ dig_find_area_poly(BPoints, &inner_size);
+ /* area size of inner contour must be smaller than isle size */
+ if (fabs(inner_size) < fabs(isle_size)) {
+ Vect_write_line(&Out, GV_BOUNDARY, BPoints, BCats);
+ line_id = Vect_write_line(&Buf, GV_BOUNDARY, BPoints, Cats);
+ arr_bc[buffers_count].inner[i] = line_id;
+ }
+ else {
+ arr_bc[buffers_count].inner[i] = -1;
+ }
+ }
+ else {
+ arr_bc[buffers_count].inner[i] = -1;
+ }
}
+ buffers_count++;
}
}
if (debug == DEBUG_BUFFER) {
- stop(&In, &Out);
+ stop(&In, &Out, &Buf, bufname, &si, debug);
exit(EXIT_SUCCESS);
}
@@ -675,30 +697,36 @@
nareas = Vect_get_num_areas(&Out);
Areas = (char *)G_calloc(nareas + 1, sizeof(char));
- G_message(_("Calculating centroids for areas..."));
+ G_message(_("Selecting areas..."));
for (area = 1; area <= nareas; area++) {
+ double x, y;
+
G_percent(area, nareas, 2);
+ Areas[area] = 0;
+
G_debug(3, "area = %d", area);
-/*** BUG *** if dynamic bufcol was used, "buffer" will only hold last value ***/
-/* TODO: use method of v.buffer2, improve it by using a custom spatial index
- * and storing original, not cleaned buffers in a temp vector */
- ret = area_in_buffer(&In, &Out, area, type, buffer, tolerance);
+ if (!Vect_area_alive(&Out, area))
+ continue;
+ ret = Vect_get_point_in_area(&Out, area, &x, &y);
+ if (ret < 0) {
+ G_warning(_("Cannot calculate area centroid"));
+ continue;
+ }
+
+ ret = point_in_buffer(arr_bc, &si, &Buf, x, y);
+
if (ret) {
G_debug(3, " -> in buffer");
Areas[area] = 1;
}
+ else
+ G_debug(3, " -> not in buffer");
- /* Write out centroid (before check if correct, so that it isd visible for debug) */
+ /* Write out centroid (all centroids, so that it is visible for debug) */
if (debug == DEBUG_CLEAN) {
- double x, y;
- ret = Vect_get_point_in_area(&Out, area, &x, &y);
- if (ret < 0) {
- G_warning(_("Cannot calculate area centroid"));
- continue;
- }
Vect_reset_cats(Cats);
if (Areas[area])
Vect_cat_set(Cats, 1, 1);
@@ -710,7 +738,7 @@
}
if (debug == DEBUG_CLEAN) {
- stop(&In, &Out);
+ stop(&In, &Out, &Buf, bufname, &si, debug);
exit(EXIT_SUCCESS);
}
@@ -721,9 +749,11 @@
G_message(_("Generating list of boundaries to be deleted..."));
for (line = 1; line <= nlines; line++) {
- G_percent(line, nlines, 2);
int j, side[2], areas[2];
+ G_percent(line, nlines, 2);
+ Lines[line] = 0;
+
G_debug(3, "line = %d", line);
if (!Vect_line_alive(&Out, line))
@@ -769,25 +799,23 @@
G_message(_("Calculating centroids for areas..."));
for (area = 1; area <= nareas; area++) {
- G_percent(area, nareas, 2);
double x, y;
+ G_percent(area, nareas, 2);
G_debug(3, "area = %d", area);
if (!Vect_area_alive(&Out, area))
continue;
-/*** BUG *** if dynamic bufcol was used, "buffer" will only hold last value ***/
-/* TODO: see above */
- ret = area_in_buffer(&In, &Out, area, type, buffer, tolerance);
+ ret = Vect_get_point_in_area(&Out, area, &x, &y);
+ if (ret < 0) {
+ G_warning(_("Cannot calculate area centroid"));
+ continue;
+ }
+ ret = point_in_buffer(arr_bc, &si, &Buf, x, y);
+
if (ret) {
- ret = Vect_get_point_in_area(&Out, area, &x, &y);
- if (ret < 0) {
- G_warning(_("Cannot calculate area centroid"));
- continue;
- }
-
Vect_reset_line(Points);
Vect_append_point(Points, x, y, 0.);
Vect_write_line(&Out, GV_CENTROID, Points, Cats);
@@ -797,6 +825,6 @@
G_message(_("Attaching centroids..."));
Vect_build_partial(&Out, GV_BUILD_CENTROIDS);
- stop(&In, &Out);
+ stop(&In, &Out, &Buf, bufname, &si, debug);
exit(EXIT_SUCCESS);
}
More information about the grass-commit
mailing list