| 1 |
|
|---|
| 2 | /***************************************************************
|
|---|
| 3 | *
|
|---|
| 4 | * MODULE: v.to.points
|
|---|
| 5 | *
|
|---|
| 6 | * AUTHOR(S): Radim Blazek
|
|---|
| 7 | * OGR support by Martin Landa <landa.martin gmail.com>
|
|---|
| 8 | *
|
|---|
| 9 | * PURPOSE: Create points along lines
|
|---|
| 10 | *
|
|---|
| 11 | * COPYRIGHT: (C) 2002-2019 by the GRASS Development Team
|
|---|
| 12 | *
|
|---|
| 13 | * This program is free software under the GNU General
|
|---|
| 14 | * Public License (>=v2). Read the file COPYING that
|
|---|
| 15 | * comes with GRASS for details.
|
|---|
| 16 | *
|
|---|
| 17 | **************************************************************/
|
|---|
| 18 | #include <stdlib.h>
|
|---|
| 19 | #include <string.h>
|
|---|
| 20 | #include <math.h>
|
|---|
| 21 | #include <grass/gis.h>
|
|---|
| 22 | #include <grass/vector.h>
|
|---|
| 23 | #include <grass/dbmi.h>
|
|---|
| 24 | #include <grass/glocale.h>
|
|---|
| 25 |
|
|---|
| 26 | #include "local_proto.h"
|
|---|
| 27 |
|
|---|
| 28 | int main(int argc, char **argv)
|
|---|
| 29 | {
|
|---|
| 30 | int field, type, vertex_type;
|
|---|
| 31 | double dmax;
|
|---|
| 32 | char buf[DB_SQL_MAX];
|
|---|
| 33 |
|
|---|
| 34 | struct {
|
|---|
| 35 | struct Option *input, *output, *type, *dmax, *lfield, *use;
|
|---|
| 36 | } opt;
|
|---|
| 37 | struct {
|
|---|
| 38 | struct Flag *table, *inter, *percent, *reverse;
|
|---|
| 39 | } flag;
|
|---|
| 40 | struct GModule *module;
|
|---|
| 41 | struct Map_info In, Out;
|
|---|
| 42 | struct line_cats *LCats;
|
|---|
| 43 | struct line_pnts *LPoints;
|
|---|
| 44 |
|
|---|
| 45 | dbDriver *driver;
|
|---|
| 46 | struct field_info *Fi;
|
|---|
| 47 |
|
|---|
| 48 | dbString stmt;
|
|---|
| 49 |
|
|---|
| 50 | G_gisinit(argv[0]);
|
|---|
| 51 |
|
|---|
| 52 | module = G_define_module();
|
|---|
| 53 | G_add_keyword(_("vector"));
|
|---|
| 54 | G_add_keyword(_("geometry"));
|
|---|
| 55 | G_add_keyword("3D");
|
|---|
| 56 | G_add_keyword(_("line"));
|
|---|
| 57 | G_add_keyword(_("node"));
|
|---|
| 58 | G_add_keyword(_("vertex"));
|
|---|
| 59 | G_add_keyword(_("point"));
|
|---|
| 60 | module->description =
|
|---|
| 61 | _("Creates points along input lines in new vector map with 2 layers.");
|
|---|
| 62 |
|
|---|
| 63 | opt.input = G_define_standard_option(G_OPT_V_INPUT);
|
|---|
| 64 |
|
|---|
| 65 | opt.lfield = G_define_standard_option(G_OPT_V_FIELD);
|
|---|
| 66 | opt.lfield->label = "Line layer number or name";
|
|---|
| 67 | opt.lfield->guisection = _("Selection");
|
|---|
| 68 |
|
|---|
| 69 | opt.type = G_define_standard_option(G_OPT_V3_TYPE);
|
|---|
| 70 | opt.type->answer = "point,line,boundary,centroid,face";
|
|---|
| 71 | opt.type->guisection = _("Selection");
|
|---|
| 72 |
|
|---|
| 73 | opt.output = G_define_standard_option(G_OPT_V_OUTPUT);
|
|---|
| 74 |
|
|---|
| 75 | opt.use = G_define_option();
|
|---|
| 76 | opt.use->key = "use";
|
|---|
| 77 | opt.use->type = TYPE_STRING;
|
|---|
| 78 | opt.use->required = NO;
|
|---|
| 79 | opt.use->description = _("Use line nodes (start/end) or vertices only");
|
|---|
| 80 | opt.use->options = "node,start,end,vertex";
|
|---|
| 81 |
|
|---|
| 82 | opt.dmax = G_define_option();
|
|---|
| 83 | opt.dmax->key = "dmax";
|
|---|
| 84 | opt.dmax->type = TYPE_DOUBLE;
|
|---|
| 85 | opt.dmax->required = NO;
|
|---|
| 86 | opt.dmax->answer = "100";
|
|---|
| 87 | opt.dmax->description = _("Maximum distance between points in map units or percentage with -p");
|
|---|
| 88 |
|
|---|
| 89 | flag.inter = G_define_flag();
|
|---|
| 90 | flag.inter->key = 'i';
|
|---|
| 91 | flag.inter->description = _("Interpolate points between line vertices (only for use=vertex)");
|
|---|
| 92 |
|
|---|
| 93 | flag.percent = G_define_flag();
|
|---|
| 94 | flag.percent->key = 'p';
|
|---|
| 95 | flag.percent->description = _("Use dmax as percentage of line length");
|
|---|
| 96 |
|
|---|
| 97 | flag.reverse = G_define_flag();
|
|---|
| 98 | flag.reverse->key = 'r';
|
|---|
| 99 | flag.reverse->description = _("Start from the end node");
|
|---|
| 100 |
|
|---|
| 101 | flag.table = G_define_standard_flag(G_FLG_V_TABLE);
|
|---|
| 102 |
|
|---|
| 103 | if (G_parser(argc, argv))
|
|---|
| 104 | exit(EXIT_FAILURE);
|
|---|
| 105 |
|
|---|
| 106 | LCats = Vect_new_cats_struct();
|
|---|
| 107 | LPoints = Vect_new_line_struct();
|
|---|
| 108 | db_init_string(&stmt);
|
|---|
| 109 |
|
|---|
| 110 | type = Vect_option_to_types(opt.type);
|
|---|
| 111 | dmax = atof(opt.dmax->answer);
|
|---|
| 112 | if (dmax <= 0)
|
|---|
| 113 | G_fatal_error(_("Option %s must be positive"), opt.dmax->key);
|
|---|
| 114 |
|
|---|
| 115 | vertex_type = 0;
|
|---|
| 116 | if (opt.use->answer) {
|
|---|
| 117 | switch (opt.use->answer[0]) {
|
|---|
| 118 | case 'n':
|
|---|
| 119 | vertex_type = GV_NODE;
|
|---|
| 120 | break;
|
|---|
| 121 | case 's':
|
|---|
| 122 | vertex_type = GV_START;
|
|---|
| 123 | break;
|
|---|
| 124 | case 'e':
|
|---|
| 125 | vertex_type = GV_END;
|
|---|
| 126 | break;
|
|---|
| 127 | default:
|
|---|
| 128 | vertex_type = GV_VERTEX;
|
|---|
| 129 | break;
|
|---|
| 130 | }
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | if (flag.inter->answer && vertex_type != GV_VERTEX) {
|
|---|
| 134 | G_warning(_("Flag -%c ignored (requires %s=%s)"), flag.inter->key,
|
|---|
| 135 | opt.use->key, "vertex");
|
|---|
| 136 | flag.inter->answer = FALSE;
|
|---|
| 137 | }
|
|---|
| 138 | if (flag.reverse->answer && (vertex_type == GV_START || vertex_type == GV_END)) {
|
|---|
| 139 | G_warning(_("Flag -%c ignored (reason %s=%s)"), flag.reverse->key,
|
|---|
| 140 | opt.use->key, opt.use->answer);
|
|---|
| 141 | flag.reverse->answer = FALSE;
|
|---|
| 142 | }
|
|---|
| 143 | Vect_check_input_output_name(opt.input->answer, opt.output->answer,
|
|---|
| 144 | G_FATAL_EXIT);
|
|---|
| 145 |
|
|---|
| 146 | /* Open input lines */
|
|---|
| 147 | Vect_set_open_level(2);
|
|---|
| 148 |
|
|---|
| 149 | if (Vect_open_old2(&In, opt.input->answer, "", opt.lfield->answer) < 0)
|
|---|
| 150 | G_fatal_error(_("Unable to open vector map <%s>"), opt.input->answer);
|
|---|
| 151 |
|
|---|
| 152 | Vect_set_error_handler_io(&In, &Out);
|
|---|
| 153 |
|
|---|
| 154 | field = Vect_get_field_number(&In, opt.lfield->answer);
|
|---|
| 155 |
|
|---|
| 156 | /* Open output segments */
|
|---|
| 157 | if (Vect_open_new(&Out, opt.output->answer, Vect_is_3d(&In)) < 0)
|
|---|
| 158 | G_fatal_error(_("Unable to create vector map <%s>"),
|
|---|
| 159 | opt.output->answer);
|
|---|
| 160 |
|
|---|
| 161 | Vect_copy_head_data(&In, &Out);
|
|---|
| 162 | Vect_hist_copy(&In, &Out);
|
|---|
| 163 | Vect_hist_command(&Out);
|
|---|
| 164 |
|
|---|
| 165 | /* Table */
|
|---|
| 166 | driver = NULL;
|
|---|
| 167 | Fi = NULL;
|
|---|
| 168 | if (!flag.table->answer) {
|
|---|
| 169 | struct field_info *Fin;
|
|---|
| 170 |
|
|---|
| 171 | /* copy input table */
|
|---|
| 172 | Fin = Vect_get_field(&In, field);
|
|---|
| 173 | if (Fin) { /* table defined */
|
|---|
| 174 | int ret;
|
|---|
| 175 |
|
|---|
| 176 | Fi = Vect_default_field_info(&Out, 1, NULL, GV_MTABLE);
|
|---|
| 177 | Vect_map_add_dblink(&Out, 1, NULL, Fi->table, Fin->key,
|
|---|
| 178 | Fi->database, Fi->driver);
|
|---|
| 179 |
|
|---|
| 180 | ret = db_copy_table(Fin->driver, Fin->database, Fin->table,
|
|---|
| 181 | Fi->driver, Vect_subst_var(Fi->database,
|
|---|
| 182 | &Out), Fi->table);
|
|---|
| 183 |
|
|---|
| 184 | if (ret == DB_FAILED) {
|
|---|
| 185 | G_fatal_error(_("Unable to copy table <%s>"),
|
|---|
| 186 | Fin->table);
|
|---|
| 187 | }
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | Fi = Vect_default_field_info(&Out, 2, NULL, GV_MTABLE);
|
|---|
| 191 | Vect_map_add_dblink(&Out, 2, NULL, Fi->table, GV_KEY_COLUMN, Fi->database,
|
|---|
| 192 | Fi->driver);
|
|---|
| 193 |
|
|---|
| 194 | /* Open driver */
|
|---|
| 195 | driver = db_start_driver_open_database(Fi->driver, Fi->database);
|
|---|
| 196 | if (driver == NULL)
|
|---|
| 197 | G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
|
|---|
| 198 | Fi->database, Fi->driver);
|
|---|
| 199 | db_set_error_handler_driver(driver);
|
|---|
| 200 |
|
|---|
| 201 | if (field == -1)
|
|---|
| 202 | sprintf(buf,
|
|---|
| 203 | "create table %s ( cat int, along double precision )",
|
|---|
| 204 | Fi->table);
|
|---|
| 205 | else
|
|---|
| 206 | sprintf(buf,
|
|---|
| 207 | "create table %s ( cat int, lcat int, along double precision )",
|
|---|
| 208 | Fi->table);
|
|---|
| 209 | db_append_string(&stmt, buf);
|
|---|
| 210 |
|
|---|
| 211 | if (db_execute_immediate(driver, &stmt) != DB_OK) {
|
|---|
| 212 | G_fatal_error(_("Unable to create table: '%s'"),
|
|---|
| 213 | db_get_string(&stmt));
|
|---|
| 214 | }
|
|---|
| 215 |
|
|---|
| 216 | if (db_create_index2(driver, Fi->table, GV_KEY_COLUMN) != DB_OK)
|
|---|
| 217 | G_warning(_("Unable to create index for table <%s>, key <%s>"),
|
|---|
| 218 | Fi->table, GV_KEY_COLUMN);
|
|---|
| 219 |
|
|---|
| 220 | if (db_grant_on_table (driver, Fi->table, DB_PRIV_SELECT,
|
|---|
| 221 | DB_GROUP | DB_PUBLIC) != DB_OK)
|
|---|
| 222 | G_fatal_error(_("Unable to grant privileges on table <%s>"),
|
|---|
| 223 | Fi->table);
|
|---|
| 224 |
|
|---|
| 225 | db_begin_transaction(driver);
|
|---|
| 226 | }
|
|---|
| 227 |
|
|---|
| 228 | if (type & (GV_POINTS | GV_LINES | GV_FACE)) {
|
|---|
| 229 | int line, nlines, nskipped;
|
|---|
| 230 |
|
|---|
| 231 | nskipped = 0;
|
|---|
| 232 | nlines = Vect_get_num_lines(&In);
|
|---|
| 233 | for (line = 1; line <= nlines; line++) {
|
|---|
| 234 | int ltype, cat;
|
|---|
| 235 |
|
|---|
| 236 | G_debug(3, "line = %d", line);
|
|---|
| 237 | G_percent(line, nlines, 2);
|
|---|
| 238 |
|
|---|
| 239 | ltype = Vect_read_line(&In, LPoints, LCats, line);
|
|---|
| 240 | if (!(ltype & type))
|
|---|
| 241 | continue;
|
|---|
| 242 | if (!Vect_cat_get(LCats, field, &cat) && field != -1) {
|
|---|
| 243 | nskipped++;
|
|---|
| 244 | continue;
|
|---|
| 245 | }
|
|---|
| 246 |
|
|---|
| 247 | /* Assign CAT for layer 0 objects (i.e. boundaries) */
|
|---|
| 248 | if (field == -1)
|
|---|
| 249 | cat = -1;
|
|---|
| 250 |
|
|---|
| 251 | if (LPoints->n_points <= 1) {
|
|---|
| 252 | write_point(&Out, LPoints->x[0], LPoints->y[0], LPoints->z[0],
|
|---|
| 253 | cat, 0.0, driver, Fi);
|
|---|
| 254 | }
|
|---|
| 255 | else if (flag.percent->answer) { /* lines */
|
|---|
| 256 | double dmaxlen = Vect_line_length(LPoints) * dmax / 100.0;
|
|---|
| 257 | write_line(&Out, LPoints, cat, vertex_type, flag.inter->answer,
|
|---|
| 258 | flag.reverse->answer, dmaxlen, driver, Fi);
|
|---|
| 259 | } else {
|
|---|
| 260 | write_line(&Out, LPoints, cat, vertex_type, flag.inter->answer,
|
|---|
| 261 | flag.reverse->answer, dmax, driver, Fi);
|
|---|
| 262 | }
|
|---|
| 263 | }
|
|---|
| 264 |
|
|---|
| 265 | if (nskipped > 0)
|
|---|
| 266 | G_warning(_("%d features without category in layer <%d> skipped. "
|
|---|
| 267 | "Note that features without category (usually boundaries) are not "
|
|---|
| 268 | "skipped when '%s=-1' is given."),
|
|---|
| 269 | nskipped, field, opt.lfield->key);
|
|---|
| 270 | }
|
|---|
| 271 |
|
|---|
| 272 | if (type == GV_AREA) {
|
|---|
| 273 | int area, nareas, centroid, cat;
|
|---|
| 274 |
|
|---|
| 275 | nareas = Vect_get_num_areas(&In);
|
|---|
| 276 | for (area = 1; area <= nareas; area++) {
|
|---|
| 277 | int i, isle, nisles;
|
|---|
| 278 |
|
|---|
| 279 | G_percent(area, nareas, 2);
|
|---|
| 280 |
|
|---|
| 281 | centroid = Vect_get_area_centroid(&In, area);
|
|---|
| 282 | cat = -1;
|
|---|
| 283 | if (centroid > 0) {
|
|---|
| 284 | Vect_read_line(&In, NULL, LCats, centroid);
|
|---|
| 285 | if (!Vect_cat_get(LCats, field, &cat))
|
|---|
| 286 | continue;
|
|---|
| 287 | }
|
|---|
| 288 |
|
|---|
| 289 | Vect_get_area_points(&In, area, LPoints);
|
|---|
| 290 |
|
|---|
| 291 | if (flag.percent->answer) {
|
|---|
| 292 | double dmaxlen = Vect_line_length(LPoints) * dmax / 100.0;
|
|---|
| 293 | write_line(&Out, LPoints, cat, vertex_type, flag.inter->answer,
|
|---|
| 294 | flag.reverse->answer, dmaxlen, driver, Fi);
|
|---|
| 295 | } else {
|
|---|
| 296 | write_line(&Out, LPoints, cat, vertex_type, flag.inter->answer,
|
|---|
| 297 | flag.reverse->answer, dmax, driver, Fi);
|
|---|
| 298 | }
|
|---|
| 299 |
|
|---|
| 300 | nisles = Vect_get_area_num_isles(&In, area);
|
|---|
| 301 |
|
|---|
| 302 | for (i = 0; i < nisles; i++) {
|
|---|
| 303 | isle = Vect_get_area_isle(&In, area, i);
|
|---|
| 304 | Vect_get_isle_points(&In, isle, LPoints);
|
|---|
| 305 |
|
|---|
| 306 | if (flag.percent->answer) {
|
|---|
| 307 | double dmaxlen = Vect_line_length(LPoints) * dmax / 100.0;
|
|---|
| 308 | write_line(&Out, LPoints, cat, vertex_type,
|
|---|
| 309 | flag.inter->answer, flag.reverse->answer,
|
|---|
| 310 | dmaxlen, driver, Fi);
|
|---|
| 311 | } else {
|
|---|
| 312 | write_line(&Out, LPoints, cat, vertex_type,
|
|---|
| 313 | flag.inter->answer, flag.reverse->answer, dmax,
|
|---|
| 314 | driver, Fi);
|
|---|
| 315 | }
|
|---|
| 316 | }
|
|---|
| 317 | }
|
|---|
| 318 | }
|
|---|
| 319 |
|
|---|
| 320 | if (!flag.table->answer) {
|
|---|
| 321 | db_commit_transaction(driver);
|
|---|
| 322 | db_close_database_shutdown_driver(driver);
|
|---|
| 323 | }
|
|---|
| 324 |
|
|---|
| 325 | Vect_build(&Out);
|
|---|
| 326 |
|
|---|
| 327 | /* Free, close ... */
|
|---|
| 328 | Vect_close(&In);
|
|---|
| 329 |
|
|---|
| 330 | G_done_msg(_("%d points written to output vector map."),
|
|---|
| 331 | Vect_get_num_primitives(&Out, GV_POINT));
|
|---|
| 332 |
|
|---|
| 333 | Vect_close(&Out);
|
|---|
| 334 |
|
|---|
| 335 | exit(EXIT_SUCCESS);
|
|---|
| 336 | }
|
|---|