From 623d59108de0a3e769029fd47efa9f5e63bb8d8a Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sat, 24 Feb 2024 12:58:23 -0700 Subject: [PATCH 01/45] v.edit: Implement the batch option for batching editing --- vector/v.edit/args.c | 225 +++---- vector/v.edit/batch.c | 435 ++++++++++++++ vector/v.edit/close.c | 1 - vector/v.edit/global.h | 8 +- vector/v.edit/main.c | 565 ++++++++++-------- vector/v.edit/proto.h | 9 +- vector/v.edit/select.c | 158 +++-- vector/v.edit/v.edit.html | 108 +++- .../v.edit/v_edit_roadsmajor_upper_half.png | Bin 0 -> 43788 bytes 9 files changed, 1090 insertions(+), 419 deletions(-) create mode 100644 vector/v.edit/batch.c create mode 100644 vector/v.edit/v_edit_roadsmajor_upper_half.png diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c index 4ed5b18c338..4de565b34b7 100644 --- a/vector/v.edit/args.c +++ b/vector/v.edit/args.c @@ -31,7 +31,6 @@ int parser(int argc, char *argv[], struct GParams *params, params->tool = G_define_option(); params->tool->key = "tool"; params->tool->type = TYPE_STRING; - params->tool->required = YES; params->tool->multiple = NO; params->tool->description = _("Tool"); desc_tool = NULL; @@ -225,6 +224,15 @@ int parser(int argc, char *argv[], struct GParams *params, params->extend_parallel->description = _("Connect parallel lines (using extend tools and threshold distance)"); + params->batch = G_define_standard_option(G_OPT_F_INPUT); + params->batch->key = "batch"; + params->batch->required = NO; + params->batch->label = _("Name of command file for batching editing"); + + G_option_required(params->tool, params->batch, NULL); + G_option_exclusive(params->tool, params->batch, NULL); + G_option_exclusive(params->in, params->batch, NULL); + if (G_parser(argc, argv)) exit(EXIT_FAILURE); @@ -242,119 +250,128 @@ int parser(int argc, char *argv[], struct GParams *params, /* check that the given arguments makes sense together */ - if (strcmp(params->tool->answer, "create") == 0) { - *action_mode = MODE_CREATE; - } - else if (strcmp(params->tool->answer, "add") == 0) { - *action_mode = MODE_ADD; - } - else if (strcmp(params->tool->answer, "delete") == 0) { - *action_mode = MODE_DEL; - } - else if (strcmp(params->tool->answer, "move") == 0) { - *action_mode = MODE_MOVE; - } - else if (strcmp(params->tool->answer, "merge") == 0) { - *action_mode = MODE_MERGE; - } - else if (strcmp(params->tool->answer, "break") == 0) { - *action_mode = MODE_BREAK; - } - else if (strcmp(params->tool->answer, "connect") == 0) { - *action_mode = MODE_CONNECT; - } - else if (strcmp(params->tool->answer, "extend") == 0) { - *action_mode = MODE_EXTEND; - } - else if (strcmp(params->tool->answer, "extendstart") == 0) { - *action_mode = MODE_EXTEND_START; - } - else if (strcmp(params->tool->answer, "extendend") == 0) { - *action_mode = MODE_EXTEND_END; - } - else if (strcmp(params->tool->answer, "vertexadd") == 0) { - *action_mode = MODE_VERTEX_ADD; - } - else if (strcmp(params->tool->answer, "vertexdel") == 0) { - *action_mode = MODE_VERTEX_DELETE; - } - else if (strcmp(params->tool->answer, "vertexmove") == 0) { - *action_mode = MODE_VERTEX_MOVE; - } - else if (strcmp(params->tool->answer, "select") == 0) { - *action_mode = MODE_SELECT; - } - else if (strcmp(params->tool->answer, "catadd") == 0) { - *action_mode = MODE_CATADD; - } - else if (strcmp(params->tool->answer, "catdel") == 0) { - *action_mode = MODE_CATDEL; - } - else if (strcmp(params->tool->answer, "copy") == 0) { - *action_mode = MODE_COPY; - } - else if (strcmp(params->tool->answer, "snap") == 0) { - *action_mode = MODE_SNAP; - } - else if (strcmp(params->tool->answer, "flip") == 0) { - *action_mode = MODE_FLIP; - } - else if (strcmp(params->tool->answer, "zbulk") == 0) { - *action_mode = MODE_ZBULK; - } - else if (strcmp(params->tool->answer, "chtype") == 0) { - *action_mode = MODE_CHTYPE; - } - else if (strcmp(params->tool->answer, "areadel") == 0) { - *action_mode = MODE_AREA_DEL; - } - else { - G_fatal_error(_("Operation '%s' not implemented"), - params->tool->answer); - } - - if ((*action_mode != MODE_CREATE && *action_mode != MODE_ADD && - *action_mode != MODE_ZBULK) && - (params->cat->answers == NULL) && (params->coord->answers == NULL) && - (params->poly->answers == NULL) && (params->id->answers == NULL) && - (params->bbox->answers == NULL) && (params->where->answer == NULL) && - (params->query->answer == NULL)) { - G_fatal_error(_("At least one option from %s must be specified"), - "cats, ids, coords, bbox, polygon, where, query"); - } + if (params->tool->answer) { + if (strcmp(params->tool->answer, "create") == 0) { + *action_mode = MODE_CREATE; + } + else if (strcmp(params->tool->answer, "add") == 0) { + *action_mode = MODE_ADD; + } + else if (strcmp(params->tool->answer, "delete") == 0) { + *action_mode = MODE_DEL; + } + else if (strcmp(params->tool->answer, "move") == 0) { + *action_mode = MODE_MOVE; + } + else if (strcmp(params->tool->answer, "merge") == 0) { + *action_mode = MODE_MERGE; + } + else if (strcmp(params->tool->answer, "break") == 0) { + *action_mode = MODE_BREAK; + } + else if (strcmp(params->tool->answer, "connect") == 0) { + *action_mode = MODE_CONNECT; + } + else if (strcmp(params->tool->answer, "extend") == 0) { + *action_mode = MODE_EXTEND; + } + else if (strcmp(params->tool->answer, "extendstart") == 0) { + *action_mode = MODE_EXTEND_START; + } + else if (strcmp(params->tool->answer, "extendend") == 0) { + *action_mode = MODE_EXTEND_END; + } + else if (strcmp(params->tool->answer, "vertexadd") == 0) { + *action_mode = MODE_VERTEX_ADD; + } + else if (strcmp(params->tool->answer, "vertexdel") == 0) { + *action_mode = MODE_VERTEX_DELETE; + } + else if (strcmp(params->tool->answer, "vertexmove") == 0) { + *action_mode = MODE_VERTEX_MOVE; + } + else if (strcmp(params->tool->answer, "select") == 0) { + *action_mode = MODE_SELECT; + } + else if (strcmp(params->tool->answer, "catadd") == 0) { + *action_mode = MODE_CATADD; + } + else if (strcmp(params->tool->answer, "catdel") == 0) { + *action_mode = MODE_CATDEL; + } + else if (strcmp(params->tool->answer, "copy") == 0) { + *action_mode = MODE_COPY; + } + else if (strcmp(params->tool->answer, "snap") == 0) { + *action_mode = MODE_SNAP; + } + else if (strcmp(params->tool->answer, "flip") == 0) { + *action_mode = MODE_FLIP; + } + else if (strcmp(params->tool->answer, "zbulk") == 0) { + *action_mode = MODE_ZBULK; + } + else if (strcmp(params->tool->answer, "chtype") == 0) { + *action_mode = MODE_CHTYPE; + } + else if (strcmp(params->tool->answer, "areadel") == 0) { + *action_mode = MODE_AREA_DEL; + } + else { + G_fatal_error(_("Operation '%s' not implemented"), + params->tool->answer); + } - if (*action_mode == MODE_MOVE || *action_mode == MODE_VERTEX_MOVE) { - if (params->move->answers == NULL) { - G_fatal_error(_("Tool %s requires option %s"), params->tool->answer, - params->move->key); + if ((*action_mode != MODE_CREATE && *action_mode != MODE_ADD && + *action_mode != MODE_ZBULK) && + (params->cat->answers == NULL) && + (params->coord->answers == NULL) && + (params->poly->answers == NULL) && (params->id->answers == NULL) && + (params->bbox->answers == NULL) && + (params->where->answer == NULL) && + (params->query->answer == NULL) && + (params->batch->answer == NULL)) { + G_fatal_error( + _("At least one option from %s must be specified"), + "cats, ids, coords, bbox, polygon, where, query, batch"); } - } - if (*action_mode == MODE_VERTEX_ADD || *action_mode == MODE_VERTEX_DELETE || - *action_mode == MODE_VERTEX_MOVE) { - if (params->coord->answers == NULL) { - G_fatal_error(_("Tool %s requires option %s"), params->tool->answer, - params->coord->key); + if (*action_mode == MODE_MOVE || *action_mode == MODE_VERTEX_MOVE) { + if (params->move->answers == NULL) { + G_fatal_error(_("Tool %s requires option %s"), + params->tool->answer, params->move->key); + } } - } - if (*action_mode == MODE_CATADD || *action_mode == MODE_CATDEL) { - if (params->cat->answers == NULL) { - G_fatal_error(_("Tool %s requires option %s"), params->tool->answer, - params->cat->key); + if (*action_mode == MODE_VERTEX_ADD || + *action_mode == MODE_VERTEX_DELETE || + *action_mode == MODE_VERTEX_MOVE) { + if (params->coord->answers == NULL) { + G_fatal_error(_("Tool %s requires option %s"), + params->tool->answer, params->coord->key); + } } - } - if (*action_mode == MODE_ZBULK) { - if (params->bbox->answers == NULL) { - G_fatal_error(_("Tool %s requires option %s"), params->tool->answer, - params->bbox->key); + if (*action_mode == MODE_CATADD || *action_mode == MODE_CATDEL) { + if (params->cat->answers == NULL) { + G_fatal_error(_("Tool %s requires option %s"), + params->tool->answer, params->cat->key); + } } - if (params->zbulk->answers == NULL) { - G_fatal_error(_("Tool %s requires option %s"), params->tool->answer, - params->zbulk->key); + + if (*action_mode == MODE_ZBULK) { + if (params->bbox->answers == NULL) { + G_fatal_error(_("Tool %s requires option %s"), + params->tool->answer, params->bbox->key); + } + if (params->zbulk->answers == NULL) { + G_fatal_error(_("Tool %s requires option %s"), + params->tool->answer, params->zbulk->key); + } } } + else + *action_mode = MODE_BATCH; return 1; } diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c new file mode 100644 index 00000000000..e909f166554 --- /dev/null +++ b/vector/v.edit/batch.c @@ -0,0 +1,435 @@ +#include "global.h" + +#define MAX_COLUMNS 12 +#define NUM_TOOLS 20 + +#define COLUMN_TOOL 0 +#define COLUMN_FLAGS 1 +#define COLUMN_MOVE 2 +#define COLUMN_IDS 3 +#define COLUMN_CATS 4 +#define COLUMN_COORDS 5 +#define COLUMN_BBOX 6 +#define COLUMN_POLYGON 7 +#define COLUMN_WHERE 8 +#define COLUMN_QUERY 9 +#define COLUMN_SNAP 10 +#define COLUMN_ZBULK 11 + +static int get_flag(char *, char); +static int get_snap(char *, double *thresh); + +int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, + const char *file, struct SelectParams *selparams, double *thresh) +{ + char *col_names[MAX_COLUMNS] = {"tool", "flags", "move", "ids", + "cats", "coords", "bbox", "polygon", + "where", "query", "snap", "zbulk"}; + int col_nums[MAX_COLUMNS] = {COLUMN_TOOL, COLUMN_FLAGS, COLUMN_MOVE, + COLUMN_IDS, COLUMN_CATS, COLUMN_COORDS, + COLUMN_BBOX, COLUMN_POLYGON, COLUMN_WHERE, + COLUMN_QUERY, COLUMN_SNAP, COLUMN_ZBULK}; + char *tool_names[NUM_TOOLS] = { + "delete", "copy", "move", "flip", "catadd", + "catdel", "merge", "break", "snap", "connect", + "extend", "extendstart", "extendend", "chtype", "vertexadd", + "vertexdel", "vertexmove", "areadel", "zbulk", "select"}; + enum mode tool_modes[NUM_TOOLS] = { + MODE_DEL, MODE_COPY, MODE_MOVE, MODE_FLIP, + MODE_CATADD, MODE_CATDEL, MODE_MERGE, MODE_BREAK, + MODE_SNAP, MODE_CONNECT, MODE_EXTEND, MODE_EXTEND_START, + MODE_EXTEND_END, MODE_CHTYPE, MODE_VERTEX_ADD, MODE_VERTEX_DELETE, + MODE_VERTEX_MOVE, MODE_AREA_DEL, MODE_ZBULK, MODE_SELECT}; + FILE *fp; + char buf[1024]; + int first = 1; + int cols[MAX_COLUMNS], ncols = 0; + int line = 0; + int total_ret = 0; + + if (strcmp(file, "-") != 0) { + if (!(fp = fopen(file, "r"))) + G_fatal_error(_("Unable to open file <%s>"), file); + } + else + fp = stdin; + + while (fgets(buf, 1024, fp)) { + char *p = strchr(buf, '\n'), *psep; + int last = 0; + enum mode action_mode; + char *flags, *move, *snap, *zbulk; + struct ilist *List; + struct line_pnts *coord = NULL; + struct cat_list *Clist = NULL; + int ret = 0; + int i; + + line++; + + selparams->ids = selparams->cats = selparams->coords = selparams->bbox = + selparams->polygon = selparams->where = selparams->query = NULL; + flags = move = snap = zbulk = NULL; + + /* remove newline */ + if (p) { + *p = 0; + if ((p = strchr(buf, '\r'))) + *p = 0; + } + else if ((p = strchr(buf, '\r'))) + *p = 0; + + /* remove comments starting with # */ + if ((p = strchr(buf, '#'))) { + *p-- = 0; + while (p >= buf && (*p == ' ' || *p == '\t')) + *p-- = 0; + } + + /* skip empty line */ + if (!*buf) + continue; + + p = buf; + + /* read batch columns */ + if (first) { + int bit_cols = 0, bit_col; + + while (!last && + ((psep = strchr(p, '|')) || (psep = strchr(p, 0)))) { + int known_col = 0; + last = !*psep; + *psep = 0; + for (i = 0; i < MAX_COLUMNS; i++) { + if (strcmp(p, col_names[i]) == 0) { + bit_col = 1 << col_nums[i]; + if (bit_cols & bit_col) + G_fatal_error( + _("Duplicate batch column '%s' not allowed"), + col_names[i]); + bit_cols |= bit_col; + cols[ncols++] = col_nums[i]; + known_col = 1; + } + } + if (!known_col) + G_fatal_error(_("Unknown batch column '%s'"), p); + + p = psep + 1; + } + + if (!(bit_cols & 1 << COLUMN_TOOL)) + G_fatal_error(_("Required batch column '%s' missing"), + col_names[COLUMN_TOOL]); + first = 0; + continue; + } + + G_message(_("Batch line %d..."), line); + + i = 0; + while (!last && ((psep = strchr(p, '|')) || (psep = strchr(p, 0)))) { + int j; + last = !*psep; + *psep = 0; + + if (i >= ncols) + G_fatal_error(_("Too many batch columns in line %d"), line); + + switch (cols[i]) { + case COLUMN_TOOL: + for (j = 0; j < NUM_TOOLS; j++) + if (strcmp(p, tool_names[j]) == 0) { + action_mode = tool_modes[j]; + break; + } + if (j == NUM_TOOLS) + G_fatal_error(_("Unsupported tool '%s'"), p); + break; + case COLUMN_FLAGS: + flags = p; + break; + case COLUMN_MOVE: + move = p; + break; + case COLUMN_IDS: + selparams->ids = p; + break; + case COLUMN_CATS: + selparams->cats = p; + break; + case COLUMN_COORDS: + selparams->coords = p; + break; + case COLUMN_BBOX: + selparams->bbox = p; + break; + case COLUMN_POLYGON: + selparams->polygon = p; + break; + case COLUMN_WHERE: + selparams->where = p; + break; + case COLUMN_QUERY: + selparams->query = p; + break; + case COLUMN_SNAP: + snap = p; + break; + case COLUMN_ZBULK: + zbulk = p; + break; + } + + i++; + p = psep + 1; + } + + if (i < ncols) + G_fatal_error(_("Too few batch columns in line %d"), line); + + List = Vect_new_list(); + + selparams->reverse = get_flag(flags, 'r'); + if (action_mode == MODE_COPY && BgMap && BgMap[0]) + List = select_lines(BgMap[0], action_mode, selparams, thresh, List); + else + List = select_lines(Map, action_mode, selparams, thresh, List); + + if (selparams->cats && *selparams->cats) { + Clist = Vect_new_cat_list(); + if (Vect_str_to_cat_list(selparams->cats, Clist)) + G_fatal_error(_("Unable to get category list <%s>"), + selparams->cats); + } + + if (selparams->coords && *selparams->coords) { + coord = Vect_new_line_struct(); + str_to_coordinates(selparams->coords, coord); + } + + switch (action_mode) { + case MODE_DEL: + ret = Vedit_delete_lines(Map, List); + G_message(n_("%d feature deleted", "%d features deleted", ret), + 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(n_("%d feature copied", "%d features copied", ret), ret); + break; + case MODE_MOVE: { + double move_x, move_y, move_z; + if (sscanf(p, "%lf,%lf,%lf", &move_x, &move_y, &move_z) != 3) + G_fatal_error(_("'%s' tool must have '%s' column"), "move", + "move"); + 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, get_snap(snap, thresh), + thresh[THRESH_SNAP]); + G_message(n_("%d feature moved", "%d features moved", ret), ret); + break; + } + case MODE_FLIP: + ret = Vedit_flip_lines(Map, List); + G_message(n_("%d line flipped", "%d lines flipped", ret), ret); + break; + case MODE_CATADD: + ret = Vedit_modify_cats(Map, List, selparams->layer, 0, Clist); + G_message(n_("%d feature modified", "%d features modified", ret), + ret); + break; + case MODE_CATDEL: + ret = Vedit_modify_cats(Map, List, selparams->layer, 1, Clist); + G_message(n_("%d feature modified", "%d features modified", ret), + ret); + break; + case MODE_MERGE: + ret = Vedit_merge_lines(Map, List); + G_message(n_("%d line merged", "%d lines merged", ret), ret); + break; + case MODE_BREAK: + if (coord) + 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(n_("%d line broken", "%d lines broken", ret), 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_CONNECT: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_connect_lines(Map, List, thresh[THRESH_SNAP]); + G_message(n_("%d line connected", "%d lines connected", ret), ret); + break; + case MODE_EXTEND: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(Map, List, 0, get_flag(flags, 'p'), + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_EXTEND_START: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(Map, List, 1, get_flag(flags, 'p'), + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_EXTEND_END: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(Map, List, 2, get_flag(flags, 'p'), + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_CHTYPE: + if ((ret = Vedit_chtype_lines(Map, List)) > 0) + G_message( + n_("%d feature converted", "%d features converted", ret), + ret); + else + G_message(_("No feature modified")); + break; + case MODE_VERTEX_ADD: + ret = Vedit_add_vertex(Map, List, coord, thresh[THRESH_COORDS]); + G_message(n_("%d vertex added", "%d vertices added", ret), ret); + + break; + case MODE_VERTEX_DELETE: + ret = Vedit_remove_vertex(Map, List, coord, thresh[THRESH_COORDS]); + G_message(n_("%d vertex removed", "%d vertices removed", ret), ret); + break; + case MODE_VERTEX_MOVE: { + double move_x, move_y, move_z; + if (sscanf(p, "%lf,%lf,%lf", &move_x, &move_y, &move_z) != 3) + G_fatal_error(_("'%s' tool must have '%s' column"), + "vertexmove", "move"); + 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, + get_flag(flags, '1'), get_snap(snap, thresh)); + G_message(n_("%d vertex moved", "%d vertices moved", ret), ret); + break; + } + case MODE_AREA_DEL: + for (i = 0; i < List->n_values; i++) { + if (Vect_get_line_type(Map, List->value[i]) != GV_CENTROID) { + G_warning( + _("Select feature %d is not centroid, ignoring..."), + List->value[i]); + continue; + } + + ret += Vedit_delete_area_centroid(Map, List->value[i]); + } + G_message(n_("%d area removed", "%d areas removed", ret), ret); + + break; + case MODE_ZBULK: { + double start, step; + double x1, y1, x2, y2; + + if (!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."), + Map->name, "zbulk", "v.extrude"); + } + + if (sscanf(zbulk, "%lf,%lf", &start, &step) != 2) + G_fatal_error(_("'%s' tool must have '%s' column"), "zbulk", + "zbulk"); + + if (!selparams->bbox || sscanf(selparams->bbox, "%lf,%lf,%lf,%lf", + &x1, &y1, &x2, &y2) != 4) + G_fatal_error(_("ZBulk must have bbox")); + + ret = Vedit_bulk_labeling(Map, List, x1, y1, x2, y2, start, step); + + G_message(n_("%d line labeled", "%d lines labeled", ret), ret); + break; + } + case MODE_SELECT: + ret = print_selected(List); + break; + case MODE_BATCH: + /* this mode */ + case MODE_CREATE: + case MODE_ADD: + case MODE_NONE: + /* unsupported tools */ + break; + } + + if (ret && action_mode != MODE_SELECT) { + Vect_build_partial(Map, GV_BUILD_NONE); + Vect_build(Map); + total_ret += ret; + } + + G_message(" "); + + Vect_destroy_list(List); + + if (coord) + Vect_destroy_line_struct(coord); + + if (Clist) + Vect_destroy_cat_list(Clist); + } + + if (fp != stdin) + fclose(fp); + + G_message(n_("%d feature edited", "%d features edited", total_ret), + total_ret); + + return total_ret; +} + +static int get_flag(char *flags, char flag) +{ + return flags && strchr(flags, flag); +} + +static int get_snap(char *snap, double *thresh) +{ + int snap_mode = NO_SNAP; + if (snap) { + if (strcmp(snap, "node") == 0) + snap_mode = SNAP; + else if (strcmp(snap, "vertex") == 0) + snap_mode = SNAPVERTEX; + else if (strcmp(snap, "no") != 0) + G_fatal_error(_("Unsupported snap '%s'"), snap); + if (snap_mode != NO_SNAP && thresh[THRESH_SNAP] <= 0) { + G_warning(_("Threshold for snapping must be > 0. No " + "snapping applied.")); + snap_mode = NO_SNAP; + } + } + return snap_mode; +} diff --git a/vector/v.edit/close.c b/vector/v.edit/close.c index 25b682ecbdc..8d432767877 100644 --- a/vector/v.edit/close.c +++ b/vector/v.edit/close.c @@ -1,4 +1,3 @@ -#include #include "global.h" /*! \brief Close lines (boundaries) diff --git a/vector/v.edit/global.h b/vector/v.edit/global.h index 745bd03fd1a..93f4b47ce50 100644 --- a/vector/v.edit/global.h +++ b/vector/v.edit/global.h @@ -46,14 +46,20 @@ enum mode { /* change feature type (point<->centroid, line<->boundary) */ MODE_CHTYPE, MODE_AREA_DEL, /* delete area */ + MODE_BATCH, /* batch editing */ }; struct GParams { struct Option *map, *in, *maxdist, *tool, *coord, *cat, *move, *bbox, *fld, - *poly, *type, *id, *where, *bmaps, *snap, *query, *zbulk; + *poly, *type, *id, *where, *bmaps, *snap, *query, *zbulk, *batch; struct Flag *header, *topo, *close, *reverse, *move_first, *extend_parallel; }; +struct SelectParams { + int layer, bglayer, type, reverse; + char *ids, *cats, *coords, *bbox, *polygon, *where, *query; +}; + #include "proto.h" #endif diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index c49b99138cc..cc0aa07dc32 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -7,9 +7,9 @@ * AUTHOR(S): Wolf Bergenheim * Jachym Cepicky * Major updates by Martin Landa - * Extend tools by Huidae Cho + * Extend tools and batch editing by Huidae Cho * - * COPYRIGHT: (C) 2006-2017 by the GRASS Development Team + * COPYRIGHT: (C) 2006-2024 by the GRASS Development Team * * This program is free software under the GNU General * Public License (>=v2). Read the file COPYING that comes @@ -29,6 +29,7 @@ int main(int argc, char *argv[]) int nbgmaps; /* number of registrated background maps */ enum mode action_mode; FILE *ascii; + struct SelectParams selparams; int i; int move_first, snap, extend_parallel; @@ -67,13 +68,6 @@ int main(int argc, char *argv[]) if (!parser(argc, argv, ¶ms, &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) { @@ -201,281 +195,334 @@ int main(int argc, char *argv[]) i++; } - move_first = params.move_first->answer ? 1 : 0; - extend_parallel = params.extend_parallel->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 (params.batch->answer) { + if (Vect_open_update2(&Map, params.map->answer, G_mapset(), + params.fld->answer) < 0) + G_fatal_error(_("Unable to open vector map <%s>"), + params.map->answer); + selparams.layer = Vect_get_field_number(&Map, params.fld->answer); + if (BgMap && BgMap[0]) + selparams.bglayer = + Vect_get_field_number(BgMap[0], params.fld->answer); + selparams.type = Vect_option_to_types(params.type); + + batch_edit(&Map, BgMap, nbgmaps, params.batch->answer, &selparams, + thresh); } - - 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, ¶ms, thresh, List); + else { + /* get list of categories */ + if (action_mode == MODE_CATADD || action_mode == MODE_CATDEL) { + 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); } - else { - List = select_lines(&Map, action_mode, ¶ms, thresh, List); + + move_first = params.move_first->answer ? 1 : 0; + extend_parallel = params.extend_parallel->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 && - action_mode != MODE_SELECT)) { - if (List->n_values < 1) { - G_warning(_("No features selected, nothing to edit")); - action_mode = MODE_NONE; - ret = 0; + if (action_mode != MODE_CREATE && action_mode != MODE_ADD) { + /* select lines */ + if (action_mode == MODE_COPY && BgMap && BgMap[0]) + selparams.layer = + Vect_get_field_number(BgMap[0], params.fld->answer); + else + selparams.layer = + Vect_get_field_number(&Map, params.fld->answer); + selparams.type = Vect_option_to_types(params.type); + selparams.reverse = params.reverse->answer; + + selparams.ids = params.id->answer; + selparams.cats = params.cat->answer; + selparams.coords = params.coord->answer; + selparams.bbox = params.bbox->answer; + selparams.polygon = params.poly->answer; + selparams.where = params.where->answer; + selparams.query = params.query->answer; + + List = Vect_new_list(); + if (action_mode == MODE_COPY && BgMap && BgMap[0]) { + List = select_lines(BgMap[0], action_mode, &selparams, thresh, + List); + } + else { + List = + select_lines(&Map, action_mode, &selparams, thresh, List); + } } - 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"); + + 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; } - Vect_close(&Map); + 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); - if (Vect_open_update2(&Map, params.map->answer, G_mapset(), - params.fld->answer) < 0) - G_fatal_error(_("Unable to open vector map <%s>"), - params.map->answer); + if (Vect_open_update2(&Map, params.map->answer, G_mapset(), + params.fld->answer) < 0) + G_fatal_error(_("Unable to open vector map <%s>"), + params.map->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; + /* 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); + /* 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); + if (ret > 0) { + int iline; + struct ilist *List_added; + + G_message(n_("%d feature added", "%d features added", ret), + ret); + + 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(n_("%d boundary closed", "%d boundaries closed", + nclosed), + nclosed); + } + Vect_destroy_list(List_added); + } + break; + case MODE_DEL: + ret = Vedit_delete_lines(&Map, List); + G_message(n_("%d feature deleted", "%d features deleted", ret), + 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(n_("%d feature moved", "%d features moved", ret), 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(n_("%d vertex moved", "%d vertices moved", ret), ret); + break; + case MODE_VERTEX_ADD: + ret = Vedit_add_vertex(&Map, List, coord, thresh[THRESH_COORDS]); + G_message(n_("%d vertex added", "%d vertices added", ret), ret); + break; + case MODE_VERTEX_DELETE: + ret = Vedit_remove_vertex(&Map, List, coord, thresh[THRESH_COORDS]); + G_message(n_("%d vertex removed", "%d vertices removed", ret), 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(n_("%d line broken", "%d lines broken", ret), 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(n_("%d line connected", "%d lines connected", ret), ret); + break; + case MODE_EXTEND: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(&Map, List, 0, extend_parallel, + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_EXTEND_START: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(&Map, List, 1, extend_parallel, + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_EXTEND_END: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(&Map, List, 2, extend_parallel, + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_MERGE: + ret = Vedit_merge_lines(&Map, List); + G_message(n_("%d line merged", "%d lines merged", ret), ret); + break; + case MODE_SELECT: + ret = print_selected(List); + break; + case MODE_CATADD: + ret = Vedit_modify_cats(&Map, List, layer, 0, Clist); + G_message(n_("%d feature modified", "%d features modified", ret), + ret); + break; + case MODE_CATDEL: + ret = Vedit_modify_cats(&Map, List, layer, 1, Clist); + G_message(n_("%d feature modified", "%d features modified", ret), + 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(n_("%d feature copied", "%d features copied", ret), 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(n_("%d line flipped", "%d lines flipped", ret), ret); + break; + case MODE_NONE: + break; + case MODE_ZBULK: { + double start, step; + double x1, y1, x2, y2; - ret = Vect_read_ascii(ascii, &Map); - if (ret > 0) { - int iline; - struct ilist *List_added; + start = atof(params.zbulk->answers[0]); + step = atof(params.zbulk->answers[1]); - G_message(n_("%d feature added", "%d features added", ret), ret); + x1 = atof(params.bbox->answers[0]); + y1 = atof(params.bbox->answers[1]); + x2 = atof(params.bbox->answers[2]); + y2 = atof(params.bbox->answers[3]); - List_added = Vect_new_list(); - for (iline = num_lines + 1; iline <= Vect_get_num_lines(&Map); - iline++) - Vect_list_append(List_added, iline); + ret = Vedit_bulk_labeling(&Map, List, x1, y1, x2, y2, start, step); - 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; + G_message(n_("%d line labeled", "%d lines labeled", ret), ret); + break; + } + case MODE_CHTYPE: + ret = Vedit_chtype_lines(&Map, List); - nclosed = close_lines(&Map, GV_BOUNDARY, thresh[THRESH_SNAP]); + if (ret > 0) { G_message( - n_("%d boundary closed", "%d boundaries closed", nclosed), - nclosed); + n_("%d feature converted", "%d features converted", ret), + ret); } - Vect_destroy_list(List_added); - } - break; - case MODE_DEL: - ret = Vedit_delete_lines(&Map, List); - G_message(n_("%d feature deleted", "%d features deleted", ret), 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(n_("%d feature moved", "%d features moved", ret), 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(n_("%d vertex moved", "%d vertices moved", ret), ret); - break; - case MODE_VERTEX_ADD: - ret = Vedit_add_vertex(&Map, List, coord, thresh[THRESH_COORDS]); - G_message(n_("%d vertex added", "%d vertices added", ret), ret); - break; - case MODE_VERTEX_DELETE: - ret = Vedit_remove_vertex(&Map, List, coord, thresh[THRESH_COORDS]); - G_message(n_("%d vertex removed", "%d vertices removed", ret), 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(n_("%d line broken", "%d lines broken", ret), 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(n_("%d line connected", "%d lines connected", ret), ret); - break; - case MODE_EXTEND: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(&Map, List, 0, extend_parallel, - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_EXTEND_START: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(&Map, List, 1, extend_parallel, - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_EXTEND_END: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(&Map, List, 2, extend_parallel, - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_MERGE: - ret = Vedit_merge_lines(&Map, List); - G_message(n_("%d line merged", "%d lines merged", ret), ret); - break; - case MODE_SELECT: - ret = print_selected(List); - break; - case MODE_CATADD: - ret = Vedit_modify_cats(&Map, List, layer, 0, Clist); - G_message(n_("%d feature modified", "%d features modified", ret), ret); - break; - case MODE_CATDEL: - ret = Vedit_modify_cats(&Map, List, layer, 1, Clist); - G_message(n_("%d feature modified", "%d features modified", ret), 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(n_("%d feature copied", "%d features copied", ret), 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(n_("%d line flipped", "%d lines flipped", ret), 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(n_("%d line labeled", "%d lines labeled", ret), ret); - break; - } - case MODE_CHTYPE: - ret = Vedit_chtype_lines(&Map, List); - - if (ret > 0) { - G_message(n_("%d feature converted", "%d features converted", ret), - ret); + else { + G_message(_("No feature modified")); + } + break; + case MODE_AREA_DEL: { + ret = 0; + for (i = 0; i < List->n_values; i++) { + if (Vect_get_line_type(&Map, List->value[i]) != GV_CENTROID) { + G_warning( + _("Select feature %d is not centroid, ignoring..."), + List->value[i]); + continue; + } + + ret += Vedit_delete_area_centroid(&Map, List->value[i]); + } + G_message(n_("%d area removed", "%d areas removed", ret), ret); + break; } - else { - G_message(_("No feature modified")); + default: + G_warning(_("Operation not implemented")); + ret = -1; + break; } - break; - case MODE_AREA_DEL: { - ret = 0; - for (i = 0; i < List->n_values; i++) { - if (Vect_get_line_type(&Map, List->value[i]) != GV_CENTROID) { - G_warning(_("Select feature %d is not centroid, ignoring..."), - List->value[i]); - continue; - } - ret += Vedit_delete_area_centroid(&Map, List->value[i]); + /* build topology only if requested or if tool!=select */ + if (action_mode != MODE_SELECT && action_mode != MODE_NONE && + params.topo->answer != 1) { + Vect_build_partial(&Map, GV_BUILD_NONE); + Vect_build(&Map); } - G_message(n_("%d area removed", "%d areas removed", ret), ret); - break; - } - default: - G_warning(_("Operation not implemented")); - ret = -1; - break; } - Vect_hist_command(&Map); + if (ascii && ascii != stdout) + fclose(ascii); - /* build topology only if requested or if tool!=select */ - if (action_mode != MODE_SELECT && action_mode != MODE_NONE && - params.topo->answer != 1) { - Vect_build_partial(&Map, GV_BUILD_NONE); - Vect_build(&Map); - } + Vect_hist_command(&Map); if (List) Vect_destroy_list(List); diff --git a/vector/v.edit/proto.h b/vector/v.edit/proto.h index b8d0226e60d..b7cd628997b 100644 --- a/vector/v.edit/proto.h +++ b/vector/v.edit/proto.h @@ -9,7 +9,7 @@ int close_lines(struct Map_info *, int, double); /* select.c */ int print_selected(struct ilist *); -struct ilist *select_lines(struct Map_info *, enum mode, struct GParams *, +struct ilist *select_lines(struct Map_info *, enum mode, struct SelectParams *, double *, struct ilist *); int sel_by_cat(struct Map_info *, struct cat_list *, int, int, char *, struct ilist *); @@ -23,6 +23,9 @@ int sel_by_where(struct Map_info *, int, int, char *, struct ilist *); int reverse_selection(struct Map_info *, int, struct ilist **); int sel_by_query(struct Map_info *, int, int, double, const char *, struct ilist *); +int str_to_coordinates(const char *, struct line_pnts *); +int str_to_bbox(const char *, struct line_pnts *); +int str_to_polygon(const char *, struct line_pnts *); /* snap.c */ int snap_lines(struct Map_info *, struct ilist *, double); @@ -32,4 +35,8 @@ int snap_line(struct Map_info *, int, int, double); double max_distance(double); void coord2bbox(double, double, double, struct line_pnts *); +/* batch.c */ +int batch_edit(struct Map_info *, struct Map_info **, int, const char *, + struct SelectParams *, double *); + #endif /* _V_EDIT_PROTO */ diff --git a/vector/v.edit/select.c b/vector/v.edit/select.c index ec5ec1db8b0..39cb2783455 100644 --- a/vector/v.edit/select.c +++ b/vector/v.edit/select.c @@ -20,7 +20,7 @@ #include #include "global.h" -static char first_selection = 1; +static char first_selection; static int merge_lists(struct ilist *, struct ilist *); static int merge_lists2(struct ilist *, struct boxlist *); @@ -35,39 +35,35 @@ static int merge_lists2(struct ilist *, struct boxlist *); \return list of newly selected features */ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, - struct GParams *params, double *thresh, + struct SelectParams *selparams, double *thresh, struct ilist *List) { int layer, type; - layer = Vect_get_field_number(Map, params->fld->answer); - type = Vect_option_to_types(params->type); + G_message(_("Selecting features...")); + + first_selection = 1; + + layer = selparams->layer; + type = selparams->type; /* select by id's */ - if (params->id->answer != NULL) { - sel_by_id(Map, type, params->id->answer, List); + if (selparams->ids && *selparams->ids) { + sel_by_id(Map, type, selparams->ids, List); } /* select by category (ignore tools catdel and catadd) */ if ((action_mode != MODE_CATADD && action_mode != MODE_CATDEL) && - params->cat->answer != NULL) { - sel_by_cat(Map, NULL, layer, type, params->cat->answer, List); + selparams->cats && *selparams->cats) { + sel_by_cat(Map, NULL, layer, type, selparams->cats, List); } /* select by coordinates (+threshold) */ - if (params->coord->answer != NULL) { - int i; - double east, north; + if (selparams->coords && *selparams->coords) { struct line_pnts *coords; coords = Vect_new_line_struct(); - i = 0; - while (params->coord->answers[i]) { - east = atof(params->coord->answers[i]); - north = atof(params->coord->answers[i + 1]); - Vect_append_point(coords, east, north, 0.0); - i += 2; - } + str_to_coordinates(selparams->coords, coords); G_verbose_message(_("Threshold value for coordinates is %.2f"), thresh[THRESH_COORDS]); @@ -77,22 +73,11 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } /* select by bbox */ - if (params->bbox->answer != NULL) { + if (selparams->bbox && *selparams->bbox) { struct line_pnts *bbox; - double x1, y1, x2, y2; bbox = Vect_new_line_struct(); - - x1 = atof(params->bbox->answers[0]); - y1 = atof(params->bbox->answers[1]); - x2 = atof(params->bbox->answers[2]); - y2 = atof(params->bbox->answers[3]); - - Vect_append_point(bbox, x1, y1, -PORT_DOUBLE_MAX); - Vect_append_point(bbox, x2, y1, PORT_DOUBLE_MAX); - Vect_append_point(bbox, x2, y2, -PORT_DOUBLE_MAX); - Vect_append_point(bbox, x1, y2, PORT_DOUBLE_MAX); - Vect_append_point(bbox, x1, y1, -PORT_DOUBLE_MAX); + str_to_bbox(selparams->bbox, bbox); /* sel_by_bbox not used */ /* @@ -106,23 +91,11 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } /* select by polygon */ - if (params->poly->answer != NULL) { - int i; + if (selparams->polygon && *selparams->polygon) { struct line_pnts *Polygon; Polygon = Vect_new_line_struct(); - - for (i = 0; params->poly->answers[i]; i += 2) { - Vect_append_point(Polygon, atof(params->poly->answers[i]), - atof(params->poly->answers[i + 1]), 0.0); - } - - /* if first and last point of polygon does not match */ - if (atof(params->poly->answers[i - 1]) != - atof(params->poly->answers[0])) { - Vect_append_point(Polygon, atof(params->poly->answers[0]), - atof(params->poly->answers[1]), 0.0); - } + str_to_polygon(selparams->polygon, Polygon); sel_by_polygon(Map, type, Polygon, List); @@ -130,12 +103,12 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } /* select by where statement */ - if (params->where->answer != NULL) { - sel_by_where(Map, layer, type, params->where->answer, List); + if (selparams->where && *selparams->where) { + sel_by_where(Map, layer, type, selparams->where, List); } /* selecy by query */ - if (params->query->answer != NULL) { + if (selparams->query && *selparams->query) { int query_type; struct ilist *List_tmp; @@ -148,10 +121,10 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } query_type = QUERY_UNKNOWN; - if (strcmp(params->query->answer, "length") == 0) { + if (strcmp(selparams->query, "length") == 0) { query_type = QUERY_LENGTH; } - else if (strcmp(params->query->answer, "dangle") == 0) { + else if (strcmp(selparams->query, "dangle") == 0) { query_type = QUERY_DANGLE; } @@ -167,7 +140,7 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } } - if (params->reverse->answer) { + if (selparams->reverse) { reverse_selection(Map, type, &List); } @@ -221,7 +194,6 @@ int sel_by_cat(struct Map_info *Map, struct cat_list *cl_orig, int layer, { struct ilist *List_tmp, *List_tmp1; struct cat_list *cl; - int i, cat; if (first_selection || cl_orig) { @@ -624,3 +596,85 @@ int reverse_selection(struct Map_info *Map, int type, struct ilist **List) return 1; } + +/** + \brief Convert string of list of coordinates to line_pnts + + \param[in] str list of coordinates as string + \param[in,out] coords pointer to line_pnts structure + + \return number of parsed points +*/ +int str_to_coordinates(const char *str, struct line_pnts *coords) +{ + char *p = (char *)str, *psep; + int npoints = 0, read_east = 1; + double east, north; + + while ((psep = strchr(p, ',')) || (psep = strchr(p, 0))) { + if (read_east) + sscanf(p, "%lf", &east); + else { + sscanf(p, "%lf", &north); + Vect_append_point(coords, east, north, 0.0); + npoints++; + } + + read_east = !read_east; + if (!*psep) + break; + p = psep + 1; + } + + if (!read_east) + G_fatal_error(_("Coordinates must be provided in multiples of %d"), 2); + + return npoints; +} + +/** + \brief Convert string of list of coordinates to bbox + + \param[in] str list of coordinates as string + \param[in,out] bbox pointer to line_pnts structure + + \return number of parsed points +*/ +int str_to_bbox(const char *str, struct line_pnts *bbox) +{ + double x1, y1, x2, y2; + + if (sscanf(str, "%lf,%lf,%lf,%lf", &x1, &y1, &x2, &y2) != 4) + G_fatal_error(_("Bounding box must have 2 coordinate pairs")); + + Vect_append_point(bbox, x1, y1, -PORT_DOUBLE_MAX); + Vect_append_point(bbox, x2, y1, PORT_DOUBLE_MAX); + Vect_append_point(bbox, x2, y2, -PORT_DOUBLE_MAX); + Vect_append_point(bbox, x1, y2, PORT_DOUBLE_MAX); + Vect_append_point(bbox, x1, y1, -PORT_DOUBLE_MAX); + + return 2; +} + +/** + \brief Convert string of list of coordinates to polygon + + \param[in] str list of coordinates as string + \param[in,out] polygon pointer to line_pnts structure + + \return number of parsed points +*/ +int str_to_polygon(const char *str, struct line_pnts *Polygon) +{ + int npoints; + + if ((npoints = str_to_coordinates(str, Polygon)) < 3) + G_fatal_error(_("Polygon must have at least 3 coordinate pairs")); + + /* if first and last point of polygon does not match */ + if (Polygon->x[Polygon->n_points - 1] != Polygon->x[0] || + Polygon->y[Polygon->n_points - 1] != Polygon->y[0]) + Vect_append_point(Polygon, Polygon->x[0], Polygon->y[0], 0.0); + + return npoints; +} diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 081deadf29c..14e9a14d0e0 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -168,6 +168,41 @@

Tool description

id's. No editing is done. +

Batch editing

+ +With the batch option, v.edit supports batch editing using a +pipe-separated table. The batch table supports the following column names: +
    +
  • tool (required) - Tool name from the tool option; + create and add tools are not supported
  • +
  • flags - -r, -1, and/or -p flags as a + combination of r, 1, and p without dashes
  • +
  • move - move option
  • +
  • ids - ids option
  • +
  • cats - cats option
  • +
  • coords - coords option
  • +
  • bbox - bbox option
  • +
  • polygon - polygon option
  • +
  • where - where option
  • +
  • query - query option
  • +
  • snap - snap option
  • +
  • zbulk - zbulk option
  • +
+The batch option is exclusive to all the above and input options. + +For each of the following lines, v.edit edit commands can be +specified. For example, this table when passed to the batch option +
+tool|flags|cats|coords
+# break||1|637108.11963224,257241.783755701
+# delete|r|1|
+
+is equivalent to the following individual commands: +
+v.edit map=vect_map tool=break cat=1 coords=637108.11963224,257241.783755701
+v.edit -r map=vect_map tool=delete cat=1
+
+

EXAMPLES

Create new vector map

@@ -430,6 +465,77 @@

Fix height of contours

zbulk=1000,10 +

Batch editing

+ +Delete lower halves of roadsmajor: + +
+# copy roadsmajor
+g.copy vect=roadsmajor,roadsmajor_upper_half
+
+# find road centers
+v.db.select map=roadsmajor -c col=cat |
+        awk '{printf "P %d %d 50%%\n", $1, $1}' |
+        v.segment roadsmajor output=roadsmajor_center
+
+# create a batch table and pipe it to v.edit
+(
+# use these batch columns
+echo "tool|cats|coords"
+
+# break roads at their center
+v.to.db -p map=roadsmajor_center option=coor sep=space |
+        awk '!/^cat/{printf "break|%d|%s,%s\n", $1, $2, $3}'
+
+# delete lower halves
+v.to.db -p map=roadsmajor option=end sep=space |
+        awk '!/^cat/{printf "delete|%d|%s,%s\n", $1, $2, $3}'
+
+# tool|cats|coords
+# break|1|637108.11963224,257241.783755701
+# break|2|636792.27198317,254339.365766968
+# break|3|638449.758677739,248299.357582029
+# ...
+# delete|1|637209.083058167,257970.129540226
+# delete|2|636981.336042672,256517.602235172
+# delete|3|637960.264160529,248293.868427705
+# ...
+) | v.edit map=roadsmajor_upper_half batch=-
+
+ +
+Upper-half major roads +
+ +On the i9-12900 CPU, the above batch command performed the same edits in 2 +seconds compared to 18 seconds by this script: + +
+# copy roadsmajor
+g.copy vect=roadsmajor,roadsmajor_upper_half
+
+# find road centers
+v.db.select map=roadsmajor -c col=cat |
+        awk '{printf "P %d %d 50%%\n", $1, $1}' |
+        v.segment roadsmajor output=roadsmajor_center
+
+# break roads at their center
+for line in $(v.to.db -p map=roadsmajor_center option=coor sep=comma | sed '/^cat/d'); do
+	cat=$(echo $line | cut -d, -f1)
+	x=$(echo $line | cut -d, -f2)
+	y=$(echo $line | cut -d, -f3)
+	v.edit map=roadsmajor_upper_half tool=break cats=$cat coords=$x,$y
+done
+
+# delete lower halves
+for line in $(v.to.db -p map=roadsmajor option=end sep=comma | sed '/^cat/d'); do
+	cat=$(echo $line | cut -d, -f1)
+	x=$(echo $line | cut -d, -f2)
+	y=$(echo $line | cut -d, -f3)
+	v.edit map=roadsmajor_upper_half tool=delete cats=$cat coords=$x,$y
+done
+
+

SEE ALSO

@@ -449,4 +555,4 @@

AUTHORS

Original author: Wolf Bergenheim - independent developer
Initial updates: Jachym Cepicky, Mendel University of Agriculture and Forestry in Brno, Czech Republic
Major update by Martin Landa, FBK-irst (formerly ITC-irst), Trento, Italy
-Extend tools by Huidae Cho +Extend tools and batch editing by Huidae Cho diff --git a/vector/v.edit/v_edit_roadsmajor_upper_half.png b/vector/v.edit/v_edit_roadsmajor_upper_half.png new file mode 100644 index 0000000000000000000000000000000000000000..8009ba663f9ba0c253dc7815ff4863a39b65ffbd GIT binary patch literal 43788 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 literal 0 HcmV?d00001 From 5eec2acf4cf2de14e1bf3788ad40710f8cb3c643 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sat, 24 Feb 2024 13:08:26 -0700 Subject: [PATCH 02/45] Add a sentence about the red color --- vector/v.edit/v.edit.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 14e9a14d0e0..83aa0cb36af 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -216,7 +216,7 @@

Create new vector map

Create new vector map and read data from file 'roads.txt':
-v.out.ascii in=roads format=standard > roads.txt;
+v.out.ascii in=roads format=standard > roads.txt;
 v.edit tool=create map=vectmap input=roads.txt
 
@@ -503,6 +503,8 @@

Batch editing

) | v.edit map=roadsmajor_upper_half batch=- +Red is the extracted upper halves of 'roadsmajor': +
Upper-half major roads
From 0767a7caa2e7a637eba7d667dc5f1aa24c148f26 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sat, 24 Feb 2024 13:14:33 -0700 Subject: [PATCH 03/45] Add empty lines --- vector/v.edit/batch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index e909f166554..c0859f6ec9e 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -418,6 +418,7 @@ static int get_flag(char *flags, char flag) static int get_snap(char *snap, double *thresh) { int snap_mode = NO_SNAP; + if (snap) { if (strcmp(snap, "node") == 0) snap_mode = SNAP; @@ -431,5 +432,6 @@ static int get_snap(char *snap, double *thresh) snap_mode = NO_SNAP; } } + return snap_mode; } From b58c427505a326cc492d47bfa026c362ad3bc446 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sat, 24 Feb 2024 13:24:52 -0700 Subject: [PATCH 04/45] Implement a faster benchmark sript --- vector/v.edit/v.edit.html | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 83aa0cb36af..5a6f09fb244 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -510,7 +510,7 @@

Batch editing

On the i9-12900 CPU, the above batch command performed the same edits in 2 -seconds compared to 18 seconds by this script: +seconds compared to 12 seconds by this script:
 # copy roadsmajor
@@ -522,18 +522,16 @@ 

Batch editing

v.segment roadsmajor output=roadsmajor_center # break roads at their center -for line in $(v.to.db -p map=roadsmajor_center option=coor sep=comma | sed '/^cat/d'); do - cat=$(echo $line | cut -d, -f1) - x=$(echo $line | cut -d, -f2) - y=$(echo $line | cut -d, -f3) +v.to.db -p map=roadsmajor_center option=coor sep=comma | + sed '/^cat/d' | + while read cat x y; do v.edit map=roadsmajor_upper_half tool=break cats=$cat coords=$x,$y done # delete lower halves -for line in $(v.to.db -p map=roadsmajor option=end sep=comma | sed '/^cat/d'); do - cat=$(echo $line | cut -d, -f1) - x=$(echo $line | cut -d, -f2) - y=$(echo $line | cut -d, -f3) +v.to.db -p map=roadsmajor option=end sep=comma | + sed '/^cat/d' | + while read cat x y; do v.edit map=roadsmajor_upper_half tool=delete cats=$cat coords=$x,$y done
From 336095cf0836cff8a3dfa43d57885e45a2c9ebb2 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sat, 24 Feb 2024 13:34:11 -0700 Subject: [PATCH 05/45] Add info about necessary columns and order --- vector/v.edit/v.edit.html | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 5a6f09fb244..a5d20d03484 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -188,10 +188,15 @@

Batch editing

  • snap - snap option
  • zbulk - zbulk option
  • + The batch option is exclusive to all the above and input options. +Not all the columns are required, but all necessary columns for all used tools +need to be declared even if some tools do not use certain options. In this +case, columns for these unused options can be left blank. The only required +column is tool. The order of columns is not important. -For each of the following lines, v.edit edit commands can be -specified. For example, this table when passed to the batch option +

    For each line of the table, v.edit commands can be specified. For +example, this table when passed to the batch option

     tool|flags|cats|coords
     # break||1|637108.11963224,257241.783755701
    
    From 656acbceb5cb90568205187ee532d85663245807 Mon Sep 17 00:00:00 2001
    From: Huidae Cho 
    Date: Sun, 25 Feb 2024 06:56:42 -0700
    Subject: [PATCH 06/45] Update vector/v.edit/args.c
    
    Co-authored-by: Martin Landa 
    ---
     vector/v.edit/args.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c
    index 4de565b34b7..eeac183dc0b 100644
    --- a/vector/v.edit/args.c
    +++ b/vector/v.edit/args.c
    @@ -227,7 +227,7 @@ int parser(int argc, char *argv[], struct GParams *params,
         params->batch = G_define_standard_option(G_OPT_F_INPUT);
         params->batch->key = "batch";
         params->batch->required = NO;
    -    params->batch->label = _("Name of command file for batching editing");
    +    params->batch->description = _("Name of command file for batching editing");
     
         G_option_required(params->tool, params->batch, NULL);
         G_option_exclusive(params->tool, params->batch, NULL);
    
    From 4360af440e34f1509eb6ce122e561105b0783fba Mon Sep 17 00:00:00 2001
    From: Huidae Cho 
    Date: Sun, 25 Feb 2024 06:57:01 -0700
    Subject: [PATCH 07/45] Update vector/v.edit/v.edit.html
    
    Co-authored-by: Martin Landa 
    ---
     vector/v.edit/v.edit.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html
    index a5d20d03484..7bb94580704 100644
    --- a/vector/v.edit/v.edit.html
    +++ b/vector/v.edit/v.edit.html
    @@ -199,7 +199,7 @@ 

    Batch editing

    example, this table when passed to the batch option
     tool|flags|cats|coords
    -# break||1|637108.11963224,257241.783755701
    +break||1|637108.11963224,257241.783755701
     # delete|r|1|
     
    is equivalent to the following individual commands: From a672d2509e2d181949d225c6385cc196369c0713 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 06:58:52 -0700 Subject: [PATCH 08/45] Update vector/v.edit/v.edit.html Co-authored-by: Martin Landa --- vector/v.edit/v.edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 7bb94580704..34c7540310b 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -200,7 +200,7 @@

    Batch editing

     tool|flags|cats|coords
     break||1|637108.11963224,257241.783755701
    -# delete|r|1|
    +delete|r|1|
     
    is equivalent to the following individual commands:
    
    From 9b28ac30e4fdc89848efb1714ddede9f9136d943 Mon Sep 17 00:00:00 2001
    From: Huidae Cho 
    Date: Sun, 25 Feb 2024 06:59:33 -0700
    Subject: [PATCH 09/45] batching editing => batch editing
    
    ---
     vector/v.edit/args.c | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c
    index eeac183dc0b..04c9ab5ee6c 100644
    --- a/vector/v.edit/args.c
    +++ b/vector/v.edit/args.c
    @@ -227,7 +227,7 @@ int parser(int argc, char *argv[], struct GParams *params,
         params->batch = G_define_standard_option(G_OPT_F_INPUT);
         params->batch->key = "batch";
         params->batch->required = NO;
    -    params->batch->description = _("Name of command file for batching editing");
    +    params->batch->description = _("Name of command file for batch editing");
     
         G_option_required(params->tool, params->batch, NULL);
         G_option_exclusive(params->tool, params->batch, NULL);
    
    From 701660741569b42d5646574835a1f4cca3d2ee99 Mon Sep 17 00:00:00 2001
    From: Huidae Cho 
    Date: Sun, 25 Feb 2024 07:02:26 -0700
    Subject: [PATCH 10/45] Add a comment about the produced table
    
    ---
     vector/v.edit/v.edit.html | 1 +
     1 file changed, 1 insertion(+)
    
    diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html
    index 34c7540310b..349dcd8b4f6 100644
    --- a/vector/v.edit/v.edit.html
    +++ b/vector/v.edit/v.edit.html
    @@ -496,6 +496,7 @@ 

    Batch editing

    v.to.db -p map=roadsmajor option=end sep=space | awk '!/^cat/{printf "delete|%d|%s,%s\n", $1, $2, $3}' +# the above three commands produce the following table: # tool|cats|coords # break|1|637108.11963224,257241.783755701 # break|2|636792.27198317,254339.365766968 From 1df8e5981c0eabecc805aecdc840e5349667d1e2 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 07:09:07 -0700 Subject: [PATCH 11/45] Add a description about stdin for batch= --- vector/v.edit/args.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c index 04c9ab5ee6c..4a4d2860166 100644 --- a/vector/v.edit/args.c +++ b/vector/v.edit/args.c @@ -227,7 +227,8 @@ int parser(int argc, char *argv[], struct GParams *params, params->batch = G_define_standard_option(G_OPT_F_INPUT); params->batch->key = "batch"; params->batch->required = NO; - params->batch->description = _("Name of command file for batch editing"); + params->batch->label = _("Name of command file for batch editing"); + params->batch->description = _("'-' for standard input"); G_option_required(params->tool, params->batch, NULL); G_option_exclusive(params->tool, params->batch, NULL); From 15d50e18b05f90fb450cbe08a3690b4f06ec1db4 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 07:14:01 -0700 Subject: [PATCH 12/45] Mention relative performance advantage in the manual --- vector/v.edit/v.edit.html | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 349dcd8b4f6..f46e1cd8154 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -171,7 +171,9 @@

    Tool description

    Batch editing

    With the batch option, v.edit supports batch editing using a -pipe-separated table. The batch table supports the following column names: +pipe-separated table. Batch editing can be faster than multiple independent +v.edit commands because of reduced overheads. The batch table supports +the following column names:
    • tool (required) - Tool name from the tool option; create and add tools are not supported
    • From 9298288baf61dbbe9b2235886a582ce953383738 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 07:29:46 -0700 Subject: [PATCH 13/45] Make batch= exclusive to all options and flags that are supported in the batch table or do not make sense --- vector/v.edit/args.c | 7 ++++--- vector/v.edit/v.edit.html | 6 +++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c index 4a4d2860166..a1ba6da7cde 100644 --- a/vector/v.edit/args.c +++ b/vector/v.edit/args.c @@ -227,12 +227,13 @@ int parser(int argc, char *argv[], struct GParams *params, params->batch = G_define_standard_option(G_OPT_F_INPUT); params->batch->key = "batch"; params->batch->required = NO; - params->batch->label = _("Name of command file for batch editing"); + params->batch->label = _("Name of input file for batch editing"); params->batch->description = _("'-' for standard input"); G_option_required(params->tool, params->batch, NULL); - G_option_exclusive(params->tool, params->batch, NULL); - G_option_exclusive(params->in, params->batch, NULL); + G_option_excludes(params->batch, params->in, params->tool, params->reverse, + params->close, params->header, params->topo, + params->move_first, params->extend_parallel, NULL); if (G_parser(argc, argv)) exit(EXIT_FAILURE); diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index f46e1cd8154..12c6eb455d7 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -210,6 +210,10 @@

      Batch editing

      v.edit -r map=vect_map tool=delete cat=1
    +

    For batch editing, topology is always built whenever there are any changes +in features because successive edits require properly built topology including +feature ID changes. +

    EXAMPLES

    Create new vector map

    @@ -518,7 +522,7 @@

    Batch editing

    On the i9-12900 CPU, the above batch command performed the same edits in 2 -seconds compared to 12 seconds by this script: +seconds compared to 12 seconds by this equivalent script:
     # copy roadsmajor
    
    From 480807ac17b8abb903f05a072d0ee189750539a1 Mon Sep 17 00:00:00 2001
    From: Huidae Cho 
    Date: Sun, 25 Feb 2024 07:33:48 -0700
    Subject: [PATCH 14/45] Make batch= exclusive to all options and flags that are
     supported in the batch table or do not make sense
    
    ---
     vector/v.edit/args.c | 9 ++++++---
     1 file changed, 6 insertions(+), 3 deletions(-)
    
    diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c
    index a1ba6da7cde..e79ac639e94 100644
    --- a/vector/v.edit/args.c
    +++ b/vector/v.edit/args.c
    @@ -231,9 +231,12 @@ int parser(int argc, char *argv[], struct GParams *params,
         params->batch->description = _("'-' for standard input");
     
         G_option_required(params->tool, params->batch, NULL);
    -    G_option_excludes(params->batch, params->in, params->tool, params->reverse,
    -                      params->close, params->header, params->topo,
    -                      params->move_first, params->extend_parallel, NULL);
    +    G_option_excludes(params->batch, params->tool, params->in, params->move,
    +                      params->id, params->cat, params->coord, params->bbox,
    +                      params->poly, params->where, params->query, params->snap,
    +                      params->zbulk, params->reverse, params->close,
    +                      params->header, params->topo, params->move_first,
    +                      params->extend_parallel, NULL);
     
         if (G_parser(argc, argv))
             exit(EXIT_FAILURE);
    
    From 7186828f59a19da8a4c20653ab09e1271a7fa17e Mon Sep 17 00:00:00 2001
    From: Huidae Cho 
    Date: Sun, 25 Feb 2024 08:11:31 -0700
    Subject: [PATCH 15/45] Add an affiliation for Huidae Cho
    
    ---
     vector/v.edit/v.edit.html | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html
    index 12c6eb455d7..3519a904ed1 100644
    --- a/vector/v.edit/v.edit.html
    +++ b/vector/v.edit/v.edit.html
    @@ -567,4 +567,4 @@ 

    AUTHORS

    Original author: Wolf Bergenheim - independent developer
    Initial updates: Jachym Cepicky, Mendel University of Agriculture and Forestry in Brno, Czech Republic
    Major update by Martin Landa, FBK-irst (formerly ITC-irst), Trento, Italy
    -Extend tools and batch editing by Huidae Cho +Extend tools and batch editing by Huidae Cho, New Mexico State University From 76d3f5e3b60539e2fc485497cd56099f5876e41f Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 08:25:20 -0700 Subject: [PATCH 16/45] Inline get_flag(); Move p = strchr() closer to its relevant code --- vector/v.edit/batch.c | 54 ++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index c0859f6ec9e..bcea89fe777 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -1,22 +1,23 @@ #include "global.h" -#define MAX_COLUMNS 12 -#define NUM_TOOLS 20 - -#define COLUMN_TOOL 0 -#define COLUMN_FLAGS 1 -#define COLUMN_MOVE 2 -#define COLUMN_IDS 3 -#define COLUMN_CATS 4 -#define COLUMN_COORDS 5 -#define COLUMN_BBOX 6 -#define COLUMN_POLYGON 7 -#define COLUMN_WHERE 8 -#define COLUMN_QUERY 9 -#define COLUMN_SNAP 10 -#define COLUMN_ZBULK 11 - -static int get_flag(char *, char); +#define GET_FLAG(flags, flag) ((flags) && strchr(flags, flag)) + +#define MAX_COLUMNS 12 +#define NUM_TOOLS 20 + +#define COLUMN_TOOL 0 +#define COLUMN_FLAGS 1 +#define COLUMN_MOVE 2 +#define COLUMN_IDS 3 +#define COLUMN_CATS 4 +#define COLUMN_COORDS 5 +#define COLUMN_BBOX 6 +#define COLUMN_POLYGON 7 +#define COLUMN_WHERE 8 +#define COLUMN_QUERY 9 +#define COLUMN_SNAP 10 +#define COLUMN_ZBULK 11 + static int get_snap(char *, double *thresh); int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, @@ -55,7 +56,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, fp = stdin; while (fgets(buf, 1024, fp)) { - char *p = strchr(buf, '\n'), *psep; + char *p, *psep; int last = 0; enum mode action_mode; char *flags, *move, *snap, *zbulk; @@ -72,7 +73,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, flags = move = snap = zbulk = NULL; /* remove newline */ - if (p) { + if ((p = strchr(buf, '\n'))) { *p = 0; if ((p = strchr(buf, '\r'))) *p = 0; @@ -192,7 +193,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, List = Vect_new_list(); - selparams->reverse = get_flag(flags, 'r'); + selparams->reverse = GET_FLAG(flags, 'r'); if (action_mode == MODE_COPY && BgMap && BgMap[0]) List = select_lines(BgMap[0], action_mode, selparams, thresh, List); else @@ -283,21 +284,21 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, case MODE_EXTEND: G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(Map, List, 0, get_flag(flags, 'p'), + ret = Vedit_extend_lines(Map, List, 0, GET_FLAG(flags, 'p'), thresh[THRESH_SNAP]); G_message(n_("%d line extended", "%d lines extended", ret), ret); break; case MODE_EXTEND_START: G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(Map, List, 1, get_flag(flags, 'p'), + ret = Vedit_extend_lines(Map, List, 1, GET_FLAG(flags, 'p'), thresh[THRESH_SNAP]); G_message(n_("%d line extended", "%d lines extended", ret), ret); break; case MODE_EXTEND_END: G_verbose_message(_("Threshold value for snapping is %.2f"), thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(Map, List, 2, get_flag(flags, 'p'), + ret = Vedit_extend_lines(Map, List, 2, GET_FLAG(flags, 'p'), thresh[THRESH_SNAP]); G_message(n_("%d line extended", "%d lines extended", ret), ret); break; @@ -328,7 +329,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, ret = Vedit_move_vertex( Map, BgMap, nbgmaps, List, coord, thresh[THRESH_COORDS], thresh[THRESH_SNAP], move_x, move_y, move_z, - get_flag(flags, '1'), get_snap(snap, thresh)); + GET_FLAG(flags, '1'), get_snap(snap, thresh)); G_message(n_("%d vertex moved", "%d vertices moved", ret), ret); break; } @@ -410,11 +411,6 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, return total_ret; } -static int get_flag(char *flags, char flag) -{ - return flags && strchr(flags, flag); -} - static int get_snap(char *snap, double *thresh) { int snap_mode = NO_SNAP; From ddd2011472aec8447f3d97d92d31286585f67f19 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 08:32:13 -0700 Subject: [PATCH 17/45] Add an empty line after variable declarations --- vector/v.edit/batch.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index bcea89fe777..b6c66b22c6a 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -43,9 +43,8 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, MODE_VERTEX_MOVE, MODE_AREA_DEL, MODE_ZBULK, MODE_SELECT}; FILE *fp; char buf[1024]; - int first = 1; + int line = 0, first = 1; int cols[MAX_COLUMNS], ncols = 0; - int line = 0; int total_ret = 0; if (strcmp(file, "-") != 0) { @@ -101,6 +100,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, while (!last && ((psep = strchr(p, '|')) || (psep = strchr(p, 0)))) { int known_col = 0; + last = !*psep; *psep = 0; for (i = 0; i < MAX_COLUMNS; i++) { @@ -133,6 +133,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, i = 0; while (!last && ((psep = strchr(p, '|')) || (psep = strchr(p, 0)))) { int j; + last = !*psep; *psep = 0; @@ -233,6 +234,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, break; case MODE_MOVE: { double move_x, move_y, move_z; + if (sscanf(p, "%lf,%lf,%lf", &move_x, &move_y, &move_z) != 3) G_fatal_error(_("'%s' tool must have '%s' column"), "move", "move"); @@ -321,6 +323,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, break; case MODE_VERTEX_MOVE: { double move_x, move_y, move_z; + if (sscanf(p, "%lf,%lf,%lf", &move_x, &move_y, &move_z) != 3) G_fatal_error(_("'%s' tool must have '%s' column"), "vertexmove", "move"); From 76e9fbf83d23fae34b80d7ad21614b794954348f Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 08:39:46 -0700 Subject: [PATCH 18/45] Rerun grass_clang_format on long strings in batch.c --- vector/v.edit/batch.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index b6c66b22c6a..5e8c43bbf08 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -221,10 +221,10 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, 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])); + 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); } @@ -357,8 +357,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (!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 " + "3D vector map. Please convert the vector map " "to 3D using e.g. %s."), Map->name, "zbulk", "v.extrude"); } @@ -426,8 +425,8 @@ static int get_snap(char *snap, double *thresh) else if (strcmp(snap, "no") != 0) G_fatal_error(_("Unsupported snap '%s'"), snap); if (snap_mode != NO_SNAP && thresh[THRESH_SNAP] <= 0) { - G_warning(_("Threshold for snapping must be > 0. No " - "snapping applied.")); + G_warning( + _("Threshold for snapping must be > 0. No snapping applied.")); snap_mode = NO_SNAP; } } From 43ec64eed28617157853a5f04e0ef8ca4828081b Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 08:59:38 -0700 Subject: [PATCH 19/45] Micro-optimize short-circuit evaluation for *editing* (non-select) tools --- vector/v.edit/batch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 5e8c43bbf08..7d9938cb336 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -387,7 +387,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, break; } - if (ret && action_mode != MODE_SELECT) { + if (action_mode != MODE_SELECT && ret) { Vect_build_partial(Map, GV_BUILD_NONE); Vect_build(Map); total_ret += ret; From 23c062076b3386810d9cc33f7d3ead359e78daf5 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 19:51:56 -0700 Subject: [PATCH 20/45] Support single-character separators and double-quoted columns for more generic CSV-like batch tables --- vector/v.edit/args.c | 3 ++ vector/v.edit/batch.c | 82 ++++++++++++++++++++++++++++++++------- vector/v.edit/global.h | 2 +- vector/v.edit/main.c | 7 +++- vector/v.edit/proto.h | 2 +- vector/v.edit/v.edit.html | 8 ++-- 6 files changed, 84 insertions(+), 20 deletions(-) diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c index e79ac639e94..94493aa8830 100644 --- a/vector/v.edit/args.c +++ b/vector/v.edit/args.c @@ -230,6 +230,9 @@ int parser(int argc, char *argv[], struct GParams *params, params->batch->label = _("Name of input file for batch editing"); params->batch->description = _("'-' for standard input"); + params->sep = G_define_standard_option(G_OPT_F_SEP); + params->sep->label = _("Field separator for batch input file"); + G_option_required(params->tool, params->batch, NULL); G_option_excludes(params->batch, params->tool, params->in, params->move, params->id, params->cat, params->coord, params->bbox, diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 7d9938cb336..65e2d690bde 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -18,10 +18,12 @@ #define COLUMN_SNAP 10 #define COLUMN_ZBULK 11 +static char *read_column(char *, char, char **); static int get_snap(char *, double *thresh); int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, - const char *file, struct SelectParams *selparams, double *thresh) + const char *file, char sep, struct SelectParams *selparams, + double *thresh) { char *col_names[MAX_COLUMNS] = {"tool", "flags", "move", "ids", "cats", "coords", "bbox", "polygon", @@ -55,8 +57,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, fp = stdin; while (fgets(buf, 1024, fp)) { - char *p, *psep; - int last = 0; + char *p, *pnext; enum mode action_mode; char *flags, *move, *snap, *zbulk; struct ilist *List; @@ -97,12 +98,9 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (first) { int bit_cols = 0, bit_col; - while (!last && - ((psep = strchr(p, '|')) || (psep = strchr(p, 0)))) { + while (p && (p = read_column(p, sep, &pnext))) { int known_col = 0; - last = !*psep; - *psep = 0; for (i = 0; i < MAX_COLUMNS; i++) { if (strcmp(p, col_names[i]) == 0) { bit_col = 1 << col_nums[i]; @@ -118,7 +116,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (!known_col) G_fatal_error(_("Unknown batch column '%s'"), p); - p = psep + 1; + p = pnext; } if (!(bit_cols & 1 << COLUMN_TOOL)) @@ -131,12 +129,9 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, G_message(_("Batch line %d..."), line); i = 0; - while (!last && ((psep = strchr(p, '|')) || (psep = strchr(p, 0)))) { + while (p && (p = read_column(p, sep, &pnext))) { int j; - last = !*psep; - *psep = 0; - if (i >= ncols) G_fatal_error(_("Too many batch columns in line %d"), line); @@ -186,7 +181,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, } i++; - p = psep + 1; + p = pnext; } if (i < ncols) @@ -413,6 +408,67 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, return total_ret; } +/** + \brief Read a column from pcol string based on + https://www.rfc-editor.org/rfc/rfc4180 + + \param[in] pcol pointer to the start of a new column; this buffer is modified + \param[in] sep separater character + \param[out] pnext pointer to the next column or NULL if last column + + \return pointer to column + \return NULL if illegal column +*/ +static char *read_column(char *pcol, char sep, char **pnext) +{ + if (*pcol == '"') { + char *p = ++pcol; + + while ((*pnext = strchr(p, '"')) && *(*pnext + 1) == '"') + p = *pnext + 2; + + if (*pnext) { + char s = *(*pnext + 1); + + if (s == sep || !s) { + /* remove closing quote */ + **pnext = 0; + if (s == sep) + /* skip to next column */ + *pnext += 2; + else + /* last column */ + *pnext = NULL; + } + else + /* extra characters after last column; illegal column */ + pcol = NULL; + + /* convert "" to " */ + if ((p = pcol)) { + while (*p) { + if (*p == '"') { + char *q; + + for (q = p; *(q + 1); q++) + *q = *(q + 1); + *q = 0; + } + p++; + } + } + } + else + /* closing quote is missing; illegal column */ + pcol = NULL; + } + else if ((*pnext = strchr(pcol, sep))) + *(*pnext)++ = 0; + /* else last column */ + + return pcol; +} + static int get_snap(char *snap, double *thresh) { int snap_mode = NO_SNAP; diff --git a/vector/v.edit/global.h b/vector/v.edit/global.h index 93f4b47ce50..86bd73603e9 100644 --- a/vector/v.edit/global.h +++ b/vector/v.edit/global.h @@ -51,7 +51,7 @@ enum mode { struct GParams { struct Option *map, *in, *maxdist, *tool, *coord, *cat, *move, *bbox, *fld, - *poly, *type, *id, *where, *bmaps, *snap, *query, *zbulk, *batch; + *poly, *type, *id, *where, *bmaps, *snap, *query, *zbulk, *batch, *sep; struct Flag *header, *topo, *close, *reverse, *move_first, *extend_parallel; }; diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index cc0aa07dc32..d2fd4d9ba76 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -196,6 +196,11 @@ int main(int argc, char *argv[]) } if (params.batch->answer) { + char *sep = G_option_to_separator(params.sep); + + if (sep[1]) + G_fatal_error(_("Field separator must be a single character")); + if (Vect_open_update2(&Map, params.map->answer, G_mapset(), params.fld->answer) < 0) G_fatal_error(_("Unable to open vector map <%s>"), @@ -206,7 +211,7 @@ int main(int argc, char *argv[]) Vect_get_field_number(BgMap[0], params.fld->answer); selparams.type = Vect_option_to_types(params.type); - batch_edit(&Map, BgMap, nbgmaps, params.batch->answer, &selparams, + batch_edit(&Map, BgMap, nbgmaps, params.batch->answer, *sep, &selparams, thresh); } else { diff --git a/vector/v.edit/proto.h b/vector/v.edit/proto.h index b7cd628997b..1cf821ffe68 100644 --- a/vector/v.edit/proto.h +++ b/vector/v.edit/proto.h @@ -36,7 +36,7 @@ double max_distance(double); void coord2bbox(double, double, double, struct line_pnts *); /* batch.c */ -int batch_edit(struct Map_info *, struct Map_info **, int, const char *, +int batch_edit(struct Map_info *, struct Map_info **, int, const char *, char, struct SelectParams *, double *); #endif /* _V_EDIT_PROTO */ diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 3519a904ed1..95477841af0 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -171,7 +171,7 @@

    Tool description

    Batch editing

    With the batch option, v.edit supports batch editing using a -pipe-separated table. Batch editing can be faster than multiple independent +CSV-like table. Batch editing can be faster than multiple independent v.edit commands because of reduced overheads. The batch table supports the following column names:
      @@ -200,9 +200,9 @@

      Batch editing

      For each line of the table, v.edit commands can be specified. For example, this table when passed to the batch option

      -tool|flags|cats|coords
      -break||1|637108.11963224,257241.783755701
      -delete|r|1|
      +tool,flags,cats,coords
      +break,,1,"637108.11963224,257241.783755701"
      +delete,r,1,
       
      is equivalent to the following individual commands:
      
      From 7398b13ce1a5599140d7959e9495ea1b430db9f8 Mon Sep 17 00:00:00 2001
      From: Huidae Cho 
      Date: Sun, 25 Feb 2024 20:39:35 -0700
      Subject: [PATCH 21/45] Add a sentence about a single-character separator
      
      ---
       vector/v.edit/v.edit.html | 5 ++++-
       1 file changed, 4 insertions(+), 1 deletion(-)
      
      diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html
      index 95477841af0..4ff0a968fe5 100644
      --- a/vector/v.edit/v.edit.html
      +++ b/vector/v.edit/v.edit.html
      @@ -198,7 +198,8 @@ 

      Batch editing

      column is tool. The order of columns is not important.

      For each line of the table, v.edit commands can be specified. For -example, this table when passed to the batch option +example, this table when passed to the batch option with +separator=comma

       tool,flags,cats,coords
       break,,1,"637108.11963224,257241.783755701"
      @@ -214,6 +215,8 @@ 

      Batch editing

      in features because successive edits require properly built topology including feature ID changes. +

      The

      separator

      option only supports a single-character separator. +

      EXAMPLES

      Create new vector map

      From 3d27af7d242dfb917cbfd1466c5b630d3c808310 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 20:44:47 -0700 Subject: [PATCH 22/45] Disallow newline as seprator --- vector/v.edit/args.c | 1 + vector/v.edit/main.c | 2 ++ vector/v.edit/v.edit.html | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/vector/v.edit/args.c b/vector/v.edit/args.c index 94493aa8830..ec161e99ff9 100644 --- a/vector/v.edit/args.c +++ b/vector/v.edit/args.c @@ -232,6 +232,7 @@ int parser(int argc, char *argv[], struct GParams *params, params->sep = G_define_standard_option(G_OPT_F_SEP); params->sep->label = _("Field separator for batch input file"); + params->sep->description = _("Special characters: pipe, comma, space, tab"); G_option_required(params->tool, params->batch, NULL); G_option_excludes(params->batch, params->tool, params->in, params->move, diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index d2fd4d9ba76..c7a94a1c6a6 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -200,6 +200,8 @@ int main(int argc, char *argv[]) if (sep[1]) G_fatal_error(_("Field separator must be a single character")); + else if (*sep == '\n') + G_fatal_error(_("Field separator cannot be a newline")); if (Vect_open_update2(&Map, params.map->answer, G_mapset(), params.fld->answer) < 0) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 4ff0a968fe5..6faedaa9652 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -215,7 +215,8 @@

      Batch editing

      in features because successive edits require properly built topology including feature ID changes. -

      The

      separator

      option only supports a single-character separator. +

      The

      separator

      option only supports a single-character separator that +is not a newline.

      EXAMPLES

      From 4336502553dfe4681699f9fe725c6232b5de8ec5 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 20:48:31 -0700 Subject: [PATCH 23/45] Empty lines --- vector/v.edit/batch.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 65e2d690bde..33869b2aa45 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -122,6 +122,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (!(bit_cols & 1 << COLUMN_TOOL)) G_fatal_error(_("Required batch column '%s' missing"), col_names[COLUMN_TOOL]); + first = 0; continue; } @@ -343,7 +344,6 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, ret += Vedit_delete_area_centroid(Map, List->value[i]); } G_message(n_("%d area removed", "%d areas removed", ret), ret); - break; case MODE_ZBULK: { double start, step; @@ -366,7 +366,6 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, G_fatal_error(_("ZBulk must have bbox")); ret = Vedit_bulk_labeling(Map, List, x1, y1, x2, y2, start, step); - G_message(n_("%d line labeled", "%d lines labeled", ret), ret); break; } From 47f75db49b97799ce86db7e9ee1ac7411ac6f8e2 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 22:00:54 -0700 Subject: [PATCH 24/45] Fatal error on multiline columns --- vector/v.edit/batch.c | 31 ++++++++++++++++++++++--------- vector/v.edit/main.c | 2 +- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 33869b2aa45..fcaae76b2f3 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -98,30 +98,37 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (first) { int bit_cols = 0, bit_col; - while (p && (p = read_column(p, sep, &pnext))) { + while ((p = read_column(p, sep, &pnext))) { int known_col = 0; for (i = 0; i < MAX_COLUMNS; i++) { if (strcmp(p, col_names[i]) == 0) { bit_col = 1 << col_nums[i]; if (bit_cols & bit_col) - G_fatal_error( - _("Duplicate batch column '%s' not allowed"), - col_names[i]); + G_fatal_error(_("Duplicate batch column '%s' not " + "allowed in line %d"), + col_names[i], line); bit_cols |= bit_col; cols[ncols++] = col_nums[i]; known_col = 1; } } if (!known_col) - G_fatal_error(_("Unknown batch column '%s'"), p); + G_fatal_error(_("Unknown batch column '%s' in line %d"), p, + line); + if (!pnext) + break; p = pnext; } + if (!p) + G_fatal_error(_("Illegal batch column in line %d"), line); + if (!(bit_cols & 1 << COLUMN_TOOL)) - G_fatal_error(_("Required batch column '%s' missing"), - col_names[COLUMN_TOOL]); + G_fatal_error( + _("Required batch column '%s' missing in line %d"), + col_names[COLUMN_TOOL], line); first = 0; continue; @@ -130,7 +137,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, G_message(_("Batch line %d..."), line); i = 0; - while (p && (p = read_column(p, sep, &pnext))) { + while ((p = read_column(p, sep, &pnext))) { int j; if (i >= ncols) @@ -144,7 +151,8 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, break; } if (j == NUM_TOOLS) - G_fatal_error(_("Unsupported tool '%s'"), p); + G_fatal_error(_("Unsupported tool '%s' in line %d"), p, + line); break; case COLUMN_FLAGS: flags = p; @@ -182,9 +190,14 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, } i++; + if (!pnext) + break; p = pnext; } + if (!p) + G_fatal_error(_("Illegal batch column in line %d"), line); + if (i < ncols) G_fatal_error(_("Too few batch columns in line %d"), line); diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index c7a94a1c6a6..73d9bb2fc08 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -198,7 +198,7 @@ int main(int argc, char *argv[]) if (params.batch->answer) { char *sep = G_option_to_separator(params.sep); - if (sep[1]) + if (*(sep + 1)) G_fatal_error(_("Field separator must be a single character")); else if (*sep == '\n') G_fatal_error(_("Field separator cannot be a newline")); From 3ad14c5b4c37103c4a6e7f05d9a30397217aba2d Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 22:56:58 -0700 Subject: [PATCH 25/45] Support add tool in batch mode --- vector/v.edit/batch.c | 228 ++++++++++++++++++++++++-------------- vector/v.edit/main.c | 2 +- vector/v.edit/select.c | 19 ++-- vector/v.edit/v.edit.html | 3 +- 4 files changed, 158 insertions(+), 94 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index fcaae76b2f3..19246f18240 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -2,21 +2,22 @@ #define GET_FLAG(flags, flag) ((flags) && strchr(flags, flag)) -#define MAX_COLUMNS 12 -#define NUM_TOOLS 20 +#define MAX_COLUMNS 13 +#define NUM_TOOLS 21 #define COLUMN_TOOL 0 #define COLUMN_FLAGS 1 -#define COLUMN_MOVE 2 -#define COLUMN_IDS 3 -#define COLUMN_CATS 4 -#define COLUMN_COORDS 5 -#define COLUMN_BBOX 6 -#define COLUMN_POLYGON 7 -#define COLUMN_WHERE 8 -#define COLUMN_QUERY 9 -#define COLUMN_SNAP 10 -#define COLUMN_ZBULK 11 +#define COLUMN_INPUT 2 +#define COLUMN_MOVE 3 +#define COLUMN_IDS 4 +#define COLUMN_CATS 5 +#define COLUMN_COORDS 6 +#define COLUMN_BBOX 7 +#define COLUMN_POLYGON 8 +#define COLUMN_WHERE 9 +#define COLUMN_QUERY 10 +#define COLUMN_SNAP 11 +#define COLUMN_ZBULK 12 static char *read_column(char *, char, char **); static int get_snap(char *, double *thresh); @@ -25,24 +26,26 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, const char *file, char sep, struct SelectParams *selparams, double *thresh) { - char *col_names[MAX_COLUMNS] = {"tool", "flags", "move", "ids", - "cats", "coords", "bbox", "polygon", - "where", "query", "snap", "zbulk"}; - int col_nums[MAX_COLUMNS] = {COLUMN_TOOL, COLUMN_FLAGS, COLUMN_MOVE, - COLUMN_IDS, COLUMN_CATS, COLUMN_COORDS, - COLUMN_BBOX, COLUMN_POLYGON, COLUMN_WHERE, - COLUMN_QUERY, COLUMN_SNAP, COLUMN_ZBULK}; + char *col_names[MAX_COLUMNS] = { + "tool", "flags", "input", "move", "ids", "cats", "coords", + "bbox", "polygon", "where", "query", "snap", "zbulk"}; + int col_nums[MAX_COLUMNS] = { + COLUMN_TOOL, COLUMN_FLAGS, COLUMN_INPUT, COLUMN_MOVE, COLUMN_IDS, + COLUMN_CATS, COLUMN_COORDS, COLUMN_BBOX, COLUMN_POLYGON, COLUMN_WHERE, + COLUMN_QUERY, COLUMN_SNAP, COLUMN_ZBULK}; char *tool_names[NUM_TOOLS] = { - "delete", "copy", "move", "flip", "catadd", - "catdel", "merge", "break", "snap", "connect", - "extend", "extendstart", "extendend", "chtype", "vertexadd", - "vertexdel", "vertexmove", "areadel", "zbulk", "select"}; + "add", "delete", "copy", "move", "flip", + "catadd", "catdel", "merge", "break", "snap", + "connect", "extend", "extendstart", "extendend", "chtype", + "vertexadd", "vertexdel", "vertexmove", "areadel", "zbulk", + "select"}; enum mode tool_modes[NUM_TOOLS] = { - MODE_DEL, MODE_COPY, MODE_MOVE, MODE_FLIP, - MODE_CATADD, MODE_CATDEL, MODE_MERGE, MODE_BREAK, - MODE_SNAP, MODE_CONNECT, MODE_EXTEND, MODE_EXTEND_START, - MODE_EXTEND_END, MODE_CHTYPE, MODE_VERTEX_ADD, MODE_VERTEX_DELETE, - MODE_VERTEX_MOVE, MODE_AREA_DEL, MODE_ZBULK, MODE_SELECT}; + MODE_ADD, MODE_DEL, MODE_COPY, MODE_MOVE, + MODE_FLIP, MODE_CATADD, MODE_CATDEL, MODE_MERGE, + MODE_BREAK, MODE_SNAP, MODE_CONNECT, MODE_EXTEND, + MODE_EXTEND_START, MODE_EXTEND_END, MODE_CHTYPE, MODE_VERTEX_ADD, + MODE_VERTEX_DELETE, MODE_VERTEX_MOVE, MODE_AREA_DEL, MODE_ZBULK, + MODE_SELECT}; FILE *fp; char buf[1024]; int line = 0, first = 1; @@ -59,7 +62,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, while (fgets(buf, 1024, fp)) { char *p, *pnext; enum mode action_mode; - char *flags, *move, *snap, *zbulk; + char *flags, *input, *move, *snap, *zbulk; struct ilist *List; struct line_pnts *coord = NULL; struct cat_list *Clist = NULL; @@ -70,7 +73,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, selparams->ids = selparams->cats = selparams->coords = selparams->bbox = selparams->polygon = selparams->where = selparams->query = NULL; - flags = move = snap = zbulk = NULL; + flags = input = move = snap = zbulk = NULL; /* remove newline */ if ((p = strchr(buf, '\n'))) { @@ -143,50 +146,55 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (i >= ncols) G_fatal_error(_("Too many batch columns in line %d"), line); - switch (cols[i]) { - case COLUMN_TOOL: - for (j = 0; j < NUM_TOOLS; j++) - if (strcmp(p, tool_names[j]) == 0) { - action_mode = tool_modes[j]; - break; - } - if (j == NUM_TOOLS) - G_fatal_error(_("Unsupported tool '%s' in line %d"), p, - line); - break; - case COLUMN_FLAGS: - flags = p; - break; - case COLUMN_MOVE: - move = p; - break; - case COLUMN_IDS: - selparams->ids = p; - break; - case COLUMN_CATS: - selparams->cats = p; - break; - case COLUMN_COORDS: - selparams->coords = p; - break; - case COLUMN_BBOX: - selparams->bbox = p; - break; - case COLUMN_POLYGON: - selparams->polygon = p; - break; - case COLUMN_WHERE: - selparams->where = p; - break; - case COLUMN_QUERY: - selparams->query = p; - break; - case COLUMN_SNAP: - snap = p; - break; - case COLUMN_ZBULK: - zbulk = p; - break; + if (*p) { + switch (cols[i]) { + case COLUMN_TOOL: + for (j = 0; j < NUM_TOOLS; j++) + if (strcmp(p, tool_names[j]) == 0) { + action_mode = tool_modes[j]; + break; + } + if (j == NUM_TOOLS) + G_fatal_error(_("Unsupported tool '%s' in line %d"), p, + line); + break; + case COLUMN_FLAGS: + flags = p; + break; + case COLUMN_INPUT: + input = p; + break; + case COLUMN_MOVE: + move = p; + break; + case COLUMN_IDS: + selparams->ids = p; + break; + case COLUMN_CATS: + selparams->cats = p; + break; + case COLUMN_COORDS: + selparams->coords = p; + break; + case COLUMN_BBOX: + selparams->bbox = p; + break; + case COLUMN_POLYGON: + selparams->polygon = p; + break; + case COLUMN_WHERE: + selparams->where = p; + break; + case COLUMN_QUERY: + selparams->query = p; + break; + case COLUMN_SNAP: + snap = p; + break; + case COLUMN_ZBULK: + zbulk = p; + break; + } } i++; @@ -206,22 +214,80 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, selparams->reverse = GET_FLAG(flags, 'r'); if (action_mode == MODE_COPY && BgMap && BgMap[0]) List = select_lines(BgMap[0], action_mode, selparams, thresh, List); - else + else if (action_mode != MODE_ADD) List = select_lines(Map, action_mode, selparams, thresh, List); - if (selparams->cats && *selparams->cats) { + if (action_mode != MODE_ADD && action_mode != MODE_SELECT && + !List->n_values) { + G_warning(_("No features selected, nothing to edit")); + action_mode = MODE_NONE; + ret = 0; + } + + if ((action_mode == MODE_CATADD || action_mode == MODE_CATDEL) && + selparams->cats) { Clist = Vect_new_cat_list(); if (Vect_str_to_cat_list(selparams->cats, Clist)) G_fatal_error(_("Unable to get category list <%s>"), selparams->cats); } - if (selparams->coords && *selparams->coords) { + if (selparams->coords) { coord = Vect_new_line_struct(); str_to_coordinates(selparams->coords, coord); } switch (action_mode) { + case MODE_ADD: { + FILE *ascii; + int num_lines; + int snap_mode = get_snap(snap, thresh); + + if (!input) + G_fatal_error(_("Input file not specified in line %d"), line); + + if (!(ascii = fopen(input, "r"))) + G_fatal_error(_("Unable to open file <%s>"), input); + + if (!GET_FLAG(flags, 'n')) + Vect_read_ascii_head(ascii, Map); + + num_lines = Vect_get_num_lines(Map); + ret = Vect_read_ascii(ascii, Map); + + fclose(ascii); + + if (ret > 0) { + int iline; + struct ilist *List_added; + + G_message(n_("%d feature added", "%d features added", ret), + ret); + + 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_mode != NO_SNAP) { /* apply snapping */ + /* snap to vertex ? */ + Vedit_snap_lines(Map, BgMap, nbgmaps, List_added, + thresh[THRESH_SNAP], + snap_mode == SNAP ? FALSE : TRUE); + } + if (GET_FLAG(flags, 'c')) { /* close boundaries */ + int nclosed = + close_lines(Map, GV_BOUNDARY, thresh[THRESH_SNAP]); + G_message(n_("%d boundary closed", "%d boundaries closed", + nclosed), + nclosed); + } + Vect_destroy_list(List_added); + } + break; + } case MODE_DEL: ret = Vedit_delete_lines(Map, List); G_message(n_("%d feature deleted", "%d features deleted", ret), @@ -385,12 +451,12 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, case MODE_SELECT: ret = print_selected(List); break; - case MODE_BATCH: - /* this mode */ case MODE_CREATE: - case MODE_ADD: - case MODE_NONE: /* unsupported tools */ + case MODE_NONE: + /* no features selected */ + case MODE_BATCH: + /* this tool */ break; } diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index 73d9bb2fc08..4806be69755 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -519,7 +519,7 @@ int main(int argc, char *argv[]) } /* build topology only if requested or if tool!=select */ - if (action_mode != MODE_SELECT && action_mode != MODE_NONE && + if (action_mode != MODE_SELECT && action_mode != MODE_NONE && ret > 0 && params.topo->answer != 1) { Vect_build_partial(&Map, GV_BUILD_NONE); Vect_build(&Map); diff --git a/vector/v.edit/select.c b/vector/v.edit/select.c index 39cb2783455..77426a7af28 100644 --- a/vector/v.edit/select.c +++ b/vector/v.edit/select.c @@ -48,18 +48,16 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, type = selparams->type; /* select by id's */ - if (selparams->ids && *selparams->ids) { + if (selparams->ids) sel_by_id(Map, type, selparams->ids, List); - } /* select by category (ignore tools catdel and catadd) */ - if ((action_mode != MODE_CATADD && action_mode != MODE_CATDEL) && - selparams->cats && *selparams->cats) { + if (action_mode != MODE_CATADD && action_mode != MODE_CATDEL && + selparams->cats) sel_by_cat(Map, NULL, layer, type, selparams->cats, List); - } /* select by coordinates (+threshold) */ - if (selparams->coords && *selparams->coords) { + if (selparams->coords) { struct line_pnts *coords; coords = Vect_new_line_struct(); @@ -73,7 +71,7 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } /* select by bbox */ - if (selparams->bbox && *selparams->bbox) { + if (selparams->bbox) { struct line_pnts *bbox; bbox = Vect_new_line_struct(); @@ -91,7 +89,7 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } /* select by polygon */ - if (selparams->polygon && *selparams->polygon) { + if (selparams->polygon) { struct line_pnts *Polygon; Polygon = Vect_new_line_struct(); @@ -103,12 +101,11 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } /* select by where statement */ - if (selparams->where && *selparams->where) { + if (selparams->where) sel_by_where(Map, layer, type, selparams->where, List); - } /* selecy by query */ - if (selparams->query && *selparams->query) { + if (selparams->query) { int query_type; struct ilist *List_tmp; diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 6faedaa9652..f420099fb29 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -176,9 +176,10 @@

      Batch editing

      the following column names:
      • tool (required) - Tool name from the tool option; - create and add tools are not supported
      • + create tool is not supported
      • flags - -r, -1, and/or -p flags as a combination of r, 1, and p without dashes
      • +
      • input - input option; 'stdin' is not supported
      • move - move option
      • ids - ids option
      • cats - cats option
      • From 59baa262fc62849911c804841fcf4cea73011e49 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 23:03:49 -0700 Subject: [PATCH 26/45] Use MODE_BATCH --- vector/v.edit/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index 4806be69755..3ba9634d612 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -195,7 +195,7 @@ int main(int argc, char *argv[]) i++; } - if (params.batch->answer) { + if (action_mode == MODE_BATCH) { char *sep = G_option_to_separator(params.sep); if (*(sep + 1)) From 334f9c8db6670ce85ac4715c4a7ee2f6f89e4741 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 23:44:17 -0700 Subject: [PATCH 27/45] Support multiple tables separated by an empty line; Remove comments (# separator or can be included in where) --- vector/v.edit/batch.c | 20 +++++++++++--------- vector/v.edit/v.edit.html | 17 +++++++++++------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 19246f18240..89c0fb16bf2 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -84,16 +84,16 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, else if ((p = strchr(buf, '\r'))) *p = 0; - /* remove comments starting with # */ - if ((p = strchr(buf, '#'))) { - *p-- = 0; - while (p >= buf && (*p == ' ' || *p == '\t')) - *p-- = 0; - } - - /* skip empty line */ - if (!*buf) + /* find the first non-whitespace character */ + p = strchr(buf, 0); + while (--p >= buf && (*p == ' ' || *p == '\t')) + ; + + /* an empty line starts a new table */ + if (p < buf) { + first = 1; continue; + } p = buf; @@ -101,6 +101,8 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (first) { int bit_cols = 0, bit_col; + ncols = 0; + while ((p = read_column(p, sep, &pnext))) { int known_col = 0; diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index f420099fb29..24d52843773 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -194,17 +194,22 @@

        Batch editing

        The batch option is exclusive to all the above and input options. Not all the columns are required, but all necessary columns for all used tools -need to be declared even if some tools do not use certain options. In this -case, columns for these unused options can be left blank. The only required -column is tool. The order of columns is not important. +in one table need to be declared even if some tools do not use certain options. +In this case, columns for these unused options can be left blank. The only +required column is tool. The order of columns is not important. + +

        However, multiple tables with different columns can be defined in one batch +file using empty lines to separate them.

        For each line of the table, v.edit commands can be specified. For example, this table when passed to the batch option with separator=comma

        -tool,flags,cats,coords
        -break,,1,"637108.11963224,257241.783755701"
        -delete,r,1,
        +tool,cats,coords
        +break,1,"637108.11963224,257241.783755701"
        +
        +tool,flags,cats
        +delete,r,1
         
        is equivalent to the following individual commands:
        
        From f21e4659385a5b5b88f964644ad9cd836122fcc6 Mon Sep 17 00:00:00 2001
        From: Huidae Cho 
        Date: Sun, 25 Feb 2024 23:49:37 -0700
        Subject: [PATCH 28/45] Add an example for a merged table
        
        ---
         vector/v.edit/v.edit.html | 10 +++++++++-
         1 file changed, 9 insertions(+), 1 deletion(-)
        
        diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html
        index 24d52843773..214dc55ea01 100644
        --- a/vector/v.edit/v.edit.html
        +++ b/vector/v.edit/v.edit.html
        @@ -202,7 +202,7 @@ 

        Batch editing

        file using empty lines to separate them.

        For each line of the table, v.edit commands can be specified. For -example, this table when passed to the batch option with +example, these two tables when passed to the batch option with separator=comma

         tool,cats,coords
        @@ -217,6 +217,14 @@ 

        Batch editing

        v.edit -r map=vect_map tool=delete cat=1
        +

        The above two tables can be merged into one by declaring the union of two +column sets and leaving irrelevant columns empty: +

        +tool,flags,cats,coords
        +break,,1,"637108.11963224,257241.783755701"
        +delete,r,1,
        +
        +

        For batch editing, topology is always built whenever there are any changes in features because successive edits require properly built topology including feature ID changes. From f9547932184b05708906115ba50decdf036c31d1 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 23:50:29 -0700 Subject: [PATCH 29/45] Fix

        to --- vector/v.edit/v.edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 214dc55ea01..a480d279617 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -229,7 +229,7 @@

        Batch editing

        in features because successive edits require properly built topology including feature ID changes. -

        The

        separator

        option only supports a single-character separator that +

        The separator option only supports a single-character separator that is not a newline.

        EXAMPLES

        From 907171ffdfc973399aadd943a1868b06c49b5c53 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 23:58:04 -0700 Subject: [PATCH 30/45] Rewrite about multiple tables --- vector/v.edit/v.edit.html | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index a480d279617..523d6c9eef1 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -192,14 +192,13 @@

        Batch editing

      • zbulk - zbulk option
      -The batch option is exclusive to all the above and input options. -Not all the columns are required, but all necessary columns for all used tools -in one table need to be declared even if some tools do not use certain options. -In this case, columns for these unused options can be left blank. The only -required column is tool. The order of columns is not important. - -

      However, multiple tables with different columns can be defined in one batch -file using empty lines to separate them. +The batch option is exclusive to all the above options. The only +required column is tool and the order of columns is not important. +Multiple tables with different columns can be defined in one batch file using +empty lines to separate them. Different tools can be used in one table, but all +necessary columns for all used tools in that table need to be declared even if +some tools do not use certain options. In this case, columns for these unused +options can be left blank.

      For each line of the table, v.edit commands can be specified. For example, these two tables when passed to the batch option with From 17fefc7c4ffadeec415de05fae62164753411749 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Sun, 25 Feb 2024 23:59:59 -0700 Subject: [PATCH 31/45] Minor change --- vector/v.edit/v.edit.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index 523d6c9eef1..131a02d373a 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -200,7 +200,7 @@

      Batch editing

      some tools do not use certain options. In this case, columns for these unused options can be left blank. -

      For each line of the table, v.edit commands can be specified. For +

      For each line in the table, v.edit commands can be specified. For example, these two tables when passed to the batch option with separator=comma

      
      From 62676c7715ece61601bea4efe7a5bb00d0efb32d Mon Sep 17 00:00:00 2001
      From: Huidae Cho 
      Date: Mon, 26 Feb 2024 00:01:23 -0700
      Subject: [PATCH 32/45] Minor change
      
      ---
       vector/v.edit/v.edit.html | 2 +-
       1 file changed, 1 insertion(+), 1 deletion(-)
      
      diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html
      index 131a02d373a..21c054b0051 100644
      --- a/vector/v.edit/v.edit.html
      +++ b/vector/v.edit/v.edit.html
      @@ -210,7 +210,7 @@ 

      Batch editing

      tool,flags,cats delete,r,1
      -is equivalent to the following individual commands: +are equivalent to the following individual commands:
       v.edit map=vect_map tool=break cat=1 coords=637108.11963224,257241.783755701
       v.edit -r map=vect_map tool=delete cat=1
      
      From 01fdf37395c0ee51913c27f812359854ad9b7426 Mon Sep 17 00:00:00 2001
      From: Huidae Cho 
      Date: Mon, 26 Feb 2024 00:52:38 -0700
      Subject: [PATCH 33/45] Properly close map for batch editing on fatal error
      
      ---
       vector/v.edit/main.c | 16 +++++++++++++++-
       1 file changed, 15 insertions(+), 1 deletion(-)
      
      diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c
      index 3ba9634d612..534685ff065 100644
      --- a/vector/v.edit/main.c
      +++ b/vector/v.edit/main.c
      @@ -20,6 +20,8 @@
       
       #include "global.h"
       
      +static void error_handler(void *);
      +
       int main(int argc, char *argv[])
       {
           struct GModule *module;
      @@ -207,6 +209,9 @@ int main(int argc, char *argv[])
                                     params.fld->answer) < 0)
                   G_fatal_error(_("Unable to open vector map <%s>"),
                                 params.map->answer);
      +
      +        G_add_error_handler(error_handler, &Map);
      +
               selparams.layer = Vect_get_field_number(&Map, params.fld->answer);
               if (BgMap && BgMap[0])
                   selparams.bglayer =
      @@ -519,7 +524,7 @@ int main(int argc, char *argv[])
               }
       
               /* build topology only if requested or if tool!=select */
      -        if (action_mode != MODE_SELECT && action_mode != MODE_NONE && ret > 0 &&
      +        if (action_mode != MODE_SELECT && action_mode != MODE_NONE &&
                   params.topo->answer != 1) {
                   Vect_build_partial(&Map, GV_BUILD_NONE);
                   Vect_build(&Map);
      @@ -560,3 +565,12 @@ int main(int argc, char *argv[])
               exit(EXIT_FAILURE);
           }
       }
      +
      +static void error_handler(void *p)
      +{
      +    struct Map_info *Map = (struct Map_info *)p;
      +
      +    Vect_build_partial(Map, GV_BUILD_NONE);
      +    Vect_build(Map);
      +    Vect_close(Map);
      +}
      
      From 617eb97237b54b439768845c057b319d2b7bc77c Mon Sep 17 00:00:00 2001
      From: Huidae Cho 
      Date: Mon, 26 Feb 2024 01:15:09 -0700
      Subject: [PATCH 34/45] Always build topology after each batch command
      
      ---
       vector/v.edit/batch.c | 13 +++++++------
       vector/v.edit/main.c  |  2 ++
       2 files changed, 9 insertions(+), 6 deletions(-)
      
      diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c
      index 89c0fb16bf2..4ab0cdf6d0b 100644
      --- a/vector/v.edit/batch.c
      +++ b/vector/v.edit/batch.c
      @@ -462,14 +462,13 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps,
                   break;
               }
       
      -        if (action_mode != MODE_SELECT && ret) {
      -            Vect_build_partial(Map, GV_BUILD_NONE);
      -            Vect_build(Map);
      -            total_ret += ret;
      -        }
      -
               G_message(" ");
       
      +        /* level 2 vector requires topology building even if there are no
      +         * changes (e.g., select) */
      +        Vect_build_partial(Map, GV_BUILD_NONE);
      +        Vect_build(Map);
      +
               Vect_destroy_list(List);
       
               if (coord)
      @@ -477,6 +476,8 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps,
       
               if (Clist)
                   Vect_destroy_cat_list(Clist);
      +
      +        total_ret += ret;
           }
       
           if (fp != stdin)
      diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c
      index 534685ff065..f5cdab57f1d 100644
      --- a/vector/v.edit/main.c
      +++ b/vector/v.edit/main.c
      @@ -570,6 +570,8 @@ static void error_handler(void *p)
       {
           struct Map_info *Map = (struct Map_info *)p;
       
      +    /* level 2 vector requires topology building even if there are no
      +     * changes (e.g., select) */
           Vect_build_partial(Map, GV_BUILD_NONE);
           Vect_build(Map);
           Vect_close(Map);
      
      From ba22c2aa59f4efaed309b5efaeab4a4989e870c3 Mon Sep 17 00:00:00 2001
      From: Huidae Cho 
      Date: Mon, 26 Feb 2024 01:26:54 -0700
      Subject: [PATCH 35/45] Empty line
      
      ---
       vector/v.edit/main.c | 1 -
       1 file changed, 1 deletion(-)
      
      diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c
      index f5cdab57f1d..c117aa5d221 100644
      --- a/vector/v.edit/main.c
      +++ b/vector/v.edit/main.c
      @@ -209,7 +209,6 @@ int main(int argc, char *argv[])
                                     params.fld->answer) < 0)
                   G_fatal_error(_("Unable to open vector map <%s>"),
                                 params.map->answer);
      -
               G_add_error_handler(error_handler, &Map);
       
               selparams.layer = Vect_get_field_number(&Map, params.fld->answer);
      
      From 280ec935a85ede8beb985b8503d1ecb88a554002 Mon Sep 17 00:00:00 2001
      From: Huidae Cho 
      Date: Mon, 26 Feb 2024 01:42:38 -0700
      Subject: [PATCH 36/45] flags column supports all flags except -b because
       topology is always built
      
      ---
       vector/v.edit/v.edit.html | 5 +++--
       1 file changed, 3 insertions(+), 2 deletions(-)
      
      diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html
      index 21c054b0051..d56c560545e 100644
      --- a/vector/v.edit/v.edit.html
      +++ b/vector/v.edit/v.edit.html
      @@ -177,8 +177,9 @@ 

      Batch editing

      • tool (required) - Tool name from the tool option; create tool is not supported
      • -
      • flags - -r, -1, and/or -p flags as a - combination of r, 1, and p without dashes
      • +
      • flags - a combination of all flags except -b without + dashes; order is not important; e.g., r1 and 1r have the + same effect
      • input - input option; 'stdin' is not supported
      • move - move option
      • ids - ids option
      • From 4e6a1454de5eadfd8a7c7afd4c1bce06ea3dafe7 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Mon, 26 Feb 2024 13:07:31 -0700 Subject: [PATCH 37/45] Build topology only when needed --- vector/v.edit/batch.c | 11 +++++------ vector/v.edit/main.c | 7 ++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 4ab0cdf6d0b..f093029bc32 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -464,11 +464,6 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, G_message(" "); - /* level 2 vector requires topology building even if there are no - * changes (e.g., select) */ - Vect_build_partial(Map, GV_BUILD_NONE); - Vect_build(Map); - Vect_destroy_list(List); if (coord) @@ -477,7 +472,11 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (Clist) Vect_destroy_cat_list(Clist); - total_ret += ret; + if (action_mode != MODE_SELECT && ret > 0) { + Vect_build_partial(Map, GV_BUILD_NONE); + Vect_build(Map); + total_ret += ret; + } } if (fp != stdin) diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index c117aa5d221..a1c52096244 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -523,7 +523,7 @@ int main(int argc, char *argv[]) } /* build topology only if requested or if tool!=select */ - if (action_mode != MODE_SELECT && action_mode != MODE_NONE && + if (action_mode != MODE_SELECT && action_mode != MODE_NONE && ret > 0 && params.topo->answer != 1) { Vect_build_partial(&Map, GV_BUILD_NONE); Vect_build(&Map); @@ -569,9 +569,6 @@ static void error_handler(void *p) { struct Map_info *Map = (struct Map_info *)p; - /* level 2 vector requires topology building even if there are no - * changes (e.g., select) */ - Vect_build_partial(Map, GV_BUILD_NONE); - Vect_build(Map); + /* if Map is not closed on fatal error, support files won't be updated */ Vect_close(Map); } From 6906317527ce114917a6f638cd18a5238be3877a Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Mon, 26 Feb 2024 21:51:02 -0700 Subject: [PATCH 38/45] Move thresh to SelectParams --- vector/v.edit/batch.c | 10 ++++++---- vector/v.edit/global.h | 1 + vector/v.edit/main.c | 15 ++++++++------- vector/v.edit/proto.h | 6 +++--- vector/v.edit/select.c | 35 ++++++++++++++++------------------- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index f093029bc32..aa5c8289430 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -23,8 +23,7 @@ static char *read_column(char *, char, char **); static int get_snap(char *, double *thresh); int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, - const char *file, char sep, struct SelectParams *selparams, - double *thresh) + const char *file, char sep, struct SelectParams *selparams) { char *col_names[MAX_COLUMNS] = { "tool", "flags", "input", "move", "ids", "cats", "coords", @@ -50,6 +49,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, char buf[1024]; int line = 0, first = 1; int cols[MAX_COLUMNS], ncols = 0; + double *thresh = selparams->thresh; int total_ret = 0; if (strcmp(file, "-") != 0) { @@ -215,9 +215,11 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, selparams->reverse = GET_FLAG(flags, 'r'); if (action_mode == MODE_COPY && BgMap && BgMap[0]) - List = select_lines(BgMap[0], action_mode, selparams, thresh, List); + List = select_lines(BgMap[0], selparams->bglayer, action_mode, + selparams, List); else if (action_mode != MODE_ADD) - List = select_lines(Map, action_mode, selparams, thresh, List); + List = select_lines(Map, selparams->layer, action_mode, selparams, + List); if (action_mode != MODE_ADD && action_mode != MODE_SELECT && !List->n_values) { diff --git a/vector/v.edit/global.h b/vector/v.edit/global.h index 86bd73603e9..d9edff5ddf0 100644 --- a/vector/v.edit/global.h +++ b/vector/v.edit/global.h @@ -58,6 +58,7 @@ struct GParams { struct SelectParams { int layer, bglayer, type, reverse; char *ids, *cats, *coords, *bbox, *polygon, *where, *query; + double *thresh; }; #include "proto.h" diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index a1c52096244..4e0916c5430 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -217,8 +217,8 @@ int main(int argc, char *argv[]) Vect_get_field_number(BgMap[0], params.fld->answer); selparams.type = Vect_option_to_types(params.type); - batch_edit(&Map, BgMap, nbgmaps, params.batch->answer, *sep, &selparams, - thresh); + batch_edit(&Map, BgMap, nbgmaps, params.batch->answer, *sep, + &selparams); } else { /* get list of categories */ @@ -246,7 +246,7 @@ int main(int argc, char *argv[]) if (action_mode != MODE_CREATE && action_mode != MODE_ADD) { /* select lines */ if (action_mode == MODE_COPY && BgMap && BgMap[0]) - selparams.layer = + selparams.bglayer = Vect_get_field_number(BgMap[0], params.fld->answer); else selparams.layer = @@ -261,15 +261,16 @@ int main(int argc, char *argv[]) selparams.polygon = params.poly->answer; selparams.where = params.where->answer; selparams.query = params.query->answer; + selparams.thresh = thresh; List = Vect_new_list(); if (action_mode == MODE_COPY && BgMap && BgMap[0]) { - List = select_lines(BgMap[0], action_mode, &selparams, thresh, - List); + List = select_lines(BgMap[0], selparams.bglayer, action_mode, + &selparams, List); } else { - List = - select_lines(&Map, action_mode, &selparams, thresh, List); + List = select_lines(&Map, selparams.layer, action_mode, + &selparams, List); } } diff --git a/vector/v.edit/proto.h b/vector/v.edit/proto.h index 1cf821ffe68..2bd923ebc0f 100644 --- a/vector/v.edit/proto.h +++ b/vector/v.edit/proto.h @@ -9,8 +9,8 @@ int close_lines(struct Map_info *, int, double); /* select.c */ int print_selected(struct ilist *); -struct ilist *select_lines(struct Map_info *, enum mode, struct SelectParams *, - double *, struct ilist *); +struct ilist *select_lines(struct Map_info *, int, enum mode, + struct SelectParams *, struct ilist *); int sel_by_cat(struct Map_info *, struct cat_list *, int, int, char *, struct ilist *); int sel_by_coordinates(struct Map_info *, int, struct line_pnts *, double, @@ -37,6 +37,6 @@ void coord2bbox(double, double, double, struct line_pnts *); /* batch.c */ int batch_edit(struct Map_info *, struct Map_info **, int, const char *, char, - struct SelectParams *, double *); + struct SelectParams *); #endif /* _V_EDIT_PROTO */ diff --git a/vector/v.edit/select.c b/vector/v.edit/select.c index 77426a7af28..d55ec58eedc 100644 --- a/vector/v.edit/select.c +++ b/vector/v.edit/select.c @@ -7,7 +7,7 @@ * AUTHOR(S): GRASS Development Team * Wolf Bergenheim, Jachym Cepicky, Martin Landa * - * COPYRIGHT: (C) 2006-2008 by the GRASS Development Team + * COPYRIGHT: (C) 2006-2024 by the GRASS Development Team * * This program is free software under the * GNU General Public License (>=v2). @@ -28,23 +28,23 @@ static int merge_lists2(struct ilist *, struct boxlist *); \brief Select vector features \param[in] Map vector map + \param[in] layer map layer \param[in] action_mode tool - \param[in] params GRASS parameters + \param[in] selparams select parameters \param[in] List list of selected features \return list of newly selected features */ -struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, - struct SelectParams *selparams, double *thresh, - struct ilist *List) +struct ilist *select_lines(struct Map_info *Map, int layer, + enum mode action_mode, + struct SelectParams *selparams, struct ilist *List) { - int layer, type; + int type; G_message(_("Selecting features...")); first_selection = 1; - layer = selparams->layer; type = selparams->type; /* select by id's */ @@ -64,8 +64,9 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, str_to_coordinates(selparams->coords, coords); G_verbose_message(_("Threshold value for coordinates is %.2f"), - thresh[THRESH_COORDS]); - sel_by_coordinates(Map, type, coords, thresh[THRESH_COORDS], List); + selparams->thresh[THRESH_COORDS]); + sel_by_coordinates(Map, type, coords, selparams->thresh[THRESH_COORDS], + List); Vect_destroy_line_struct(coords); } @@ -113,21 +114,18 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, List_tmp = List; first_selection = 0; } - else { + else List_tmp = Vect_new_list(); - } query_type = QUERY_UNKNOWN; - if (strcmp(selparams->query, "length") == 0) { + if (strcmp(selparams->query, "length") == 0) query_type = QUERY_LENGTH; - } - else if (strcmp(selparams->query, "dangle") == 0) { + else if (strcmp(selparams->query, "dangle") == 0) query_type = QUERY_DANGLE; - } G_verbose_message(_("Threshold value for querying is %.2f"), - thresh[THRESH_QUERY]); - Vedit_select_by_query(Map, type, layer, thresh[THRESH_QUERY], + selparams->thresh[THRESH_QUERY]); + Vedit_select_by_query(Map, type, layer, selparams->thresh[THRESH_QUERY], query_type, List_tmp); /* merge lists (only duplicate items) */ @@ -137,9 +135,8 @@ struct ilist *select_lines(struct Map_info *Map, enum mode action_mode, } } - if (selparams->reverse) { + if (selparams->reverse) reverse_selection(Map, type, &List); - } G_message(n_("%d of %d feature selected from vector map <%s>", "%d of %d features selected from vector map <%s>", From cd272ceaa2b3ec29d7dc576ccd5bec671a0d7280 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Tue, 27 Feb 2024 00:31:44 -0700 Subject: [PATCH 39/45] Move duplicate editing code to edit.c --- vector/v.edit/batch.c | 348 +++++++---------------------------------- vector/v.edit/edit.c | 267 +++++++++++++++++++++++++++++++ vector/v.edit/global.h | 7 + vector/v.edit/main.c | 336 +++++---------------------------------- vector/v.edit/proto.h | 4 + vector/v.edit/select.c | 14 +- 6 files changed, 376 insertions(+), 600 deletions(-) create mode 100644 vector/v.edit/edit.c diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index aa5c8289430..5f31cae6409 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -1,26 +1,25 @@ #include "global.h" -#define GET_FLAG(flags, flag) ((flags) && strchr(flags, flag)) - -#define MAX_COLUMNS 13 -#define NUM_TOOLS 21 - -#define COLUMN_TOOL 0 -#define COLUMN_FLAGS 1 -#define COLUMN_INPUT 2 -#define COLUMN_MOVE 3 -#define COLUMN_IDS 4 -#define COLUMN_CATS 5 -#define COLUMN_COORDS 6 -#define COLUMN_BBOX 7 -#define COLUMN_POLYGON 8 -#define COLUMN_WHERE 9 -#define COLUMN_QUERY 10 -#define COLUMN_SNAP 11 -#define COLUMN_ZBULK 12 +#define GET_FLAG(flag) (flags && strchr(flags, flag)) + +#define MAX_COLUMNS 13 +#define NUM_TOOLS 21 + +#define COLUMN_TOOL 0 +#define COLUMN_FLAGS 1 +#define COLUMN_INPUT 2 +#define COLUMN_MOVE 3 +#define COLUMN_IDS 4 +#define COLUMN_CATS 5 +#define COLUMN_COORDS 6 +#define COLUMN_BBOX 7 +#define COLUMN_POLYGON 8 +#define COLUMN_WHERE 9 +#define COLUMN_QUERY 10 +#define COLUMN_SNAP 11 +#define COLUMN_ZBULK 12 static char *read_column(char *, char, char **); -static int get_snap(char *, double *thresh); int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, const char *file, char sep, struct SelectParams *selparams) @@ -50,8 +49,11 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, int line = 0, first = 1; int cols[MAX_COLUMNS], ncols = 0; double *thresh = selparams->thresh; + struct EditParams editparams; int total_ret = 0; + editparams.thresh = thresh; + if (strcmp(file, "-") != 0) { if (!(fp = fopen(file, "r"))) G_fatal_error(_("Unable to open file <%s>"), file); @@ -61,11 +63,9 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, while (fgets(buf, 1024, fp)) { char *p, *pnext; + char *flags, *input; enum mode action_mode; - char *flags, *input, *move, *snap, *zbulk; struct ilist *List; - struct line_pnts *coord = NULL; - struct cat_list *Clist = NULL; int ret = 0; int i; @@ -73,7 +73,10 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, selparams->ids = selparams->cats = selparams->coords = selparams->bbox = selparams->polygon = selparams->where = selparams->query = NULL; - flags = input = move = snap = zbulk = NULL; + editparams.input = NULL; + editparams.move = editparams.cats = editparams.coords = + editparams.snap = editparams.zbulk = editparams.bbox = NULL; + flags = input = NULL; /* remove newline */ if ((p = strchr(buf, '\n'))) { @@ -167,19 +170,19 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, input = p; break; case COLUMN_MOVE: - move = p; + editparams.move = p; break; case COLUMN_IDS: selparams->ids = p; break; case COLUMN_CATS: - selparams->cats = p; + editparams.cats = selparams->cats = p; break; case COLUMN_COORDS: - selparams->coords = p; + editparams.coords = selparams->coords = p; break; case COLUMN_BBOX: - selparams->bbox = p; + editparams.bbox = selparams->bbox = p; break; case COLUMN_POLYGON: selparams->polygon = p; @@ -191,10 +194,10 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, selparams->query = p; break; case COLUMN_SNAP: - snap = p; + editparams.snap = p; break; case COLUMN_ZBULK: - zbulk = p; + editparams.zbulk = p; break; } } @@ -211,9 +214,18 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, if (i < ncols) G_fatal_error(_("Too few batch columns in line %d"), line); + editparams.close = action_mode == MODE_ADD && GET_FLAG('c'); + editparams.header = action_mode == MODE_ADD && GET_FLAG('n'); + editparams.move_first = + action_mode == MODE_VERTEX_MOVE && GET_FLAG('1'); + editparams.extend_parallel = + (action_mode == MODE_EXTEND || action_mode == MODE_EXTEND_START || + action_mode == MODE_EXTEND_END) && + GET_FLAG('p'); + List = Vect_new_list(); - selparams->reverse = GET_FLAG(flags, 'r'); + selparams->reverse = GET_FLAG('r'); if (action_mode == MODE_COPY && BgMap && BgMap[0]) List = select_lines(BgMap[0], selparams->bglayer, action_mode, selparams, List); @@ -222,258 +234,29 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, List); if (action_mode != MODE_ADD && action_mode != MODE_SELECT && - !List->n_values) { + List->n_values < 1) G_warning(_("No features selected, nothing to edit")); - action_mode = MODE_NONE; - ret = 0; - } - - if ((action_mode == MODE_CATADD || action_mode == MODE_CATDEL) && - selparams->cats) { - Clist = Vect_new_cat_list(); - if (Vect_str_to_cat_list(selparams->cats, Clist)) - G_fatal_error(_("Unable to get category list <%s>"), - selparams->cats); - } - - if (selparams->coords) { - coord = Vect_new_line_struct(); - str_to_coordinates(selparams->coords, coord); - } - - switch (action_mode) { - case MODE_ADD: { - FILE *ascii; - int num_lines; - int snap_mode = get_snap(snap, thresh); - - if (!input) - G_fatal_error(_("Input file not specified in line %d"), line); - - if (!(ascii = fopen(input, "r"))) - G_fatal_error(_("Unable to open file <%s>"), input); - - if (!GET_FLAG(flags, 'n')) - Vect_read_ascii_head(ascii, Map); - - num_lines = Vect_get_num_lines(Map); - ret = Vect_read_ascii(ascii, Map); - - fclose(ascii); - - if (ret > 0) { - int iline; - struct ilist *List_added; - - G_message(n_("%d feature added", "%d features added", ret), - ret); - - 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_mode != NO_SNAP) { /* apply snapping */ - /* snap to vertex ? */ - Vedit_snap_lines(Map, BgMap, nbgmaps, List_added, - thresh[THRESH_SNAP], - snap_mode == SNAP ? FALSE : TRUE); - } - if (GET_FLAG(flags, 'c')) { /* close boundaries */ - int nclosed = - close_lines(Map, GV_BOUNDARY, thresh[THRESH_SNAP]); - G_message(n_("%d boundary closed", "%d boundaries closed", - nclosed), - nclosed); - } - Vect_destroy_list(List_added); - } - break; - } - case MODE_DEL: - ret = Vedit_delete_lines(Map, List); - G_message(n_("%d feature deleted", "%d features deleted", ret), - 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(n_("%d feature copied", "%d features copied", ret), ret); - break; - case MODE_MOVE: { - double move_x, move_y, move_z; - - if (sscanf(p, "%lf,%lf,%lf", &move_x, &move_y, &move_z) != 3) - G_fatal_error(_("'%s' tool must have '%s' column"), "move", - "move"); - 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, get_snap(snap, thresh), - thresh[THRESH_SNAP]); - G_message(n_("%d feature moved", "%d features moved", ret), ret); - break; - } - case MODE_FLIP: - ret = Vedit_flip_lines(Map, List); - G_message(n_("%d line flipped", "%d lines flipped", ret), ret); - break; - case MODE_CATADD: - ret = Vedit_modify_cats(Map, List, selparams->layer, 0, Clist); - G_message(n_("%d feature modified", "%d features modified", ret), - ret); - break; - case MODE_CATDEL: - ret = Vedit_modify_cats(Map, List, selparams->layer, 1, Clist); - G_message(n_("%d feature modified", "%d features modified", ret), - ret); - break; - case MODE_MERGE: - ret = Vedit_merge_lines(Map, List); - G_message(n_("%d line merged", "%d lines merged", ret), ret); - break; - case MODE_BREAK: - if (coord) - 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(n_("%d line broken", "%d lines broken", ret), 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_CONNECT: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_connect_lines(Map, List, thresh[THRESH_SNAP]); - G_message(n_("%d line connected", "%d lines connected", ret), ret); - break; - case MODE_EXTEND: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(Map, List, 0, GET_FLAG(flags, 'p'), - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_EXTEND_START: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(Map, List, 1, GET_FLAG(flags, 'p'), - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_EXTEND_END: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(Map, List, 2, GET_FLAG(flags, 'p'), - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_CHTYPE: - if ((ret = Vedit_chtype_lines(Map, List)) > 0) - G_message( - n_("%d feature converted", "%d features converted", ret), - ret); - else - G_message(_("No feature modified")); - break; - case MODE_VERTEX_ADD: - ret = Vedit_add_vertex(Map, List, coord, thresh[THRESH_COORDS]); - G_message(n_("%d vertex added", "%d vertices added", ret), ret); - - break; - case MODE_VERTEX_DELETE: - ret = Vedit_remove_vertex(Map, List, coord, thresh[THRESH_COORDS]); - G_message(n_("%d vertex removed", "%d vertices removed", ret), ret); - break; - case MODE_VERTEX_MOVE: { - double move_x, move_y, move_z; - - if (sscanf(p, "%lf,%lf,%lf", &move_x, &move_y, &move_z) != 3) - G_fatal_error(_("'%s' tool must have '%s' column"), - "vertexmove", "move"); - 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, - GET_FLAG(flags, '1'), get_snap(snap, thresh)); - G_message(n_("%d vertex moved", "%d vertices moved", ret), ret); - break; - } - case MODE_AREA_DEL: - for (i = 0; i < List->n_values; i++) { - if (Vect_get_line_type(Map, List->value[i]) != GV_CENTROID) { - G_warning( - _("Select feature %d is not centroid, ignoring..."), - List->value[i]); - continue; - } - - ret += Vedit_delete_area_centroid(Map, List->value[i]); - } - G_message(n_("%d area removed", "%d areas removed", ret), ret); - break; - case MODE_ZBULK: { - double start, step; - double x1, y1, x2, y2; - - if (!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."), - Map->name, "zbulk", "v.extrude"); + else { + if (action_mode == MODE_ADD) { + if (!input) + G_fatal_error(_("'%s' tool must have '%s' column"), "add", + "input"); + + if (!(editparams.input = fopen(input, "r"))) + G_fatal_error(_("Unable to open file <%s>"), input); } - if (sscanf(zbulk, "%lf,%lf", &start, &step) != 2) - G_fatal_error(_("'%s' tool must have '%s' column"), "zbulk", - "zbulk"); + ret = edit(Map, selparams->layer, BgMap, nbgmaps, List, action_mode, + &editparams, line); - if (!selparams->bbox || sscanf(selparams->bbox, "%lf,%lf,%lf,%lf", - &x1, &y1, &x2, &y2) != 4) - G_fatal_error(_("ZBulk must have bbox")); - - ret = Vedit_bulk_labeling(Map, List, x1, y1, x2, y2, start, step); - G_message(n_("%d line labeled", "%d lines labeled", ret), ret); - break; - } - case MODE_SELECT: - ret = print_selected(List); - break; - case MODE_CREATE: - /* unsupported tools */ - case MODE_NONE: - /* no features selected */ - case MODE_BATCH: - /* this tool */ - break; + if (action_mode == MODE_ADD && editparams.input) + fclose(editparams.input); } G_message(" "); Vect_destroy_list(List); - if (coord) - Vect_destroy_line_struct(coord); - - if (Clist) - Vect_destroy_cat_list(Clist); - if (action_mode != MODE_SELECT && ret > 0) { Vect_build_partial(Map, GV_BUILD_NONE); Vect_build(Map); @@ -550,24 +333,3 @@ static char *read_column(char *pcol, char sep, char **pnext) return pcol; } - -static int get_snap(char *snap, double *thresh) -{ - int snap_mode = NO_SNAP; - - if (snap) { - if (strcmp(snap, "node") == 0) - snap_mode = SNAP; - else if (strcmp(snap, "vertex") == 0) - snap_mode = SNAPVERTEX; - else if (strcmp(snap, "no") != 0) - G_fatal_error(_("Unsupported snap '%s'"), snap); - if (snap_mode != NO_SNAP && thresh[THRESH_SNAP] <= 0) { - G_warning( - _("Threshold for snapping must be > 0. No snapping applied.")); - snap_mode = NO_SNAP; - } - } - - return snap_mode; -} diff --git a/vector/v.edit/edit.c b/vector/v.edit/edit.c new file mode 100644 index 00000000000..cb78c4a8236 --- /dev/null +++ b/vector/v.edit/edit.c @@ -0,0 +1,267 @@ +#include "global.h" + +static int get_snap(char *, double *thresh); + +int edit(struct Map_info *Map, int layer, struct Map_info **BgMap, int nbgmaps, + struct ilist *List, enum mode action_mode, + struct EditParams *editparams, int line) +{ + double *thresh = editparams->thresh; + struct cat_list *Clist = NULL; + struct line_pnts *coord = NULL; + int ret = 0; + + if (action_mode == MODE_CATADD || action_mode == MODE_CATDEL) { + Clist = Vect_new_cat_list(); + if (editparams->cats && Vect_str_to_cat_list(editparams->cats, Clist)) + G_fatal_error(_("Unable to get category list <%s>"), + editparams->cats); + } + + if ((action_mode == MODE_BREAK || action_mode == MODE_VERTEX_ADD || + action_mode == MODE_VERTEX_DELETE || + action_mode == MODE_VERTEX_MOVE) && + editparams->coords) { + coord = Vect_new_line_struct(); + str_to_coordinates(editparams->coords, coord); + } + + switch (action_mode) { + case MODE_ADD: { + int num_lines; + int snap_mode = get_snap(editparams->snap, thresh); + + if (!editparams->header) + Vect_read_ascii_head(editparams->input, Map); + + num_lines = Vect_get_num_lines(Map); + ret = Vect_read_ascii(editparams->input, Map); + + if (ret > 0) { + int iline; + struct ilist *List_added; + + G_message(n_("%d feature added", "%d features added", ret), ret); + + 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_mode != NO_SNAP) { /* apply snapping */ + /* snap to vertex ? */ + Vedit_snap_lines(Map, BgMap, nbgmaps, List_added, + thresh[THRESH_SNAP], + snap_mode == SNAP ? FALSE : TRUE); + } + if (editparams->close) { /* close boundaries */ + int nclosed = + close_lines(Map, GV_BOUNDARY, thresh[THRESH_SNAP]); + G_message( + n_("%d boundary closed", "%d boundaries closed", nclosed), + nclosed); + } + Vect_destroy_list(List_added); + } + break; + } + case MODE_DEL: + ret = Vedit_delete_lines(Map, List); + G_message(n_("%d feature deleted", "%d features deleted", ret), 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(n_("%d feature copied", "%d features copied", ret), ret); + break; + case MODE_MOVE: { + double move_x, move_y, move_z; + + if (sscanf(editparams->move, "%lf,%lf,%lf", &move_x, &move_y, + &move_z) != 3) + G_fatal_error(_("'%s' tool must have '%s' column"), "move", "move"); + 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, get_snap(editparams->snap, thresh), + thresh[THRESH_SNAP]); + G_message(n_("%d feature moved", "%d features moved", ret), ret); + break; + } + case MODE_FLIP: + ret = Vedit_flip_lines(Map, List); + G_message(n_("%d line flipped", "%d lines flipped", ret), ret); + break; + case MODE_CATADD: + ret = Vedit_modify_cats(Map, List, layer, 0, Clist); + G_message(n_("%d feature modified", "%d features modified", ret), ret); + break; + case MODE_CATDEL: + ret = Vedit_modify_cats(Map, List, layer, 1, Clist); + G_message(n_("%d feature modified", "%d features modified", ret), ret); + break; + case MODE_MERGE: + ret = Vedit_merge_lines(Map, List); + G_message(n_("%d line merged", "%d lines merged", ret), ret); + break; + case MODE_BREAK: + if (coord) + 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(n_("%d line broken", "%d lines broken", ret), 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_CONNECT: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_connect_lines(Map, List, thresh[THRESH_SNAP]); + G_message(n_("%d line connected", "%d lines connected", ret), ret); + break; + case MODE_EXTEND: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(Map, List, 0, editparams->extend_parallel, + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_EXTEND_START: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(Map, List, 1, editparams->extend_parallel, + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_EXTEND_END: + G_verbose_message(_("Threshold value for snapping is %.2f"), + thresh[THRESH_SNAP]); + ret = Vedit_extend_lines(Map, List, 2, editparams->extend_parallel, + thresh[THRESH_SNAP]); + G_message(n_("%d line extended", "%d lines extended", ret), ret); + break; + case MODE_CHTYPE: + if ((ret = Vedit_chtype_lines(Map, List)) > 0) + G_message(n_("%d feature converted", "%d features converted", ret), + ret); + else + G_message(_("No feature modified")); + break; + case MODE_VERTEX_ADD: + ret = Vedit_add_vertex(Map, List, coord, thresh[THRESH_COORDS]); + G_message(n_("%d vertex added", "%d vertices added", ret), ret); + + break; + case MODE_VERTEX_DELETE: + ret = Vedit_remove_vertex(Map, List, coord, thresh[THRESH_COORDS]); + G_message(n_("%d vertex removed", "%d vertices removed", ret), ret); + break; + case MODE_VERTEX_MOVE: { + double move_x, move_y, move_z; + + if (sscanf(editparams->move, "%lf,%lf,%lf", &move_x, &move_y, + &move_z) != 3) + G_fatal_error(_("'%s' tool must have '%s' column"), "vertexmove", + "move"); + 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, editparams->move_first, + get_snap(editparams->snap, thresh)); + G_message(n_("%d vertex moved", "%d vertices moved", ret), ret); + break; + } + case MODE_AREA_DEL: { + int i; + for (i = 0; i < List->n_values; i++) { + if (Vect_get_line_type(Map, List->value[i]) != GV_CENTROID) { + G_warning(_("Select feature %d is not centroid, ignoring..."), + List->value[i]); + continue; + } + + ret += Vedit_delete_area_centroid(Map, List->value[i]); + } + G_message(n_("%d area removed", "%d areas removed", ret), ret); + break; + } + case MODE_ZBULK: { + double start, step; + double x1, y1, x2, y2; + + /* in batch editing (line > 0), check if Map is 3D; in non-batch + * editing (line == 0), this check is done earlier in main.c */ + if (line && !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."), + Map->name, "zbulk", "v.extrude"); + } + + if (sscanf(editparams->zbulk, "%lf,%lf", &start, &step) != 2) + G_fatal_error(_("'%s' tool must have '%s' column"), "zbulk", + "zbulk"); + + if (!editparams->bbox || sscanf(editparams->bbox, "%lf,%lf,%lf,%lf", + &x1, &y1, &x2, &y2) != 4) + G_fatal_error(_("ZBulk must have bbox")); + + ret = Vedit_bulk_labeling(Map, List, x1, y1, x2, y2, start, step); + G_message(n_("%d line labeled", "%d lines labeled", ret), ret); + break; + } + case MODE_SELECT: + ret = print_selected(List); + break; + case MODE_CREATE: + case MODE_NONE: + case MODE_BATCH: + break; + } + + if (Clist) + Vect_destroy_cat_list(Clist); + + if (coord) + Vect_destroy_line_struct(coord); + + return ret; +} + +static int get_snap(char *snap, double *thresh) +{ + int snap_mode = NO_SNAP; + + if (snap) { + if (strcmp(snap, "node") == 0) + snap_mode = SNAP; + else if (strcmp(snap, "vertex") == 0) + snap_mode = SNAPVERTEX; + else if (strcmp(snap, "no") != 0) + G_fatal_error(_("Unsupported snap '%s'"), snap); + if (snap_mode != NO_SNAP && thresh[THRESH_SNAP] <= 0) { + G_warning( + _("Threshold for snapping must be > 0. No snapping applied.")); + snap_mode = NO_SNAP; + } + } + + return snap_mode; +} diff --git a/vector/v.edit/global.h b/vector/v.edit/global.h index d9edff5ddf0..a89049f858d 100644 --- a/vector/v.edit/global.h +++ b/vector/v.edit/global.h @@ -61,6 +61,13 @@ struct SelectParams { double *thresh; }; +struct EditParams { + FILE *input; + char *move, *cats, *coords, *snap, *zbulk, *bbox; + double *thresh; + int close, header, move_first, extend_parallel; +}; + #include "proto.h" #endif diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index 4e0916c5430..7011a6dd294 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -20,8 +20,6 @@ #include "global.h" -static void error_handler(void *); - int main(int argc, char *argv[]) { struct GModule *module; @@ -32,24 +30,16 @@ int main(int argc, char *argv[]) enum mode action_mode; FILE *ascii; struct SelectParams selparams; - + struct EditParams editparams; int i; - int move_first, snap, extend_parallel; - int ret, layer; - double move_x, move_y, move_z, thresh[3]; - - struct line_pnts *coord; - + int ret; + double thresh[3]; struct ilist *List; - struct cat_list *Clist; - ascii = NULL; List = NULL; BgMap = NULL; nbgmaps = 0; - coord = NULL; - Clist = NULL; G_gisinit(argv[0]); @@ -77,9 +67,8 @@ int main(int argc, char *argv[]) if (ascii == NULL) G_fatal_error(_("Unable to open file <%s>"), params.in->answer); } - else { + else ascii = stdin; - } } if (!ascii && action_mode == MODE_ADD) G_fatal_error(_("Required parameter <%s> not set"), params.in->key); @@ -99,10 +88,9 @@ int main(int argc, char *argv[]) /* 3D vector maps? */ putenv("GRASS_VECTOR_EXTERNAL_IMMEDIATE=1"); ret = Vect_open_new(&Map, params.map->answer, WITHOUT_Z); - if (ret == -1) { + if (ret == -1) G_fatal_error(_("Unable to create vector map <%s>"), params.map->answer); - } Vect_set_error_handler_io(NULL, &Map); /* native or external data source ? */ @@ -123,10 +111,8 @@ int main(int argc, char *argv[]) G_debug(1, "Map created"); - if (ascii) { - /* also add new vector features */ + if (ascii) /* also add new vector features */ action_mode = MODE_ADD; - } } else { /* open selected vector file */ if (action_mode == MODE_ADD) /* write */ @@ -175,7 +161,6 @@ int main(int argc, char *argv[]) } } - layer = Vect_get_field_number(&Map, params.fld->answer); i = 0; while (params.maxdist->answers[i]) { switch (i) { @@ -209,40 +194,19 @@ int main(int argc, char *argv[]) params.fld->answer) < 0) G_fatal_error(_("Unable to open vector map <%s>"), params.map->answer); - G_add_error_handler(error_handler, &Map); + Vect_set_error_handler_io(&Map, NULL); selparams.layer = Vect_get_field_number(&Map, params.fld->answer); if (BgMap && BgMap[0]) selparams.bglayer = Vect_get_field_number(BgMap[0], params.fld->answer); selparams.type = Vect_option_to_types(params.type); + selparams.thresh = thresh; batch_edit(&Map, BgMap, nbgmaps, params.batch->answer, *sep, &selparams); } else { - /* get list of categories */ - if (action_mode == MODE_CATADD || action_mode == MODE_CATDEL) { - 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); - } - - move_first = params.move_first->answer ? 1 : 0; - extend_parallel = params.extend_parallel->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 */ if (action_mode == MODE_COPY && BgMap && BgMap[0]) @@ -253,7 +217,6 @@ int main(int argc, char *argv[]) Vect_get_field_number(&Map, params.fld->answer); selparams.type = Vect_option_to_types(params.type); selparams.reverse = params.reverse->answer; - selparams.ids = params.id->answer; selparams.cats = params.cat->answer; selparams.coords = params.coord->answer; @@ -264,14 +227,12 @@ int main(int argc, char *argv[]) selparams.thresh = thresh; List = Vect_new_list(); - if (action_mode == MODE_COPY && BgMap && BgMap[0]) { + if (action_mode == MODE_COPY && BgMap && BgMap[0]) List = select_lines(BgMap[0], selparams.bglayer, action_mode, &selparams, List); - } - else { + else List = select_lines(&Map, selparams.layer, action_mode, &selparams, List); - } } if ((action_mode != MODE_CREATE && action_mode != MODE_ADD && @@ -301,244 +262,40 @@ int main(int argc, char *argv[]) } } - /* 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); - if (ret > 0) { - int iline; - struct ilist *List_added; - - G_message(n_("%d feature added", "%d features added", ret), - ret); - - 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(n_("%d boundary closed", "%d boundaries closed", - nclosed), - nclosed); - } - Vect_destroy_list(List_added); - } - break; - case MODE_DEL: - ret = Vedit_delete_lines(&Map, List); - G_message(n_("%d feature deleted", "%d features deleted", ret), - 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(n_("%d feature moved", "%d features moved", ret), 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(n_("%d vertex moved", "%d vertices moved", ret), ret); - break; - case MODE_VERTEX_ADD: - ret = Vedit_add_vertex(&Map, List, coord, thresh[THRESH_COORDS]); - G_message(n_("%d vertex added", "%d vertices added", ret), ret); - break; - case MODE_VERTEX_DELETE: - ret = Vedit_remove_vertex(&Map, List, coord, thresh[THRESH_COORDS]); - G_message(n_("%d vertex removed", "%d vertices removed", ret), 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(n_("%d line broken", "%d lines broken", ret), 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(n_("%d line connected", "%d lines connected", ret), ret); - break; - case MODE_EXTEND: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(&Map, List, 0, extend_parallel, - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_EXTEND_START: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(&Map, List, 1, extend_parallel, - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_EXTEND_END: - G_verbose_message(_("Threshold value for snapping is %.2f"), - thresh[THRESH_SNAP]); - ret = Vedit_extend_lines(&Map, List, 2, extend_parallel, - thresh[THRESH_SNAP]); - G_message(n_("%d line extended", "%d lines extended", ret), ret); - break; - case MODE_MERGE: - ret = Vedit_merge_lines(&Map, List); - G_message(n_("%d line merged", "%d lines merged", ret), ret); - break; - case MODE_SELECT: - ret = print_selected(List); - break; - case MODE_CATADD: - ret = Vedit_modify_cats(&Map, List, layer, 0, Clist); - G_message(n_("%d feature modified", "%d features modified", ret), - ret); - break; - case MODE_CATDEL: - ret = Vedit_modify_cats(&Map, List, layer, 1, Clist); - G_message(n_("%d feature modified", "%d features modified", ret), - 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(n_("%d feature copied", "%d features copied", ret), 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(n_("%d line flipped", "%d lines flipped", ret), 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(n_("%d line labeled", "%d lines labeled", ret), ret); - break; - } - case MODE_CHTYPE: - ret = Vedit_chtype_lines(&Map, List); - - if (ret > 0) { - G_message( - n_("%d feature converted", "%d features converted", ret), - ret); + if (action_mode != MODE_NONE) { + editparams.input = ascii; + editparams.move = params.move->answer; + editparams.cats = params.cat->answer; + editparams.coords = params.coord->answer; + editparams.snap = params.snap->answer; + editparams.zbulk = params.zbulk->answer; + editparams.bbox = params.bbox->answer; + editparams.thresh = thresh; + editparams.close = params.close->answer ? 1 : 0; + editparams.header = params.header->answer ? 1 : 0; + editparams.move_first = params.move_first->answer ? 1 : 0; + editparams.extend_parallel = params.extend_parallel->answer ? 1 : 0; + + /* perform requested editation */ + ret = edit(&Map, selparams.layer, BgMap, nbgmaps, List, action_mode, + &editparams, 0); + + /* build topology only if requested or if tool!=select */ + if (action_mode != MODE_SELECT && ret > 0 && + params.topo->answer != 1) { + Vect_build_partial(&Map, GV_BUILD_NONE); + Vect_build(&Map); } - else { - G_message(_("No feature modified")); - } - break; - case MODE_AREA_DEL: { - ret = 0; - for (i = 0; i < List->n_values; i++) { - if (Vect_get_line_type(&Map, List->value[i]) != GV_CENTROID) { - G_warning( - _("Select feature %d is not centroid, ignoring..."), - List->value[i]); - continue; - } - - ret += Vedit_delete_area_centroid(&Map, List->value[i]); - } - G_message(n_("%d area removed", "%d areas removed", ret), ret); - break; - } - default: - G_warning(_("Operation not implemented")); - ret = -1; - break; - } - - /* build topology only if requested or if tool!=select */ - if (action_mode != MODE_SELECT && action_mode != MODE_NONE && ret > 0 && - params.topo->answer != 1) { - Vect_build_partial(&Map, GV_BUILD_NONE); - Vect_build(&Map); } } + if (List) + Vect_destroy_list(List); + if (ascii && ascii != stdout) fclose(ascii); Vect_hist_command(&Map); - - if (List) - Vect_destroy_list(List); - Vect_close(&Map); G_debug(1, "Map closed"); @@ -550,26 +307,7 @@ int main(int argc, char *argv[]) } 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); - } -} - -static void error_handler(void *p) -{ - struct Map_info *Map = (struct Map_info *)p; - - /* if Map is not closed on fatal error, support files won't be updated */ - Vect_close(Map); + exit(ret > -1 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/vector/v.edit/proto.h b/vector/v.edit/proto.h index 2bd923ebc0f..4e0d0e35d66 100644 --- a/vector/v.edit/proto.h +++ b/vector/v.edit/proto.h @@ -39,4 +39,8 @@ void coord2bbox(double, double, double, struct line_pnts *); int batch_edit(struct Map_info *, struct Map_info **, int, const char *, char, struct SelectParams *); +/* edit.c */ +int edit(struct Map_info *, int, struct Map_info **, int, struct ilist *, + enum mode, struct EditParams *, int); + #endif /* _V_EDIT_PROTO */ diff --git a/vector/v.edit/select.c b/vector/v.edit/select.c index d55ec58eedc..4cafbff12b5 100644 --- a/vector/v.edit/select.c +++ b/vector/v.edit/select.c @@ -39,14 +39,13 @@ struct ilist *select_lines(struct Map_info *Map, int layer, enum mode action_mode, struct SelectParams *selparams, struct ilist *List) { - int type; + int type = selparams->type; + double *thresh = selparams->thresh; G_message(_("Selecting features...")); first_selection = 1; - type = selparams->type; - /* select by id's */ if (selparams->ids) sel_by_id(Map, type, selparams->ids, List); @@ -64,9 +63,8 @@ struct ilist *select_lines(struct Map_info *Map, int layer, str_to_coordinates(selparams->coords, coords); G_verbose_message(_("Threshold value for coordinates is %.2f"), - selparams->thresh[THRESH_COORDS]); - sel_by_coordinates(Map, type, coords, selparams->thresh[THRESH_COORDS], - List); + thresh[THRESH_COORDS]); + sel_by_coordinates(Map, type, coords, thresh[THRESH_COORDS], List); Vect_destroy_line_struct(coords); } @@ -124,8 +122,8 @@ struct ilist *select_lines(struct Map_info *Map, int layer, query_type = QUERY_DANGLE; G_verbose_message(_("Threshold value for querying is %.2f"), - selparams->thresh[THRESH_QUERY]); - Vedit_select_by_query(Map, type, layer, selparams->thresh[THRESH_QUERY], + thresh[THRESH_QUERY]); + Vedit_select_by_query(Map, type, layer, thresh[THRESH_QUERY], query_type, List_tmp); /* merge lists (only duplicate items) */ From 4339dfe07514aec1bbe7ce30d85baf2e5a7b19d5 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Tue, 27 Feb 2024 00:42:21 -0700 Subject: [PATCH 40/45] Fix a bug in the example --- vector/v.edit/v.edit.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vector/v.edit/v.edit.html b/vector/v.edit/v.edit.html index d56c560545e..215036fa368 100644 --- a/vector/v.edit/v.edit.html +++ b/vector/v.edit/v.edit.html @@ -552,16 +552,16 @@

        Batch editing

        v.segment roadsmajor output=roadsmajor_center # break roads at their center -v.to.db -p map=roadsmajor_center option=coor sep=comma | +v.to.db -p map=roadsmajor_center option=coor sep=space | sed '/^cat/d' | - while read cat x y; do + while read cat x y z; do v.edit map=roadsmajor_upper_half tool=break cats=$cat coords=$x,$y done # delete lower halves -v.to.db -p map=roadsmajor option=end sep=comma | +v.to.db -p map=roadsmajor option=end sep=space | sed '/^cat/d' | - while read cat x y; do + while read cat x y z; do v.edit map=roadsmajor_upper_half tool=delete cats=$cat coords=$x,$y done
      From d60975f83a2b43eec3af5b56337c086a7e15fb60 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Tue, 27 Feb 2024 00:46:22 -0700 Subject: [PATCH 41/45] Add a comment about unsupported multi-line columns --- vector/v.edit/batch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 5f31cae6409..203c06f45c1 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -275,7 +275,7 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, /** \brief Read a column from pcol string based on - https://www.rfc-editor.org/rfc/rfc4180 + https://www.rfc-editor.org/rfc/rfc4180; Does not support multi-line columns \param[in] pcol pointer to the start of a new column; this buffer is modified \param[in] sep separater character From bf7c826a349b44a532f3a116cca80bd0c8a8b5b9 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Tue, 27 Feb 2024 00:49:29 -0700 Subject: [PATCH 42/45] Add a comment about unsupported multi-line columns --- vector/v.edit/batch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 203c06f45c1..9648cd6d41c 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -324,7 +324,8 @@ static char *read_column(char *pcol, char sep, char **pnext) } } else - /* closing quote is missing; illegal column */ + /* closing quote is missing; possibly unsupported multi-line column; + * treat it as illegal */ pcol = NULL; } else if ((*pnext = strchr(pcol, sep))) From f187082b958e002ccec3505385e7a11d9c60e469 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Tue, 27 Feb 2024 00:56:48 -0700 Subject: [PATCH 43/45] Move List to non-batch block --- vector/v.edit/main.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index 7011a6dd294..d9ef173ac2e 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -34,10 +34,8 @@ int main(int argc, char *argv[]) int i; int ret; double thresh[3]; - struct ilist *List; ascii = NULL; - List = NULL; BgMap = NULL; nbgmaps = 0; @@ -207,6 +205,8 @@ int main(int argc, char *argv[]) &selparams); } else { + struct ilist *List = NULL; + if (action_mode != MODE_CREATE && action_mode != MODE_ADD) { /* select lines */ if (action_mode == MODE_COPY && BgMap && BgMap[0]) @@ -287,10 +287,10 @@ int main(int argc, char *argv[]) Vect_build(&Map); } } - } - if (List) - Vect_destroy_list(List); + if (List) + Vect_destroy_list(List); + } if (ascii && ascii != stdout) fclose(ascii); From c08a8a3016a3c177486e31103626156d89c15de7 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Tue, 27 Feb 2024 09:22:15 -0700 Subject: [PATCH 44/45] Add back the custom error handler to build topology on error --- vector/v.edit/main.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/vector/v.edit/main.c b/vector/v.edit/main.c index d9ef173ac2e..8355c5da11a 100644 --- a/vector/v.edit/main.c +++ b/vector/v.edit/main.c @@ -20,6 +20,8 @@ #include "global.h" +static void error_handler(void *); + int main(int argc, char *argv[]) { struct GModule *module; @@ -192,7 +194,7 @@ int main(int argc, char *argv[]) params.fld->answer) < 0) G_fatal_error(_("Unable to open vector map <%s>"), params.map->answer); - Vect_set_error_handler_io(&Map, NULL); + G_add_error_handler(error_handler, &Map); selparams.layer = Vect_get_field_number(&Map, params.fld->answer); if (BgMap && BgMap[0]) @@ -311,3 +313,12 @@ int main(int argc, char *argv[]) exit(ret > -1 ? EXIT_SUCCESS : EXIT_FAILURE); } + +static void error_handler(void *p) +{ + struct Map_info *Map = (struct Map_info *)p; + + Vect_build_partial(Map, GV_BUILD_NONE); + Vect_build(Map); + Vect_close(Map); +} From 15a1d41ff3dbb54d7611312c20120518b3cd2b02 Mon Sep 17 00:00:00 2001 From: Huidae Cho Date: Tue, 27 Feb 2024 11:46:14 -0700 Subject: [PATCH 45/45] Move separator line after topology building --- vector/v.edit/batch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vector/v.edit/batch.c b/vector/v.edit/batch.c index 9648cd6d41c..a666e5c09bc 100644 --- a/vector/v.edit/batch.c +++ b/vector/v.edit/batch.c @@ -253,8 +253,6 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, fclose(editparams.input); } - G_message(" "); - Vect_destroy_list(List); if (action_mode != MODE_SELECT && ret > 0) { @@ -262,6 +260,8 @@ int batch_edit(struct Map_info *Map, struct Map_info **BgMap, int nbgmaps, Vect_build(Map); total_ret += ret; } + + G_message(" "); } if (fp != stdin)