Exemplo n.º 1
0
void
Vect_break_lines(struct Map_info *Map, int type, struct Map_info *Err)
{
    Vect_break_lines_list(Map, NULL, NULL, type, Err);

    return;
}
Exemplo n.º 2
0
int line_area(struct Map_info *In, int *field, struct Map_info *Tmp,
	      struct Map_info *Out, struct field_info *Fi,
	      dbDriver * driver, int operator, int *ofield,
	      ATTRIBUTES * attr, struct ilist *BList)
{
    int line, nlines, ncat;
    struct line_pnts *Points;
    struct line_cats *Cats, *ACats, *OCats;

    char buf[1000];
    dbString stmt;

    Points = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();
    ACats = Vect_new_cats_struct();
    OCats = Vect_new_cats_struct();
    db_init_string(&stmt);

    G_message(_("Breaking lines..."));
    Vect_break_lines_list(Tmp, NULL, BList, GV_LINE | GV_BOUNDARY, NULL);

    /*
    G_message(_("Merging lines..."));
    Vect_merge_lines(Tmp, GV_LINE, NULL, NULL);
    */

    nlines = Vect_get_num_lines(Tmp);

    /* Warning!: cleaning process (break) creates new vertices which are usually slightly 
     * moved (RE), to compare such new vertex with original input is a problem?
     * 
     * TODO?: would it be better to copy centroids also and query output map? 
     */

    /* Check if the line is inside or outside binput area */
    G_message(_("Selecting lines..."));
    ncat = 1;
    for (line = 1; line <= nlines; line++) {
	int ltype;

	G_percent(line, nlines, 1);	/* must be before any continue */

	if (!Vect_line_alive(Tmp, line))
	    continue;

	ltype = Vect_get_line_type(Tmp, line);

	if (ltype == GV_BOUNDARY) {	/* No more needed */
	    continue;
	}

	/* Now the type should be only GV_LINE */

	/* Decide if the line is inside or outside the area. In theory:
	 * 1) All vertices outside
	 *      - easy, first vertex must be outside
	 * 2) All vertices inside 
	 * 3) All vertices on the boundary, we take it as inside (attention, 
	 *    result of Vect_point_in_area() for points on segments between vertices may be both
	 *    inside or outside, because of representation of numbers)
	 * 4) One or two end vertices on the boundary, all others outside
	 * 5) One or two end vertices on the boundary, all others inside 
	 *
	 */

	/* Note/TODO: the test done is quite simple, check the point in the middle of segment.
	 * If the line overlaps the boundary, the result may be both outside and inside
	 * this should be solved (check angles?)
	 * This should not happen if Vect_break_lines_list() works correctly
	 */

	/* merge here */
	merge_line(Tmp, line, Points, Cats);

	G_debug(3, "line = %d", line);

	point_area(&(In[1]), field[1], (Points->x[0] + Points->x[1]) / 2,
		   (Points->y[0] + Points->y[1]) / 2, ACats);

	if ((ACats->n_cats > 0 && operator == OP_AND) ||
	    (ACats->n_cats == 0 && operator == OP_NOT)) {
	    int i;

	    /* Point is inside */
	    G_debug(3, "OK, write line, line ncats = %d area ncats = %d",
		    Cats->n_cats, ACats->n_cats);

	    Vect_reset_cats(OCats);

	    if (ofield[0]  > 0) {
		/* rewrite with all combinations of acat - bcat (-1 in cycle for null) */
		for (i = -1; i < Cats->n_cats; i++) {	/* line cats */
		    int j;

		    if (i == -1 && Cats->n_cats > 0)
			continue;	/* no need to make null */

		    for (j = -1; j < ACats->n_cats; j++) {
			if (j == -1 && ACats->n_cats > 0)
			    continue;	/* no need to make null */

			if (ofield[0] > 0)
			    Vect_cat_set(OCats, ofield[0], ncat);

			/* Attributes */
			if (driver) {
			    ATTR *at;

			    sprintf(buf, "insert into %s values ( %d", Fi->table,
				    ncat);
			    db_set_string(&stmt, buf);

			    /* cata */
			    if (i >= 0) {
				if (attr[0].columns) {
				    at = find_attr(&(attr[0]), Cats->cat[i]);
				    if (!at)
					G_fatal_error(_("Attribute not found"));

				    if (at->values)
					db_append_string(&stmt, at->values);
				    else
					db_append_string(&stmt,
							 attr[0].null_values);
				}
				else {
				    sprintf(buf, ", %d", Cats->cat[i]);
				    db_append_string(&stmt, buf);
				}
			    }
			    else {
				if (attr[0].columns) {
				    db_append_string(&stmt, attr[0].null_values);
				}
				else {
				    sprintf(buf, ", null");
				    db_append_string(&stmt, buf);
				}
			    }

			    /* catb */
			    if (j >= 0) {
				if (attr[1].columns) {
				    at = find_attr(&(attr[1]), ACats->cat[j]);
				    if (!at)
					G_fatal_error(_("Attribute not found"));

				    if (at->values)
					db_append_string(&stmt, at->values);
				    else
					db_append_string(&stmt,
							 attr[1].null_values);
				}
				else {
				    sprintf(buf, ", %d", ACats->cat[j]);
				    db_append_string(&stmt, buf);
				}
			    }
			    else {
				if (attr[1].columns) {
				    db_append_string(&stmt, attr[1].null_values);
				}
				else {
				    sprintf(buf, ", null");
				    db_append_string(&stmt, buf);
				}
			    }

			    db_append_string(&stmt, " )");

			    G_debug(3, "%s", db_get_string(&stmt));

			    if (db_execute_immediate(driver, &stmt) != DB_OK)
				G_warning(_("Unable to insert new record: '%s'"),
					  db_get_string(&stmt));
			}

			ncat++;
		    }
		}
	    }

	    /* Add cats from input vectors */
	    if (ofield[1] > 0 && field[0] > 0) {
		for (i = 0; i < Cats->n_cats; i++) {
		    if (Cats->field[i] == field[0])
			Vect_cat_set(OCats, ofield[1], Cats->cat[i]);
		}
	    }

	    if (ofield[2] > 0 && field[1] > 0 && ofield[1] != ofield[2]) {
		for (i = 0; i < ACats->n_cats; i++) {
		    if (ACats->field[i] == field[1])
			Vect_cat_set(OCats, ofield[2], ACats->cat[i]);
		}
	    }

	    Vect_write_line(Out, ltype, Points, OCats);
	}
    }

    return 0;
}
Exemplo n.º 3
0
int area_area(struct Map_info *In, int *field, struct Map_info *Tmp,
	      struct Map_info *Out, struct field_info *Fi,
	      dbDriver * driver, int operator, int *ofield,
	      ATTRIBUTES * attr, struct ilist *BList, double snap)
{
    int ret, input, line, nlines, area, nareas;
    int in_area, in_centr, out_cat;
    struct line_pnts *Points;
    struct line_cats *Cats;
    CENTR *Centr;
    char buf[1000];
    dbString stmt;
    int nmodif;
    int verbose;

    verbose = G_verbose();

    Points = Vect_new_line_struct();
    Cats = Vect_new_cats_struct();

    /* optional snap */
    if (snap > 0) {
	int i, j, snapped_lines = 0;
	struct bound_box box;
	struct boxlist *boxlist = Vect_new_boxlist(0);
	struct ilist *reflist = Vect_new_list();
	
	G_message(_("Snapping boundaries with %g ..."), snap);

	/* snap boundaries in B to boundaries in A,
	 * not modifying boundaries in A */

	if (BList->n_values > 1)
	    qsort(BList->value, BList->n_values, sizeof(int), cmp_int);

	snapped_lines = 0;
	nlines = BList->n_values;
	for (i = 0; i < nlines; i++) {
	    line = BList->value[i];
	    Vect_read_line(Tmp, Points, Cats, line);
	    /* select lines by box */
	    Vect_get_line_box(Tmp, line, &box);
	    box.E += snap;
	    box.W -= snap;
	    box.N += snap;
	    box.S -= snap;
	    box.T = 0.0;
	    box.B = 0.0;
	    Vect_select_lines_by_box(Tmp, &box, GV_BOUNDARY, boxlist);
	    
	    if (boxlist->n_values > 0) {
		Vect_reset_list(reflist);
		for (j = 0; j < boxlist->n_values; j++) {
		    int aline = boxlist->id[j];

		    if (!bsearch(&aline, BList->value, BList->n_values,
			sizeof(int), cmp_int)) {
			G_ilist_add(reflist, aline);
		    }
		}
		
		/* snap bline to alines */
		if (Vect_snap_line(Tmp, reflist, Points, snap, 0, NULL, NULL)) {
		    /* rewrite bline*/
		    Vect_delete_line(Tmp, line);
		    ret = Vect_write_line(Tmp, GV_BOUNDARY, Points, Cats);
		    G_ilist_add(BList, ret);
		    snapped_lines++;
		    G_debug(3, "line %d snapped", line);
		}
	    }
	}
	Vect_destroy_boxlist(boxlist);
	Vect_destroy_list(reflist);

	G_verbose_message(n_("%d boundary snapped",
                             "%d boundaries snapped",
                             snapped_lines), snapped_lines);
    }

    /* same procedure like for v.in.ogr:
     * Vect_clean_small_angles_at_nodes() can change the geometry so that new intersections
     * are created. We must call Vect_break_lines(), Vect_remove_duplicates()
     * and Vect_clean_small_angles_at_nodes() until no more small dangles are found */
    do {
	G_message(_("Breaking lines..."));
	Vect_break_lines_list(Tmp, NULL, BList, GV_BOUNDARY, NULL);

	/* Probably not necessary for LINE x AREA */
	G_message(_("Removing duplicates..."));
	Vect_remove_duplicates(Tmp, GV_BOUNDARY, NULL);

	G_message(_("Cleaning boundaries at nodes..."));
	nmodif =
	    Vect_clean_small_angles_at_nodes(Tmp, GV_BOUNDARY, NULL);
    } while (nmodif > 0);

    /* ?: May be result of Vect_break_lines() + Vect_remove_duplicates() any dangle or bridge?
     * In that case, calls to Vect_remove_dangles() and Vect_remove_bridges() would be also necessary */

    G_set_verbose(0);
    /* should be fast, be silent */
    Vect_build_partial(Tmp, GV_BUILD_AREAS);
    G_set_verbose(verbose);
    nlines = Vect_get_num_lines(Tmp);
    ret = 0;
    for (line = 1; line <= nlines; line++) {
	if (!Vect_line_alive(Tmp, line))
	    continue;
	if (Vect_get_line_type(Tmp, line) == GV_BOUNDARY) {
	    int left, rite;
	    
	    Vect_get_line_areas(Tmp, line, &left, &rite);
	    
	    if (left == 0 || rite == 0) {
		/* invalid boundary */
		ret = 1;
		break;
	    }
	}
    }
    if (ret) {
	Vect_remove_dangles(Tmp, GV_BOUNDARY, -1, NULL);
	Vect_remove_bridges(Tmp, NULL, NULL, NULL);
    }

    G_set_verbose(0);
    Vect_build_partial(Tmp, GV_BUILD_NONE);
    Vect_build_partial(Tmp, GV_BUILD_BASE);
    G_set_verbose(verbose);
    G_message(_("Merging lines..."));
    Vect_merge_lines(Tmp, GV_BOUNDARY, NULL, NULL);

    /* Attach islands */
    G_message(_("Attaching islands..."));
    /* can take some time, show messages */
    Vect_build_partial(Tmp, GV_BUILD_ATTACH_ISLES);

    /* Calculate new centroids for all areas */
    nareas = Vect_get_num_areas(Tmp);

    Centr = (CENTR *) G_malloc((nareas + 1) * sizeof(CENTR));	/* index from 1 ! */
    for (area = 1; area <= nareas; area++) {
	ret =
	    Vect_get_point_in_area(Tmp, area, &(Centr[area].x),
				   &(Centr[area].y));
	if (ret < 0) {
	    G_warning(_("Cannot calculate area centroid"));
	    Centr[area].valid = 0;
	}
	else {
	    Centr[area].valid = 1;
	}
    }

    /* Query input maps */
    for (input = 0; input < 2; input++) {
	G_message(_("Querying vector map <%s>..."),
		  Vect_get_full_name(&(In[input])));

	for (area = 1; area <= nareas; area++) {
	    Centr[area].cat[input] = Vect_new_cats_struct();

	    G_percent(area, nareas, 1);

	    in_area =
		Vect_find_area(&(In[input]), Centr[area].x, Centr[area].y);
	    if (in_area > 0) {
		in_centr = Vect_get_area_centroid(&(In[input]), in_area);
		if (in_centr > 0) {
		    int i;

		    Vect_read_line(&(In[input]), NULL, Cats, in_centr);
		    /* Add all cats with original field number */
		    for (i = 0; i < Cats->n_cats; i++) {
			if (Cats->field[i] == field[input]) {
			    ATTR *at;

			    Vect_cat_set(Centr[area].cat[input], ofield[input + 1],
					 Cats->cat[i]);

			    /* Mark as used */
			    at = find_attr(&(attr[input]), Cats->cat[i]);
			    if (!at)
				G_fatal_error(_("Attribute not found"));

			    at->used = 1;
			}
		    }
		}
	    }
	}
    }

    G_message(_("Writing centroids..."));

    db_init_string(&stmt);
    out_cat = 1;
    for (area = 1; area <= nareas; area++) {
	int i;

	G_percent(area, nareas, 1);

	/* check the condition */
	switch (operator) {
	case OP_AND:
	    if (!
		(Centr[area].cat[0]->n_cats > 0 &&
		 Centr[area].cat[1]->n_cats > 0))
		continue;
	    break;
	case OP_OR:
	    if (!
		(Centr[area].cat[0]->n_cats > 0 ||
		 Centr[area].cat[1]->n_cats > 0))
		continue;
	    break;
	case OP_NOT:
	    if (!
		(Centr[area].cat[0]->n_cats > 0 &&
		 !(Centr[area].cat[1]->n_cats > 0)))
		continue;
	    break;
	case OP_XOR:
	    if ((Centr[area].cat[0]->n_cats > 0 &&
		 Centr[area].cat[1]->n_cats > 0) ||
		(!(Centr[area].cat[0]->n_cats > 0) &&
		 !(Centr[area].cat[1]->n_cats > 0)))
		continue;
	    break;
	}

	Vect_reset_line(Points);
	Vect_reset_cats(Cats);

	Vect_append_point(Points, Centr[area].x, Centr[area].y, 0.0);

	if (ofield[0] > 0) {
	    /* Add new cats for all combinations of input cats (-1 in cycle for null) */
	    for (i = -1; i < Centr[area].cat[0]->n_cats; i++) {
		int j;

		if (i == -1 && Centr[area].cat[0]->n_cats > 0)
		    continue;	/* no need to make null */

		for (j = -1; j < Centr[area].cat[1]->n_cats; j++) {
		    if (j == -1 && Centr[area].cat[1]->n_cats > 0)
			continue;	/* no need to make null */

		    if (ofield[0] > 0)
			Vect_cat_set(Cats, ofield[0], out_cat);

		    /* attributes */
		    if (driver) {
			ATTR *at;

			sprintf(buf, "insert into %s values ( %d", Fi->table,
				out_cat);
			db_set_string(&stmt, buf);

			/* cata */
			if (i >= 0) {
			    if (attr[0].columns) {
				at = find_attr(&(attr[0]),
					       Centr[area].cat[0]->cat[i]);
				if (!at)
				    G_fatal_error(_("Attribute not found"));

				if (at->values)
				    db_append_string(&stmt, at->values);
				else
				    db_append_string(&stmt, attr[0].null_values);
			    }
			    else {
				sprintf(buf, ", %d", Centr[area].cat[0]->cat[i]);
				db_append_string(&stmt, buf);
			    }
			}
			else {
			    if (attr[0].columns) {
				db_append_string(&stmt, attr[0].null_values);
			    }
			    else {
				sprintf(buf, ", null");
				db_append_string(&stmt, buf);
			    }
			}

			/* catb */
			if (j >= 0) {
			    if (attr[1].columns) {
				at = find_attr(&(attr[1]),
					       Centr[area].cat[1]->cat[j]);
				if (!at)
				    G_fatal_error(_("Attribute not found"));

				if (at->values)
				    db_append_string(&stmt, at->values);
				else
				    db_append_string(&stmt, attr[1].null_values);
			    }
			    else {
				sprintf(buf, ", %d", Centr[area].cat[1]->cat[j]);
				db_append_string(&stmt, buf);
			    }
			}
			else {
			    if (attr[1].columns) {
				db_append_string(&stmt, attr[1].null_values);
			    }
			    else {
				sprintf(buf, ", null");
				db_append_string(&stmt, buf);
			    }
			}

			db_append_string(&stmt, " )");

			G_debug(3, "%s", db_get_string(&stmt));

			if (db_execute_immediate(driver, &stmt) != DB_OK)
			    G_warning(_("Unable to insert new record: '%s'"),
				      db_get_string(&stmt));
		    }
		    out_cat++;
		}
	    }
	}

	/* Add all cats from input vectors */
	if (ofield[1] > 0 && field[0] > 0) {
	    for (i = 0; i < Centr[area].cat[0]->n_cats; i++) {
		if (Centr[area].cat[0]->field[i] == field[0])
		    Vect_cat_set(Cats, ofield[1], Centr[area].cat[0]->cat[i]);
	    }
	}

	if (ofield[2] > 0 && field[1] > 0 && ofield[1] != ofield[2]) {
	    for (i = 0; i < Centr[area].cat[1]->n_cats; i++) {
		if (Centr[area].cat[1]->field[i] == field[1])
		    Vect_cat_set(Cats, ofield[2], Centr[area].cat[1]->cat[i]);
	    }
	}

	Vect_write_line(Tmp, GV_CENTROID, Points, Cats);
	Vect_write_line(Out, GV_CENTROID, Points, Cats);
    }

    G_set_verbose(0);
    /* should be fast, be silent */
    Vect_build_partial(Tmp, GV_BUILD_CENTROIDS);
    G_set_verbose(verbose);
    /* Copy valid boundaries to final output */
    nlines = Vect_get_num_lines(Tmp);

    for (line = 1; line <= nlines; line++) {
	int i, ltype, side[2], centr[2];

	G_percent(line, nlines, 1);	/* must be before any continue */

	if (!Vect_line_alive(Tmp, line))
	    continue;

	ltype = Vect_read_line(Tmp, Points, Cats, line);
	if (!(ltype & GV_BOUNDARY))
	    continue;

	Vect_get_line_areas(Tmp, line, &side[0], &side[1]);

	for (i = 0; i < 2; i++) {
	    if (side[i] == 0) {	/* This should not happen ! */
		centr[i] = 0;
		continue;
	    }

	    if (side[i] > 0) {
		area = side[i];
	    }
	    else {		/* island */
		area = Vect_get_isle_area(Tmp, abs(side[i]));
	    }

	    if (area > 0)
		centr[i] = Vect_get_area_centroid(Tmp, area);
	    else
		centr[i] = 0;
	}

	if (centr[0] || centr[1])
	    Vect_write_line(Out, GV_BOUNDARY, Points, Cats);
    }

    return 0;
}
Exemplo n.º 4
0
int main(int argc, char *argv[])
{
    struct GModule *module;
    struct GParams params;
    struct Map_info Map;
    struct Map_info **BgMap;	/* backgroud vector maps */
    int nbgmaps;		/* number of registrated background maps */
    enum mode action_mode;
    FILE *ascii;

    int i;
    int move_first, snap;
    int ret, layer;
    double move_x, move_y, move_z, thresh[3];

    struct line_pnts *coord;

    struct ilist *List;

    struct cat_list *Clist;

    ascii = NULL;
    List = NULL;
    BgMap = NULL;
    nbgmaps = 0;
    coord = NULL;
    Clist = NULL;

    G_gisinit(argv[0]);

    module = G_define_module();
    module->overwrite = TRUE;
    G_add_keyword(_("vector"));
    G_add_keyword(_("editing"));
    G_add_keyword(_("geometry"));
    module->description = _("Edits a vector map, allows adding, deleting "
			    "and modifying selected vector features.");

    if (!parser(argc, argv, &params, &action_mode))
	exit(EXIT_FAILURE);

    /* get list of categories */
    Clist = Vect_new_cat_list();
    if (params.cat->answer && Vect_str_to_cat_list(params.cat->answer, Clist)) {
	G_fatal_error(_("Unable to get category list <%s>"),
		      params.cat->answer);
    }

    /* open input file */
    if (params.in->answer) {
	if (strcmp(params.in->answer, "-") != 0) {
	    ascii = fopen(params.in->answer, "r");
	    if (ascii == NULL)
		G_fatal_error(_("Unable to open file <%s>"),
			      params.in->answer);
	}
	else {
	    ascii = stdin;
	}
    }
    if (!ascii && action_mode == MODE_ADD)
	G_fatal_error(_("Required parameter <%s> not set"), params.in->key);
    
    if (action_mode == MODE_CREATE) {
	int overwrite;

	overwrite = G_check_overwrite(argc, argv);
	if (G_find_vector2(params.map->answer, G_mapset())) {
	    if (!overwrite)
		G_fatal_error(_("Vector map <%s> already exists"),
			      params.map->answer);
	}

	/* 3D vector maps? */
	ret = Vect_open_new(&Map, params.map->answer, WITHOUT_Z);
	if (Vect_maptype(&Map) == GV_FORMAT_OGR_DIRECT) {
	    int type;
	    type = Vect_option_to_types(params.type);
	    if (type != GV_POINT && type != GV_LINE &&
		type != GV_BOUNDARY)
		G_fatal_error(_("Supported feature type for OGR layer: "
				"%s, %s or %s"), "point", "line", "boundary");
	    V2_open_new_ogr(&Map, type);
	}
	if (ret == -1) {
	    G_fatal_error(_("Unable to create vector map <%s>"),
			  params.map->answer);
	}

	G_debug(1, "Map created");

	if (ascii) {
	    /* also add new vector features */
	    action_mode = MODE_ADD;
	}
    }
    else {			/* open selected vector file */
	if (action_mode == MODE_ADD)	/* write */
	    ret = Vect_open_update2(&Map, params.map->answer, G_mapset(), params.fld->answer);
	else			/* read-only -- select features */
	    ret = Vect_open_old2(&Map, params.map->answer, G_mapset(), params.fld->answer);

	if (ret < 2)
	    G_fatal_error(_("Unable to open vector map <%s> at topological level %d"),
			  params.map->answer, 2);
    }

    G_debug(1, "Map opened");

    /* open backgroud maps */
    if (params.bmaps->answer) {
	i = 0;

	while (params.bmaps->answers[i]) {
	    const char *bmap = params.bmaps->answers[i];
	    const char *mapset = G_find_vector2(bmap, "");
	    if (!mapset)
		G_fatal_error(_("Vector map <%s> not found"), bmap);

	    if (strcmp(
		    G_fully_qualified_name(params.map->answer, G_mapset()),
		    G_fully_qualified_name(bmap, mapset)) == 0) {
		G_fatal_error(_("Unable to open vector map <%s> as the background map. "
			       "It is given as vector map to be edited."),
			      bmap);
	    }
	    nbgmaps++;
	    BgMap = (struct Map_info **)G_realloc(
		BgMap, nbgmaps * sizeof(struct Map_info *));
	    BgMap[nbgmaps - 1] =
		(struct Map_info *)G_malloc(sizeof(struct Map_info));
	    if (Vect_open_old(BgMap[nbgmaps - 1], bmap, "") == -1)
		G_fatal_error(_("Unable to open vector map <%s>"), bmap);
	    G_verbose_message(_("Background vector map <%s> registered"), bmap);
	    i++;
	}
    }

    layer = Vect_get_field_number(&Map, params.fld->answer);
    i = 0;
    while (params.maxdist->answers[i]) {
	switch (i) {
	case THRESH_COORDS:
	    thresh[THRESH_COORDS] =
		max_distance(atof(params.maxdist->answers[THRESH_COORDS]));
	    thresh[THRESH_SNAP] = thresh[THRESH_QUERY] =
		thresh[THRESH_COORDS];
	    break;
	case THRESH_SNAP:
	    thresh[THRESH_SNAP] =
		max_distance(atof(params.maxdist->answers[THRESH_SNAP]));
	    break;
	case THRESH_QUERY:
	    thresh[THRESH_QUERY] =
		atof(params.maxdist->answers[THRESH_QUERY]);
	    break;
	default:
	    break;
	}
	i++;
    }

    move_first = params.move_first->answer ? 1 : 0;
    snap = NO_SNAP;
    if (strcmp(params.snap->answer, "node") == 0)
	snap = SNAP;
    else if (strcmp(params.snap->answer, "vertex") == 0)
	snap = SNAPVERTEX;
    if (snap != NO_SNAP && thresh[THRESH_SNAP] <= 0) {
	G_warning(_("Threshold for snapping must be > 0. No snapping applied."));
	snap = NO_SNAP;
    }
    
    if (action_mode != MODE_CREATE && action_mode != MODE_ADD) {
	/* select lines */
	List = Vect_new_list();
	G_message(_("Selecting features..."));
	if (action_mode == MODE_COPY && BgMap && BgMap[0]) {
	    List = select_lines(BgMap[0], action_mode, &params, thresh, List);
	}
	else {
	    List = select_lines(&Map, action_mode, &params, thresh, List);
	}
    }

    if ((action_mode != MODE_CREATE && action_mode != MODE_ADD &&
	 action_mode != MODE_SELECT)) {
	if (List->n_values < 1) {
	    G_warning(_("No features selected, nothing to edit"));
	    action_mode = MODE_NONE;
	    ret = 0;
	}
	else {
	    /* reopen the map for updating */
	    if (action_mode == MODE_ZBULK && !Vect_is_3d(&Map)) {
		Vect_close(&Map);
		G_fatal_error(_("Vector map <%s> is not 3D. Tool '%s' requires 3D vector map. "
			       "Please convert the vector map "
			       "to 3D using e.g. %s."), params.map->answer,
			      params.tool->answer, "v.extrude");
	    }
	    Vect_close(&Map);

	    Vect_open_update2(&Map, params.map->answer, G_mapset(), params.fld->answer);
	}
    }

    /* coords option -> array */
    if (params.coord->answers) {
	coord = Vect_new_line_struct();
	int i = 0;
	double east, north;

	while (params.coord->answers[i]) {
	    east = atof(params.coord->answers[i]);
	    north = atof(params.coord->answers[i + 1]);
	    Vect_append_point(coord, east, north, 0.0);
	    i += 2;
	}
    }

    /* perform requested editation */
    switch (action_mode) {
    case MODE_CREATE:
	break;
    case MODE_ADD:
	if (!params.header->answer)
	    Vect_read_ascii_head(ascii, &Map);
	int num_lines;
	num_lines = Vect_get_num_lines(&Map);
	
	ret = Vect_read_ascii(ascii, &Map);
	G_message(_("%d features added"), ret);
	if (ret > 0) {
	    int iline;
	    struct ilist *List_added;
	    
	    List_added = Vect_new_list();
	    for (iline = num_lines + 1; iline <= Vect_get_num_lines(&Map); iline++)
		Vect_list_append(List_added, iline);
	    
	    G_verbose_message(_("Threshold value for snapping is %.2f"),
			      thresh[THRESH_SNAP]);
	    if (snap != NO_SNAP) { /* apply snapping */
		/* snap to vertex ? */
		Vedit_snap_lines(&Map, BgMap, nbgmaps, List_added,
				 thresh[THRESH_SNAP],
				 snap == SNAP ? FALSE : TRUE); 
	    }
	    if (params.close->answer) {	/* close boundaries */
		int nclosed;

		nclosed = close_lines(&Map, GV_BOUNDARY, thresh[THRESH_SNAP]);
		G_message(_("%d boundaries closed"), nclosed);
	    }
	    Vect_destroy_list(List_added);
	}
	break;
    case MODE_DEL:
	ret = Vedit_delete_lines(&Map, List);
	G_message(_("%d features deleted"), ret);
	break;
    case MODE_MOVE:
	move_x = atof(params.move->answers[0]);
	move_y = atof(params.move->answers[1]);
	move_z = atof(params.move->answers[2]);
	G_verbose_message(_("Threshold value for snapping is %.2f"),
			  thresh[THRESH_SNAP]);
	ret = Vedit_move_lines(&Map, BgMap, nbgmaps, List, move_x, move_y, move_z, snap, thresh[THRESH_SNAP]);
	G_message(_("%d features moved"), ret);
	break;
    case MODE_VERTEX_MOVE:
	move_x = atof(params.move->answers[0]);
	move_y = atof(params.move->answers[1]);
	move_z = atof(params.move->answers[2]);
	G_verbose_message(_("Threshold value for snapping is %.2f"),
			  thresh[THRESH_SNAP]);
	ret = Vedit_move_vertex(&Map, BgMap, nbgmaps, List, coord, thresh[THRESH_COORDS], thresh[THRESH_SNAP], move_x, move_y, move_z, move_first, snap);
	G_message(_("%d vertices moved"), ret);
	break;
    case MODE_VERTEX_ADD:
	ret = Vedit_add_vertex(&Map, List, coord, thresh[THRESH_COORDS]);
	G_message(_("%d vertices added"), ret);
	break;
    case MODE_VERTEX_DELETE:
	ret = Vedit_remove_vertex(&Map, List, coord, thresh[THRESH_COORDS]);
	G_message(_("%d vertices removed"), ret);
	break;
    case MODE_BREAK:
	if (params.coord->answer) {
	    ret = Vedit_split_lines(&Map, List,
				    coord, thresh[THRESH_COORDS], NULL);
	}
	else {
	    ret = Vect_break_lines_list(&Map, List, NULL, GV_LINES, NULL);
	}
	G_message(_("%d lines broken"), ret);
	break;
    case MODE_CONNECT:
	G_verbose_message(_("Threshold value for snapping is %.2f"),
			  thresh[THRESH_SNAP]);
	ret = Vedit_connect_lines(&Map, List, thresh[THRESH_SNAP]);
	G_message(_("%d lines connected"), ret);
	break;
    case MODE_MERGE:
	ret = Vedit_merge_lines(&Map, List);
	G_message(_("%d lines merged"), ret);
	break;
    case MODE_SELECT:
	ret = print_selected(List);
	break;
    case MODE_CATADD:
	ret = Vedit_modify_cats(&Map, List, layer, 0, Clist);
	G_message(_("%d features modified"), ret);
	break;
    case MODE_CATDEL:
	ret = Vedit_modify_cats(&Map, List, layer, 1, Clist);
	G_message(_("%d features modified"), ret);
	break;
    case MODE_COPY:
	if (BgMap && BgMap[0]) {
	    if (nbgmaps > 1)
		G_warning(_("Multiple background maps were given. "
			    "Selected features will be copied only from "
			    "vector map <%s>."),
			  Vect_get_full_name(BgMap[0]));

	    ret = Vedit_copy_lines(&Map, BgMap[0], List);
	}
	else {
	    ret = Vedit_copy_lines(&Map, NULL, List);
	}
	G_message(_("%d features copied"), ret);
	break;
    case MODE_SNAP:
	G_verbose_message(_("Threshold value for snapping is %.2f"),
			  thresh[THRESH_SNAP]);
	ret = snap_lines(&Map, List, thresh[THRESH_SNAP]);
	break;
    case MODE_FLIP:
	ret = Vedit_flip_lines(&Map, List);
	G_message(_("%d lines flipped"), ret);
	break;
    case MODE_NONE:
	break;
    case MODE_ZBULK:{
	    double start, step;
	    double x1, y1, x2, y2;

	    start = atof(params.zbulk->answers[0]);
	    step = atof(params.zbulk->answers[1]);

	    x1 = atof(params.bbox->answers[0]);
	    y1 = atof(params.bbox->answers[1]);
	    x2 = atof(params.bbox->answers[2]);
	    y2 = atof(params.bbox->answers[3]);

	    ret = Vedit_bulk_labeling(&Map, List,
				      x1, y1, x2, y2, start, step);

	    G_message(_("%d lines labeled"), ret);
	    break;
	}
    case MODE_CHTYPE:{
	    ret = Vedit_chtype_lines(&Map, List);

	    if (ret > 0) {
		G_message(_("%d features converted"), ret);
	    }
	    else {
		G_message(_("No feature modified"));
	    }
	    break;
	}
    default:
	G_warning(_("Operation not implemented"));
	ret = -1;
	break;
    }
    
    Vect_hist_command(&Map);

    /* build topology only if requested or if tool!=select */
    if (!(action_mode == MODE_SELECT || params.topo->answer == 1 ||
	 !MODE_NONE)) {
	Vect_build_partial(&Map, GV_BUILD_NONE);
	Vect_build(&Map);
    }

    if (List)
	Vect_destroy_list(List);

    Vect_close(&Map);

    G_debug(1, "Map closed");

    /* close background maps */
    for (i = 0; i < nbgmaps; i++) {
	Vect_close(BgMap[i]);
	G_free((void *)BgMap[i]);
    }
    G_free((void *)BgMap);

    if (coord)
	Vect_destroy_line_struct(coord);

    if (Clist)
	Vect_destroy_cat_list(Clist);

    G_done_msg(" ");

    if (ret > -1) {
	exit(EXIT_SUCCESS);
    }
    else {
	exit(EXIT_FAILURE);
    }
}