int main(int argc, char *argv[]) { struct Map_info In, Out, Error; struct line_pnts *Points; struct line_cats *Cats; int i, type, iter; struct GModule *module; /* GRASS module for parsing arguments */ struct Option *map_in, *map_out, *error_out, *thresh_opt, *method_opt, *look_ahead_opt; struct Option *iterations_opt, *cat_opt, *alpha_opt, *beta_opt, *type_opt; struct Option *field_opt, *where_opt, *reduction_opt, *slide_opt; struct Option *angle_thresh_opt, *degree_thresh_opt, *closeness_thresh_opt; struct Option *betweeness_thresh_opt; struct Flag *notab_flag, *loop_support_flag; int with_z; int total_input, total_output; /* Number of points in the input/output map respectively */ double thresh, alpha, beta, reduction, slide, angle_thresh; double degree_thresh, closeness_thresh, betweeness_thresh; int method; int look_ahead, iterations; int loop_support; int layer; int n_lines; int simplification, mask_type; struct cat_list *cat_list = NULL; char *s, *descriptions; /* initialize GIS environment */ G_gisinit(argv[0]); /* reads grass env, stores program name to G_program_name() */ /* initialize module */ module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("generalization")); G_add_keyword(_("simplification")); G_add_keyword(_("smoothing")); G_add_keyword(_("displacement")); G_add_keyword(_("network generalization")); module->description = _("Performs vector based generalization."); /* Define the different options as defined in gis.h */ map_in = G_define_standard_option(G_OPT_V_INPUT); field_opt = G_define_standard_option(G_OPT_V_FIELD_ALL); type_opt = G_define_standard_option(G_OPT_V_TYPE); type_opt->options = "line,boundary,area"; type_opt->answer = "line,boundary,area"; type_opt->guisection = _("Selection"); map_out = G_define_standard_option(G_OPT_V_OUTPUT); error_out = G_define_standard_option(G_OPT_V_OUTPUT); error_out->key = "error"; error_out->required = NO; error_out->description = _("Error map of all lines and boundaries not being generalized due to topology issues or over-simplification"); method_opt = G_define_option(); method_opt->key = "method"; method_opt->type = TYPE_STRING; method_opt->required = YES; method_opt->multiple = NO; method_opt->options = "douglas,douglas_reduction,lang,reduction,reumann,boyle,sliding_averaging,distance_weighting,chaiken,hermite,snakes,network,displacement"; descriptions = NULL; G_asprintf(&descriptions, "douglas;%s;" "douglas_reduction;%s;" "lang;%s;" "reduction;%s;" "reumann;%s;" "boyle;%s;" "sliding_averaging;%s;" "distance_weighting;%s;" "chaiken;%s;" "hermite;%s;" "snakes;%s;" "network;%s;" "displacement;%s;", _("Douglas-Peucker Algorithm"), _("Douglas-Peucker Algorithm with reduction parameter"), _("Lang Simplification Algorithm"), _("Vertex Reduction Algorithm eliminates points close to each other"), _("Reumann-Witkam Algorithm"), _("Boyle's Forward-Looking Algorithm"), _("McMaster's Sliding Averaging Algorithm"), _("McMaster's Distance-Weighting Algorithm"), _("Chaiken's Algorithm"), _("Interpolation by Cubic Hermite Splines"), _("Snakes method for line smoothing"), _("Network generalization"), _("Displacement of lines close to each other")); method_opt->descriptions = G_store(descriptions); method_opt->description = _("Generalization algorithm"); thresh_opt = G_define_option(); thresh_opt->key = "threshold"; thresh_opt->type = TYPE_DOUBLE; thresh_opt->required = YES; thresh_opt->options = "0-1000000000"; thresh_opt->description = _("Maximal tolerance value"); look_ahead_opt = G_define_option(); look_ahead_opt->key = "look_ahead"; look_ahead_opt->type = TYPE_INTEGER; look_ahead_opt->required = NO; look_ahead_opt->answer = "7"; look_ahead_opt->description = _("Look-ahead parameter"); reduction_opt = G_define_option(); reduction_opt->key = "reduction"; reduction_opt->type = TYPE_DOUBLE; reduction_opt->required = NO; reduction_opt->answer = "50"; reduction_opt->options = "0-100"; reduction_opt->description = _("Percentage of the points in the output of 'douglas_reduction' algorithm"); slide_opt = G_define_option(); slide_opt->key = "slide"; slide_opt->type = TYPE_DOUBLE; slide_opt->required = NO; slide_opt->answer = "0.5"; slide_opt->options = "0-1"; slide_opt->description = _("Slide of computed point toward the original point"); angle_thresh_opt = G_define_option(); angle_thresh_opt->key = "angle_thresh"; angle_thresh_opt->type = TYPE_DOUBLE; angle_thresh_opt->required = NO; angle_thresh_opt->answer = "3"; angle_thresh_opt->options = "0-180"; angle_thresh_opt->description = _("Minimum angle between two consecutive segments in Hermite method"); degree_thresh_opt = G_define_option(); degree_thresh_opt->key = "degree_thresh"; degree_thresh_opt->type = TYPE_INTEGER; degree_thresh_opt->required = NO; degree_thresh_opt->answer = "0"; degree_thresh_opt->description = _("Degree threshold in network generalization"); closeness_thresh_opt = G_define_option(); closeness_thresh_opt->key = "closeness_thresh"; closeness_thresh_opt->type = TYPE_DOUBLE; closeness_thresh_opt->required = NO; closeness_thresh_opt->answer = "0"; closeness_thresh_opt->options = "0-1"; closeness_thresh_opt->description = _("Closeness threshold in network generalization"); betweeness_thresh_opt = G_define_option(); betweeness_thresh_opt->key = "betweeness_thresh"; betweeness_thresh_opt->type = TYPE_DOUBLE; betweeness_thresh_opt->required = NO; betweeness_thresh_opt->answer = "0"; betweeness_thresh_opt->description = _("Betweeness threshold in network generalization"); alpha_opt = G_define_option(); alpha_opt->key = "alpha"; alpha_opt->type = TYPE_DOUBLE; alpha_opt->required = NO; alpha_opt->answer = "1.0"; alpha_opt->description = _("Snakes alpha parameter"); beta_opt = G_define_option(); beta_opt->key = "beta"; beta_opt->type = TYPE_DOUBLE; beta_opt->required = NO; beta_opt->answer = "1.0"; beta_opt->description = _("Snakes beta parameter"); iterations_opt = G_define_option(); iterations_opt->key = "iterations"; iterations_opt->type = TYPE_INTEGER; iterations_opt->required = NO; iterations_opt->answer = "1"; iterations_opt->description = _("Number of iterations"); cat_opt = G_define_standard_option(G_OPT_V_CATS); cat_opt->guisection = _("Selection"); where_opt = G_define_standard_option(G_OPT_DB_WHERE); where_opt->guisection = _("Selection"); loop_support_flag = G_define_flag(); loop_support_flag->key = 'l'; loop_support_flag->label = _("Disable loop support"); loop_support_flag->description = _("Do not modify end points of lines forming a closed loop"); notab_flag = G_define_standard_flag(G_FLG_V_TABLE); notab_flag->description = _("Do not copy attributes"); notab_flag->guisection = _("Attributes"); /* options and flags parser */ if (G_parser(argc, argv)) exit(EXIT_FAILURE); thresh = atof(thresh_opt->answer); look_ahead = atoi(look_ahead_opt->answer); alpha = atof(alpha_opt->answer); beta = atof(beta_opt->answer); reduction = atof(reduction_opt->answer); iterations = atoi(iterations_opt->answer); slide = atof(slide_opt->answer); angle_thresh = atof(angle_thresh_opt->answer); degree_thresh = atof(degree_thresh_opt->answer); closeness_thresh = atof(closeness_thresh_opt->answer); betweeness_thresh = atof(betweeness_thresh_opt->answer); mask_type = type_mask(type_opt); G_debug(3, "Method: %s", method_opt->answer); s = method_opt->answer; if (strcmp(s, "douglas") == 0) method = DOUGLAS; else if (strcmp(s, "lang") == 0) method = LANG; else if (strcmp(s, "reduction") == 0) method = VERTEX_REDUCTION; else if (strcmp(s, "reumann") == 0) method = REUMANN; else if (strcmp(s, "boyle") == 0) method = BOYLE; else if (strcmp(s, "distance_weighting") == 0) method = DISTANCE_WEIGHTING; else if (strcmp(s, "chaiken") == 0) method = CHAIKEN; else if (strcmp(s, "hermite") == 0) method = HERMITE; else if (strcmp(s, "snakes") == 0) method = SNAKES; else if (strcmp(s, "douglas_reduction") == 0) method = DOUGLAS_REDUCTION; else if (strcmp(s, "sliding_averaging") == 0) method = SLIDING_AVERAGING; else if (strcmp(s, "network") == 0) method = NETWORK; else if (strcmp(s, "displacement") == 0) { method = DISPLACEMENT; /* we can displace only the lines */ mask_type = GV_LINE; } else { G_fatal_error(_("Unknown method")); exit(EXIT_FAILURE); } /* simplification or smoothing? */ switch (method) { case DOUGLAS: case DOUGLAS_REDUCTION: case LANG: case VERTEX_REDUCTION: case REUMANN: simplification = 1; break; default: simplification = 0; break; } Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); Vect_check_input_output_name(map_in->answer, map_out->answer, G_FATAL_EXIT); Vect_set_open_level(2); if (Vect_open_old2(&In, map_in->answer, "", field_opt->answer) < 1) G_fatal_error(_("Unable to open vector map <%s>"), map_in->answer); if (Vect_get_num_primitives(&In, mask_type) == 0) { G_warning(_("No lines found in input map <%s>"), map_in->answer); Vect_close(&In); exit(EXIT_SUCCESS); } with_z = Vect_is_3d(&In); if (0 > Vect_open_new(&Out, map_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create vector map <%s>"), map_out->answer); } if (error_out->answer) { if (0 > Vect_open_new(&Error, error_out->answer, with_z)) { Vect_close(&In); G_fatal_error(_("Unable to create error vector map <%s>"), error_out->answer); } } Vect_copy_head_data(&In, &Out); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); total_input = total_output = 0; layer = Vect_get_field_number(&In, field_opt->answer); /* parse filter options */ if (layer > 0) cat_list = Vect_cats_set_constraint(&In, layer, where_opt->answer, cat_opt->answer); if (method == DISPLACEMENT) { /* modifies only lines, all other features including boundaries are preserved */ /* options where, cats, and layer are respected */ G_message(_("Displacement...")); snakes_displacement(&In, &Out, thresh, alpha, beta, 1.0, 10.0, iterations, cat_list, layer); } /* TODO: rearrange code below. It's really messy */ if (method == NETWORK) { /* extracts lines of selected type, all other features are discarded */ /* options where, cats, and layer are ignored */ G_message(_("Network generalization...")); total_output = graph_generalization(&In, &Out, mask_type, degree_thresh, closeness_thresh, betweeness_thresh); } /* copy tables here because method == NETWORK is complete and * tables for Out may be needed for parse_filter_options() below */ if (!notab_flag->answer) { if (method == NETWORK) copy_tables_by_cats(&In, &Out); else Vect_copy_tables(&In, &Out, -1); } else if (where_opt->answer && method < NETWORK) { G_warning(_("Attributes are needed for 'where' option, copying table")); Vect_copy_tables(&In, &Out, -1); } /* smoothing/simplification */ if (method < NETWORK) { /* modifies only lines of selected type, all other features are preserved */ int not_modified_boundaries = 0, n_oversimplified = 0; struct line_pnts *APoints; /* original Points */ set_topo_debug(); Vect_copy_map_lines(&In, &Out); Vect_build_partial(&Out, GV_BUILD_CENTROIDS); G_message("-----------------------------------------------------"); G_message(_("Generalization (%s)..."), method_opt->answer); G_message(_("Using threshold: %g %s"), thresh, G_database_unit_name(1)); G_percent_reset(); APoints = Vect_new_line_struct(); n_lines = Vect_get_num_lines(&Out); for (i = 1; i <= n_lines; i++) { int after = 0; G_percent(i, n_lines, 1); type = Vect_read_line(&Out, APoints, Cats, i); if (!(type & GV_LINES) || !(mask_type & type)) continue; if (layer > 0) { if ((type & GV_LINE) && !Vect_cats_in_constraint(Cats, layer, cat_list)) continue; else if ((type & GV_BOUNDARY)) { int do_line = 0; int left, right; do_line = Vect_cats_in_constraint(Cats, layer, cat_list); if (!do_line) { /* check if any of the centroids is selected */ Vect_get_line_areas(&Out, i, &left, &right); if (left < 0) left = Vect_get_isle_area(&Out, abs(left)); if (right < 0) right = Vect_get_isle_area(&Out, abs(right)); if (left > 0) { Vect_get_area_cats(&Out, left, Cats); do_line = Vect_cats_in_constraint(Cats, layer, cat_list); } if (!do_line && right > 0) { Vect_get_area_cats(&Out, right, Cats); do_line = Vect_cats_in_constraint(Cats, layer, cat_list); } } if (!do_line) continue; } } Vect_line_prune(APoints); if (APoints->n_points < 2) /* Line of length zero, delete if boundary ? */ continue; total_input += APoints->n_points; /* copy points */ Vect_reset_line(Points); Vect_append_points(Points, APoints, GV_FORWARD); loop_support = 0; if (!loop_support_flag->answer) { int n1, n2; Vect_get_line_nodes(&Out, i, &n1, &n2); if (n1 == n2) { if (Vect_get_node_n_lines(&Out, n1) == 2) { if (abs(Vect_get_node_line(&Out, n1, 0)) == i && abs(Vect_get_node_line(&Out, n1, 1)) == i) loop_support = 1; } } } for (iter = 0; iter < iterations; iter++) { switch (method) { case DOUGLAS: douglas_peucker(Points, thresh, with_z); break; case DOUGLAS_REDUCTION: douglas_peucker_reduction(Points, thresh, reduction, with_z); break; case LANG: lang(Points, thresh, look_ahead, with_z); break; case VERTEX_REDUCTION: vertex_reduction(Points, thresh, with_z); break; case REUMANN: reumann_witkam(Points, thresh, with_z); break; case BOYLE: boyle(Points, look_ahead, loop_support, with_z); break; case SLIDING_AVERAGING: sliding_averaging(Points, slide, look_ahead, loop_support, with_z); break; case DISTANCE_WEIGHTING: distance_weighting(Points, slide, look_ahead, loop_support, with_z); break; case CHAIKEN: chaiken(Points, thresh, loop_support, with_z); break; case HERMITE: hermite(Points, thresh, angle_thresh, loop_support, with_z); break; case SNAKES: snakes(Points, alpha, beta, loop_support, with_z); break; } } if (loop_support == 0) { /* safety check, BUG in method if not passed */ if (APoints->x[0] != Points->x[0] || APoints->y[0] != Points->y[0] || APoints->z[0] != Points->z[0]) G_fatal_error(_("Method '%s' did not preserve first point"), method_opt->answer); if (APoints->x[APoints->n_points - 1] != Points->x[Points->n_points - 1] || APoints->y[APoints->n_points - 1] != Points->y[Points->n_points - 1] || APoints->z[APoints->n_points - 1] != Points->z[Points->n_points - 1]) G_fatal_error(_("Method '%s' did not preserve last point"), method_opt->answer); } else { /* safety check, BUG in method if not passed */ if (Points->x[0] != Points->x[Points->n_points - 1] || Points->y[0] != Points->y[Points->n_points - 1] || Points->z[0] != Points->z[Points->n_points - 1]) G_fatal_error(_("Method '%s' did not preserve loop"), method_opt->answer); } Vect_line_prune(Points); /* oversimplified line */ if (Points->n_points < 2) { after = APoints->n_points; n_oversimplified++; if (error_out->answer) Vect_write_line(&Error, type, APoints, Cats); } /* check for topology corruption */ else if (type == GV_BOUNDARY) { if (!check_topo(&Out, i, APoints, Points, Cats)) { after = APoints->n_points; not_modified_boundaries++; if (error_out->answer) Vect_write_line(&Error, type, APoints, Cats); } else after = Points->n_points; } else { /* type == GV_LINE */ Vect_rewrite_line(&Out, i, type, Points, Cats); after = Points->n_points; } total_output += after; } if (not_modified_boundaries > 0) G_warning(_("%d boundaries were not modified because modification would damage topology"), not_modified_boundaries); if (n_oversimplified > 0) G_warning(_("%d lines/boundaries were not modified due to over-simplification"), n_oversimplified); G_message("-----------------------------------------------------"); /* make sure that clean topo is built at the end */ Vect_build_partial(&Out, GV_BUILD_NONE); if (error_out->answer) Vect_build_partial(&Error, GV_BUILD_NONE); } Vect_build(&Out); if (error_out->answer) Vect_build(&Error); Vect_close(&In); Vect_close(&Out); if (error_out->answer) Vect_close(&Error); G_message("-----------------------------------------------------"); if (total_input != 0 && total_input != total_output) G_done_msg(_("Number of vertices for selected features %s from %d to %d (%d%% remaining)"), simplification ? _("reduced") : _("changed"), total_input, total_output, (total_output * 100) / total_input); else G_done_msg(" "); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { char *output, buf[DB_SQL_MAX]; double (*rng)(void) = G_drand48; double zmin, zmax; int seed; int i, j, k, n, type, usefloat; int area, nareas, field; struct boxlist *List = NULL; BOX_SIZE *size_list = NULL; int alloc_size_list = 0; struct Map_info In, Out; struct line_pnts *Points; struct line_cats *Cats; struct cat_list *cat_list; struct bound_box box; struct Cell_head window; struct GModule *module; struct { struct Option *input, *field, *cats, *where, *output, *nsites, *zmin, *zmax, *zcol, *ztype, *seed; } parm; struct { struct Flag *z, *notopo, *a; } flag; struct field_info *Fi; dbDriver *driver; dbTable *table; dbString sql; G_gisinit(argv[0]); module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("sampling")); G_add_keyword(_("statistics")); G_add_keyword(_("random")); module->description = _("Generates random 2D/3D vector points."); parm.output = G_define_standard_option(G_OPT_V_OUTPUT); parm.nsites = G_define_option(); parm.nsites->key = "n"; parm.nsites->type = TYPE_INTEGER; parm.nsites->required = YES; parm.nsites->description = _("Number of points to be created"); parm.input = G_define_standard_option(G_OPT_V_INPUT); parm.input->required = NO; parm.input->description = _("Restrict points to areas in input vector"); parm.input->guisection = _("Selection"); parm.field = G_define_standard_option(G_OPT_V_FIELD_ALL); parm.field->guisection = _("Selection"); parm.cats = G_define_standard_option(G_OPT_V_CATS); parm.cats->guisection = _("Selection"); parm.where = G_define_standard_option(G_OPT_DB_WHERE); parm.where->guisection = _("Selection"); parm.zmin = G_define_option(); parm.zmin->key = "zmin"; parm.zmin->type = TYPE_DOUBLE; parm.zmin->required = NO; parm.zmin->description = _("Minimum z height (needs -z flag or column name)"); parm.zmin->answer = "0.0"; parm.zmin->guisection = _("3D output"); parm.zmax = G_define_option(); parm.zmax->key = "zmax"; parm.zmax->type = TYPE_DOUBLE; parm.zmax->required = NO; parm.zmax->description = _("Maximum z height (needs -z flag or column name)"); parm.zmax->answer = "0.0"; parm.zmax->guisection = _("3D output"); parm.seed = G_define_option(); parm.seed->key = "seed"; parm.seed->type = TYPE_INTEGER; parm.seed->required = NO; parm.seed->description = _("The seed to initialize the random generator. If not set the process ID is used"); parm.zcol = G_define_standard_option(G_OPT_DB_COLUMN); parm.zcol->label = _("Name of column for z values"); parm.zcol->description = _("Writes z values to column"); parm.zcol->guisection = _("3D output"); parm.ztype = G_define_option(); parm.ztype->key = "column_type"; parm.ztype->type = TYPE_STRING; parm.ztype->required = NO; parm.ztype->multiple = NO; parm.ztype->description = _("Type of column for z values"); parm.ztype->options = "integer,double precision"; parm.ztype->answer = "double precision"; parm.ztype->guisection = _("3D output"); flag.z = G_define_flag(); flag.z->key = 'z'; flag.z->description = _("Create 3D output"); flag.z->guisection = _("3D output"); flag.a = G_define_flag(); flag.a->key = 'a'; flag.a->description = _("Generate n points for each individual area"); flag.notopo = G_define_standard_flag(G_FLG_V_TOPO); if (G_parser(argc, argv)) exit(EXIT_FAILURE); output = parm.output->answer; n = atoi(parm.nsites->answer); if(parm.seed->answer) seed = atoi(parm.seed->answer); if (n <= 0) { G_fatal_error(_("Number of points must be > 0 (%d given)"), n); } nareas = 0; cat_list = NULL; field = -1; if (parm.input->answer) { Vect_set_open_level(2); /* topology required */ if (2 > Vect_open_old2(&In, parm.input->answer, "", parm.field->answer)) G_fatal_error(_("Unable to open vector map <%s>"), parm.input->answer); if (parm.field->answer) field = Vect_get_field_number(&In, parm.field->answer); if ((parm.cats->answer || parm.where->answer) && field == -1) { G_warning(_("Invalid layer number (%d). Parameter '%s' or '%s' specified, assuming layer '1'."), field, parm.cats->key, parm.where->key); field = 1; } if (field > 0) cat_list = Vect_cats_set_constraint(&In, field, parm.where->answer, parm.cats->answer); nareas = Vect_get_num_areas(&In); if (nareas == 0) { Vect_close(&In); G_fatal_error(_("No areas in vector map <%s>"), parm.input->answer); } } else { if (flag.a->answer) G_fatal_error(_("The <-%c> flag requires an input vector with areas"), flag.a->key); } /* create new vector map */ if (-1 == Vect_open_new(&Out, output, flag.z->answer ? WITH_Z : WITHOUT_Z)) G_fatal_error(_("Unable to create vector map <%s>"), output); Vect_set_error_handler_io(NULL, &Out); /* Do we need to write random values into attribute table? */ usefloat = -1; if (parm.zcol->answer) { Fi = Vect_default_field_info(&Out, 1, NULL, GV_1TABLE); driver = db_start_driver_open_database(Fi->driver, Vect_subst_var(Fi->database, &Out)); if (driver == NULL) { G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Vect_subst_var(Fi->database, &Out), Fi->driver); } db_set_error_handler_driver(driver); db_begin_transaction(driver); db_init_string(&sql); sprintf(buf, "create table %s (%s integer, %s %s)", Fi->table, GV_KEY_COLUMN, parm.zcol->answer, parm.ztype->answer); db_set_string(&sql, buf); Vect_map_add_dblink(&Out, 1, NULL, Fi->table, GV_KEY_COLUMN, Fi->database, Fi->driver); /* Create table */ G_debug(3, db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { G_fatal_error(_("Unable to create table: %s"), db_get_string(&sql)); } /* Create index */ if (db_create_index2(driver, Fi->table, Fi->key) != DB_OK) G_warning(_("Unable to create index")); /* Grant */ if (db_grant_on_table (driver, Fi->table, DB_PRIV_SELECT, DB_GROUP | DB_PUBLIC) != DB_OK) { G_fatal_error(_("Unable to grant privileges on table <%s>"), Fi->table); } /* OK. Let's check what type of column user has created */ db_set_string(&sql, Fi->table); if (db_describe_table(driver, &sql, &table) != DB_OK) { G_fatal_error(_("Unable to describe table <%s>"), Fi->table); } if (db_get_table_number_of_columns(table) != 2) { G_fatal_error(_("Table should contain only two columns")); } type = db_get_column_sqltype(db_get_table_column(table, 1)); if (type == DB_SQL_TYPE_SMALLINT || type == DB_SQL_TYPE_INTEGER) usefloat = 0; if (type == DB_SQL_TYPE_REAL || type == DB_SQL_TYPE_DOUBLE_PRECISION) usefloat = 1; if (usefloat < 0) { G_fatal_error(_("You have created unsupported column type. This module supports only INTEGER" " and DOUBLE PRECISION column types.")); } } Vect_hist_command(&Out); /* Init the random seed */ if(parm.seed->answer) G_srand48(seed); else G_srand48_auto(); G_get_window(&window); Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); if (nareas > 0) { int first = 1, count; struct bound_box abox, bbox; box.W = window.west; box.E = window.east; box.S = window.south; box.N = window.north; box.B = -PORT_DOUBLE_MAX; box.T = PORT_DOUBLE_MAX; count = 0; for (i = 1; i <= nareas; i++) { if (!Vect_get_area_centroid(&In, i)) continue; if (field > 0) { if (Vect_get_area_cats(&In, i, Cats)) continue; if (!Vect_cats_in_constraint(Cats, field, cat_list)) continue; } Vect_get_area_box(&In, i, &abox); if (!Vect_box_overlap(&abox, &box)) continue; if (first) { Vect_box_copy(&bbox, &abox); first = 0; } else Vect_box_extend(&bbox, &abox); count++; } if (count == 0) { Vect_close(&In); Vect_close(&Out); Vect_delete(output); G_fatal_error(_("Selected areas in input vector <%s> do not overlap with the current region"), parm.input->answer); } Vect_box_copy(&box, &bbox); /* does the vector overlap with the current region ? */ if (box.W >= window.east || box.E <= window.west || box.S >= window.north || box.N <= window.south) { Vect_close(&In); Vect_close(&Out); Vect_delete(output); G_fatal_error(_("Input vector <%s> does not overlap with the current region"), parm.input->answer); } /* try to reduce the current region */ if (window.east > box.E) window.east = box.E; if (window.west < box.W) window.west = box.W; if (window.north > box.N) window.north = box.N; if (window.south < box.S) window.south = box.S; List = Vect_new_boxlist(1); alloc_size_list = 10; size_list = G_malloc(alloc_size_list * sizeof(BOX_SIZE)); } zmin = zmax = 0; if (flag.z->answer || parm.zcol->answer) { zmax = atof(parm.zmax->answer); zmin = atof(parm.zmin->answer); } G_message(_("Generating points...")); if (flag.a->answer && nareas > 0) { struct bound_box abox, bbox; int cat = 1; /* n points for each area */ nareas = Vect_get_num_areas(&In); G_percent(0, nareas, 1); for (area = 1; area <= nareas; area++) { G_percent(area, nareas, 1); if (!Vect_get_area_centroid(&In, area)) continue; if (field > 0) { if (Vect_get_area_cats(&In, area, Cats)) continue; if (!Vect_cats_in_constraint(Cats, field, cat_list)) { continue; } } box.W = window.west; box.E = window.east; box.S = window.south; box.N = window.north; box.B = -PORT_DOUBLE_MAX; box.T = PORT_DOUBLE_MAX; Vect_get_area_box(&In, area, &abox); if (!Vect_box_overlap(&box, &abox)) continue; bbox = abox; if (bbox.W < box.W) bbox.W = box.W; if (bbox.E > box.E) bbox.E = box.E; if (bbox.S < box.S) bbox.S = box.S; if (bbox.N > box.N) bbox.N = box.N; for (i = 0; i < n; ++i) { double x, y, z; int outside = 1; int ret; Vect_reset_line(Points); Vect_reset_cats(Cats); while (outside) { x = rng() * (bbox.W - bbox.E) + bbox.E; y = rng() * (bbox.N - bbox.S) + bbox.S; z = rng() * (zmax - zmin) + zmin; ret = Vect_point_in_area(x, y, &In, area, &abox); G_debug(3, " area = %d Vect_point_in_area() = %d", area, ret); if (ret >= 1) { outside = 0; } } if (flag.z->answer) Vect_append_point(Points, x, y, z); else Vect_append_point(Points, x, y, 0.0); if (parm.zcol->answer) { sprintf(buf, "insert into %s values ( %d, ", Fi->table, i + 1); db_set_string(&sql, buf); /* Round random value if column is integer type */ if (usefloat) sprintf(buf, "%f )", z); else sprintf(buf, "%.0f )", z); db_append_string(&sql, buf); G_debug(3, db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { G_fatal_error(_("Cannot insert new row: %s"), db_get_string(&sql)); } } Vect_cat_set(Cats, 1, cat++); Vect_write_line(&Out, GV_POINT, Points, Cats); } } } else { /* n points in total */ for (i = 0; i < n; ++i) { double x, y, z; G_percent(i, n, 4); Vect_reset_line(Points); Vect_reset_cats(Cats); x = rng() * (window.west - window.east) + window.east; y = rng() * (window.north - window.south) + window.south; z = rng() * (zmax - zmin) + zmin; if (nareas) { int outside = 1; do { /* select areas by box */ box.E = x; box.W = x; box.N = y; box.S = y; box.T = PORT_DOUBLE_MAX; box.B = -PORT_DOUBLE_MAX; Vect_select_areas_by_box(&In, &box, List); G_debug(3, " %d areas selected by box", List->n_values); /* sort areas by size, the smallest is likely to be the nearest */ if (alloc_size_list < List->n_values) { alloc_size_list = List->n_values; size_list = G_realloc(size_list, alloc_size_list * sizeof(BOX_SIZE)); } k = 0; for (j = 0; j < List->n_values; j++) { area = List->id[j]; if (!Vect_get_area_centroid(&In, area)) continue; if (field > 0) { if (Vect_get_area_cats(&In, area, Cats)) continue; if (!Vect_cats_in_constraint(Cats, field, cat_list)) { continue; } } List->id[k] = List->id[j]; List->box[k] = List->box[j]; size_list[k].i = List->id[k]; box = List->box[k]; size_list[k].box = List->box[k]; size_list[k].size = (box.N - box.S) * (box.E - box.W); k++; } List->n_values = k; if (List->n_values == 2) { /* simple swap */ if (size_list[1].size < size_list[0].size) { size_list[0].i = List->id[1]; size_list[1].i = List->id[0]; size_list[0].box = List->box[1]; size_list[1].box = List->box[0]; } } else if (List->n_values > 2) qsort(size_list, List->n_values, sizeof(BOX_SIZE), sort_by_size); for (j = 0; j < List->n_values; j++) { int ret; area = size_list[j].i; ret = Vect_point_in_area(x, y, &In, area, &size_list[j].box); G_debug(3, " area = %d Vect_point_in_area() = %d", area, ret); if (ret >= 1) { outside = 0; break; } } if (outside) { x = rng() * (window.west - window.east) + window.east; y = rng() * (window.north - window.south) + window.south; z = rng() * (zmax - zmin) + zmin; } } while (outside); } if (flag.z->answer) Vect_append_point(Points, x, y, z); else Vect_append_point(Points, x, y, 0.0); if (parm.zcol->answer) { sprintf(buf, "insert into %s values ( %d, ", Fi->table, i + 1); db_set_string(&sql, buf); /* Round random value if column is integer type */ if (usefloat) sprintf(buf, "%f )", z); else sprintf(buf, "%.0f )", z); db_append_string(&sql, buf); G_debug(3, db_get_string(&sql)); if (db_execute_immediate(driver, &sql) != DB_OK) { G_fatal_error(_("Cannot insert new row: %s"), db_get_string(&sql)); } } Vect_cat_set(Cats, 1, i + 1); Vect_write_line(&Out, GV_POINT, Points, Cats); } G_percent(1, 1, 1); } if (parm.zcol->answer) { db_commit_transaction(driver); db_close_database_shutdown_driver(driver); } if (!flag.notopo->answer) { Vect_build(&Out); } Vect_close(&Out); exit(EXIT_SUCCESS); }
int main(int argc, char *argv[]) { struct GModule *module; struct { struct Option *input, *output, *zshift, *height, *elevation, *hcolumn, *type, *field, *cats, *where, *interp, *scale, *null; } opt; struct { struct Flag *trace; } flag; struct Map_info In, Out; struct line_pnts *Points; struct line_cats *Cats; struct bound_box map_box; struct cat_list *cat_list; struct Cell_head window; int field; int only_type, cat; int fdrast, interp_method, trace; double objheight, objheight_default, voffset; double scale, null_val; struct field_info *Fi; dbDriver *driver = NULL; char *comment; module = G_define_module(); G_add_keyword(_("vector")); G_add_keyword(_("geometry")); G_add_keyword(_("sampling")); G_add_keyword(_("3D")); module->label = _("Extrudes flat vector features to 3D vector features with defined height."); module->description = _("Optionally the height can be derived from sampling of elevation raster map."); flag.trace = G_define_flag(); flag.trace->key = 't'; flag.trace->description = _("Trace elevation"); flag.trace->guisection = _("Elevation"); opt.input = G_define_standard_option(G_OPT_V_INPUT); opt.field = G_define_standard_option(G_OPT_V_FIELD_ALL); opt.field->guisection = _("Selection"); opt.cats = G_define_standard_option(G_OPT_V_CATS); opt.cats->guisection = _("Selection"); opt.where = G_define_standard_option(G_OPT_DB_WHERE); opt.where->guisection = _("Selection"); opt.type = G_define_standard_option(G_OPT_V_TYPE); opt.type->answer = "point,line,area"; opt.type->options = "point,line,area"; opt.type->guisection = _("Selection"); opt.output = G_define_standard_option(G_OPT_V_OUTPUT); opt.zshift = G_define_option(); opt.zshift->key = "zshift"; opt.zshift->description = _("Shifting value for z coordinates"); opt.zshift->type = TYPE_DOUBLE; opt.zshift->required = NO; opt.zshift->answer = "0"; opt.zshift->guisection = _("Height"); opt.height = G_define_option(); opt.height->key = "height"; opt.height->type = TYPE_DOUBLE; opt.height->required = NO; opt.height->multiple = NO; opt.height->description = _("Fixed height for 3D vector features"); opt.height->guisection = _("Height"); opt.hcolumn = G_define_standard_option(G_OPT_DB_COLUMN); opt.hcolumn->key = "height_column"; opt.hcolumn->multiple = NO; opt.hcolumn->description = _("Name of attribute column with feature height"); opt.hcolumn->guisection = _("Height"); /* raster sampling */ opt.elevation = G_define_standard_option(G_OPT_R_ELEV); opt.elevation->required = NO; opt.elevation->description = _("Elevation raster map for height extraction"); opt.elevation->guisection = _("Elevation"); opt.interp = G_define_standard_option(G_OPT_R_INTERP_TYPE); opt.interp->answer = "nearest"; opt.interp->guisection = _("Elevation"); opt.scale = G_define_option(); opt.scale->key = "scale"; opt.scale->type = TYPE_DOUBLE; opt.scale->description = _("Scale factor sampled raster values"); opt.scale->answer = "1.0"; opt.scale->guisection = _("Elevation"); opt.null = G_define_option(); opt.null->key = "null_value"; opt.null->type = TYPE_DOUBLE; opt.null->description = _("Height for sampled raster NULL values"); opt.null->guisection = _("Elevation"); G_gisinit(argv[0]); if (G_parser(argc, argv)) exit(EXIT_FAILURE); if (!opt.height->answer && !opt.hcolumn->answer) { G_fatal_error(_("One of '%s' or '%s' parameters must be set"), opt.height->key, opt.hcolumn->key); } sscanf(opt.zshift->answer, "%lf", &voffset); G_debug(1, "voffset = %f", voffset); if (opt.height->answer) sscanf(opt.height->answer, "%lf", &objheight); else objheight = 0.; G_debug(1, "objheight = %f", objheight); objheight_default = objheight; only_type = Vect_option_to_types(opt.type); /* sampling method */ interp_method = Rast_option_to_interp_type(opt.interp); /* used to scale sampled raster values */ scale = atof(opt.scale->answer); /* is null value defined */ if (opt.null->answer) null_val = atof(opt.null->answer); /* trace elevation */ trace = flag.trace->answer ? TRUE : FALSE; /* set input vector map name and mapset */ Vect_check_input_output_name(opt.input->answer, opt.output->answer, G_FATAL_EXIT); Points = Vect_new_line_struct(); Cats = Vect_new_cats_struct(); Vect_set_open_level(2); /* topology required for input */ /* opening input vector map */ if (Vect_open_old2(&In, opt.input->answer, "", opt.field->answer) < 0) G_fatal_error(_("Unable to open vector map <%s>"), opt.input->answer); Vect_set_error_handler_io(&In, &Out); /* creating output vector map */ if (Vect_open_new(&Out, opt.output->answer, WITH_Z) < 0) G_fatal_error(_("Unable to create vector map <%s>"), opt.output->answer); field = Vect_get_field_number(&In, opt.field->answer); if ((opt.hcolumn->answer || opt.cats->answer || opt.where->answer) && field == -1) { G_warning(_("Invalid layer number (%d). " "Parameter '%s', '%s' or '%s' specified, assuming layer '1'."), field, opt.hcolumn->key, opt.cats->key, opt.where->key); field = 1; } /* set constraint for cats or where */ cat_list = NULL; if (field > 0) cat_list = Vect_cats_set_constraint(&In, field, opt.where->answer, opt.cats->answer); Vect_hist_copy(&In, &Out); Vect_hist_command(&Out); /* opening database connection, if required */ if (opt.hcolumn->answer) { int ctype; dbColumn *column; if ((Fi = Vect_get_field(&In, field)) == NULL) G_fatal_error(_("Database connection not defined for layer %d"), field); if ((driver = db_start_driver_open_database(Fi->driver, Fi->database)) == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); db_set_error_handler_driver(driver); if (db_get_column(driver, Fi->table, opt.hcolumn->answer, &column) != DB_OK) G_fatal_error(_("Column <%s> does not exist"), opt.hcolumn->answer); else db_free_column(column); ctype = db_column_Ctype(driver, Fi->table, opt.hcolumn->answer); if (ctype != DB_C_TYPE_INT && ctype != DB_C_TYPE_STRING && ctype != DB_C_TYPE_DOUBLE) { G_fatal_error(_("Column <%s>: invalid data type"), opt.hcolumn->answer); } } /* do we work with elevation raster? */ fdrast = -1; if (opt.elevation->answer) { /* raster setup */ G_get_window(&window); /* open the elev raster, and check for error condition */ fdrast = Rast_open_old(opt.elevation->answer, ""); } /* if area */ if (only_type & GV_AREA) { int area, nareas, centroid; nareas = Vect_get_num_areas(&In); G_debug(2, "n_areas = %d", nareas); if (nareas > 0) G_message(_("Extruding areas...")); for (area = 1; area <= nareas; area++) { G_debug(3, "area = %d", area); G_percent(area, nareas, 2); if (!Vect_area_alive(&In, area)) continue; centroid = Vect_get_area_centroid(&In, area); if (!centroid) { G_warning(_("Skipping area %d without centroid"), area); continue; } Vect_read_line(&In, NULL, Cats, centroid); if (field > 0 && !Vect_cats_in_constraint(Cats, field, cat_list)) continue; /* height attribute */ if (opt.hcolumn->answer) { cat = Vect_get_area_cat(&In, area, field); if (cat == -1) { G_warning(_("No category defined for area %d. Using default fixed height %f."), area, objheight_default); objheight = objheight_default; } if (get_height(Fi, opt.hcolumn->answer, driver, cat, &objheight) != 0) { G_warning(_("Unable to fetch height from DB for area %d. Using default fixed height %f."), area, objheight_default); objheight = objheight_default; } } /* if opt.hcolumn->answer */ Vect_get_area_points(&In, area, Points); G_debug(3, "area: %d height: %f", area, objheight); extrude(&In, &Out, Cats, Points, fdrast, trace, interp_method, scale, opt.null->answer ? TRUE : FALSE, null_val, objheight, voffset, &window, GV_AREA, centroid); } /* foreach area */ } if (only_type > 0) { int line, nlines; int type; G_debug(1, "other than areas"); /* loop through each line in the dataset */ nlines = Vect_get_num_lines(&In); G_message(_("Extruding features...")); for (line = 1; line <= nlines; line++) { /* progress feedback */ G_percent(line, nlines, 2); if (!Vect_line_alive(&In, line)) continue; /* read line */ type = Vect_read_line(&In, Points, Cats, line); if (!(type & only_type)) continue; if (field > 0 && !Vect_cats_in_constraint(Cats, field, cat_list)) continue; /* height attribute */ if (opt.hcolumn->answer) { cat = Vect_get_line_cat(&In, line, field); if (cat == -1) { G_warning(_("No category defined for feature %d. Using default fixed height %f."), line, objheight_default); objheight = objheight_default; } if (get_height(Fi, opt.hcolumn->answer, driver, cat, &objheight) != 0) { G_warning(_("Unable to fetch height from DB for line %d. Using default fixed height %f."), line, objheight_default); objheight = objheight_default; } } /* if opt.hcolumn->answer */ extrude(&In, &Out, Cats, Points, fdrast, trace, interp_method, scale, opt.null->answer ? TRUE : FALSE, null_val, objheight, voffset, &window, type, -1); } /* for each line */ } /* else if area */ if (driver) { db_close_database(driver); db_shutdown_driver(driver); } G_important_message(_("Copying attribute table...")); if (field < 0) Vect_copy_tables(&In, &Out, 0); else Vect_copy_table_by_cat_list(&In, &Out, field, field, NULL, GV_1TABLE, cat_list); Vect_build(&Out); /* header */ G_asprintf(&comment, "Generated by %s from vector map <%s>", G_program_name(), Vect_get_full_name(&In)); Vect_set_comment(&Out, comment); G_free(comment); Vect_get_map_box(&Out, &map_box); Vect_close(&In); Vect_close(&Out); Vect_destroy_line_struct(Points); Vect_destroy_cats_struct(Cats); G_done_msg("T: %f B: %f.", map_box.T, map_box.B); exit(EXIT_SUCCESS); }
int vect_to_rast(const char *vector_map, const char *raster_map, const char *field_name, const char *column, int cache_mb, int use, double value, int value_type, const char *rgbcolumn, const char *labelcolumn, int ftype, char *where, char *cats, int dense) { struct Map_info Map; struct line_pnts *Points; int i, field; struct cat_list *cat_list = NULL; int fd; /* for raster map */ int nareas, nlines; /* number of converted features */ int nareas_all, nplines_all; /* number of all areas, points/lines */ int stat; int format; int pass, npasses; /* Attributes */ int nrec; int ctype = 0; struct field_info *Fi; dbDriver *Driver; dbCatValArray cvarr; int is_fp = 0; nareas = 0; G_verbose_message(_("Loading data...")); Vect_set_open_level(2); if (Vect_open_old2(&Map, vector_map, "", field_name) < 0) G_fatal_error(_("Unable to open vector map <%s>"), vector_map); field = Vect_get_field_number(&Map, field_name); if (field > 0) cat_list = Vect_cats_set_constraint(&Map, field, where, cats); if ((use == USE_Z) && !(Vect_is_3d(&Map))) G_fatal_error(_("Vector map <%s> is not 3D"), Vect_get_full_name(&Map)); switch (use) { case USE_ATTR: db_CatValArray_init(&cvarr); if (!(Fi = Vect_get_field(&Map, field))) G_fatal_error(_("Database connection not defined for layer <%s>"), field_name); if ((Driver = db_start_driver_open_database(Fi->driver, Fi->database)) == NULL) G_fatal_error(_("Unable to open database <%s> by driver <%s>"), Fi->database, Fi->driver); db_set_error_handler_driver(Driver); /* Note do not check if the column exists in the table because it may be expression */ if ((nrec = db_select_CatValArray(Driver, Fi->table, Fi->key, column, NULL, &cvarr)) == -1) G_fatal_error(_("Column <%s> not found"), column); G_debug(3, "nrec = %d", nrec); ctype = cvarr.ctype; if (ctype != DB_C_TYPE_INT && ctype != DB_C_TYPE_DOUBLE) G_fatal_error(_("Column type (%s) not supported (did you mean 'labelcolumn'?)"), db_sqltype_name(ctype)); if (nrec < 0) G_fatal_error(_("No records selected from table <%s>"), Fi->table); G_debug(1, "%d records selected from table", nrec); db_close_database_shutdown_driver(Driver); for (i = 0; i < cvarr.n_values; i++) { if (ctype == DB_C_TYPE_INT) { G_debug(3, "cat = %d val = %d", cvarr.value[i].cat, cvarr.value[i].val.i); } else if (ctype == DB_C_TYPE_DOUBLE) { G_debug(3, "cat = %d val = %f", cvarr.value[i].cat, cvarr.value[i].val.d); } } switch (ctype) { case DB_C_TYPE_INT: format = CELL_TYPE; break; case DB_C_TYPE_DOUBLE: format = DCELL_TYPE; break; default: G_fatal_error(_("Unable to use column <%s>"), column); break; } break; case USE_CAT: format = CELL_TYPE; break; case USE_VAL: format = value_type; break; case USE_Z: format = DCELL_TYPE; is_fp = 1; break; case USE_D: format = DCELL_TYPE; break; default: G_fatal_error(_("Unknown use type: %d"), use); } fd = Rast_open_new(raster_map, format); Points = Vect_new_line_struct(); if (use != USE_Z && use != USE_D && (ftype & GV_AREA)) { if ((nareas = sort_areas(&Map, Points, field, cat_list)) == 0) G_warning(_("No areas selected from vector map <%s>"), vector_map); G_debug(1, "%d areas sorted", nareas); } if (nareas > 0 && dense) { G_warning(_("Area conversion and line densification are mutually exclusive, disabling line densification.")); dense = 0; } nlines = Vect_get_num_primitives(&Map, ftype); nplines_all = nlines; npasses = begin_rasterization(cache_mb, format, dense); pass = 0; nareas_all = Vect_get_num_areas(&Map); do { pass++; if (npasses > 1) G_message(_("Pass %d of %d:"), pass, npasses); stat = 0; if ((use != USE_Z && use != USE_D) && nareas) { if (do_areas (&Map, Points, &cvarr, ctype, use, value, value_type) < 0) { G_warning(_("Problem processing areas from vector map <%s>, continuing..."), vector_map); stat = -1; break; } } if (nlines) { if ((nlines = do_lines(&Map, Points, &cvarr, ctype, field, cat_list, use, value, value_type, ftype, &nplines_all, dense)) < 0) { G_warning(_("Problem processing lines from vector map <%s>, continuing..."), vector_map); stat = -1; break; } } G_important_message(_("Writing raster map...")); stat = output_raster(fd); } while (stat == 0); G_suppress_warnings(0); /* stat: 0 == repeat; 1 == done; -1 == error; */ Vect_destroy_line_struct(Points); if (stat < 0) { Rast_unopen(fd); return 1; } Vect_close(&Map); G_verbose_message(_("Creating support files for raster map...")); Rast_close(fd); update_hist(raster_map, vector_map, Map.head.orig_scale); /* colors */ if (rgbcolumn) { if (use != USE_ATTR && use != USE_CAT) { G_warning(_("Color can be updated from database only if use=attr")); update_colors(raster_map); } else { update_dbcolors(raster_map, vector_map, field, rgbcolumn, is_fp, column); } } else if (use == USE_D) update_fcolors(raster_map); else update_colors(raster_map); update_cats(raster_map); /* labels */ update_labels(raster_map, vector_map, field, labelcolumn, use, value, column); #if 0 /* maximum possible numer of areas: number of centroids * actual number of areas, currently unknown: * number of areas with centroid that are within cat constraint * and overlap with current region */ if (nareas_all > 0) G_message(_("Converted areas: %d of %d"), nareas, Vect_get_num_primitives(&Map, GV_CENTROID)); /* maximum possible numer of lines: number of GV_LINE + GV_POINT * actual number of lines, currently unknown: * number of lines are within cat constraint * and overlap with current region */ if (nlines > 0 && nplines_all > 0) G_message(_("Converted points/lines: %d of %d"), nlines, nplines_all); #endif return 0; }