1 /***********************************************************************
2 *
3 * MODULE: v.out.lidar
4 *
5 * AUTHOR(S): Vaclav Petras
6 *
7 * PURPOSE: Export LiDAR LAS points
8 *
9 * COPYRIGHT: (C) 2015 by Vaclav Petras and the GRASS Development Team
10 *
11 * This program is free software under the
12 * GNU General Public License (>=v2).
13 * Read the file COPYING that comes with GRASS
14 * for details.
15 *
16 ***********************************************************************/
17
18 #include <stdlib.h>
19
20 #include <grass/gis.h>
21 #include <grass/colors.h>
22 #include <grass/raster.h>
23 #include <grass/dbmi.h>
24 #include <grass/vector.h>
25 #include <grass/gprojects.h>
26 #include <grass/glocale.h>
27
28 #include <liblas/capi/liblas.h>
29
30 #define LAS_FIRST 1
31 #define LAS_MID 2
32 #define LAS_LAST 3
33
34 struct WriteContext
35 {
36 LASWriterH las_writer;
37 LASPointH las_point;
38 LASColorH las_color;
39 struct Colors *color_table;
40 int layer;
41 int return_layer;
42 int class_layer;
43 int rgb_layer;
44 dbCatValArray *return_column_values;
45 dbCatValArray *n_returns_column_values;
46 dbCatValArray *class_column_values;
47 dbCatValArray *grass_rgb_column_values;
48 dbCatValArray *red_column_values;
49 dbCatValArray *green_column_values;
50 dbCatValArray *blue_column_values;
51 };
52
53
54 struct LidarColumnNames
55 {
56 const char *return_n;
57 const char *n_returns;
58 const char *class_n;
59 const char *grass_rgb;
60 const char *red;
61 const char *green;
62 const char *blue;
63 };
64
65
66 /*! Open database and store driver and field info
67 *
68 * Use close_database() when you are finished with queries
69 */
open_database(struct Map_info * vector,int field,dbDriver ** driver,struct field_info ** f_info)70 static void open_database(struct Map_info *vector, int field,
71 dbDriver ** driver, struct field_info **f_info)
72 {
73 struct field_info *f_info_tmp = Vect_get_field(vector, field);
74 if (f_info_tmp == NULL) {
75 /* not ideal message since we don't know the original name of
76 * the field in case of OGR */
77 G_fatal_error(_("Database connection not defined for layer <%d>"),
78 field);
79 }
80
81 dbDriver *driver_tmp =
82 db_start_driver_open_database(f_info_tmp->driver,
83 f_info_tmp->database);
84 if (driver_tmp == NULL)
85 G_fatal_error("Unable to open database <%s> by driver <%s>",
86 f_info_tmp->database, f_info_tmp->driver);
87 db_set_error_handler_driver(driver_tmp);
88 *f_info = f_info_tmp;
89 *driver = driver_tmp;
90 }
91
close_database(dbDriver * driver)92 static void close_database(dbDriver * driver)
93 {
94 db_close_database_shutdown_driver(driver);
95 }
96
97 /*! Get values in a column
98 *
99 * Checks the type of the column; fails with fatal for non-numeric columns
100 * and warns for floating point columns.
101 *
102 * Use db_CatValArray_free() and G_free() to deallocate the memory.
103 *
104 * \returns cat-value array with column values for each category
105 */
select_integers_from_database(dbDriver * driver,struct field_info * f_info,const char * column,const char * where)106 static dbCatValArray *select_integers_from_database(dbDriver * driver,
107 struct field_info *f_info,
108 const char *column,
109 const char *where)
110 {
111 G_debug(1, "select_integers_from_database: column=%s", column);
112 dbCatValArray *column_values = G_malloc(sizeof(dbCatValArray));
113
114 /* check if column exists */
115 int ctype = db_column_Ctype(driver, f_info->table, column);
116
117 if (ctype == -1)
118 G_fatal_error(_("Column <%s> not found in table <%s>"),
119 column, f_info->table);
120 if (ctype != DB_C_TYPE_INT && ctype != DB_C_TYPE_DOUBLE)
121 G_fatal_error(_("Only numeric column type is supported (column <%s> in table <%s>)"),
122 column, f_info->table);
123 if (ctype == DB_C_TYPE_DOUBLE)
124 G_warning(_("Double values will be converted to integers (column <%s> in table <%s>)"),
125 column, f_info->table);
126
127 db_CatValArray_init(column_values);
128 int nrec =
129 db_select_CatValArray(driver, f_info->table, f_info->key, column,
130 where, column_values);
131
132 G_debug(2, "db_select_CatValArray() nrec = %d", nrec);
133 if (nrec < 0)
134 G_fatal_error(_("Unable to select data from table"));
135 return column_values;
136 }
137
138 /*! Get values in a column
139 *
140 * Checks the type of the column; fails with fatal for non-numeric columns
141 * and warns for floating point columns.
142 *
143 * \returns cat-value array with column values for each category
144 */
select_strings_from_database(dbDriver * driver,struct field_info * f_info,const char * column,const char * where)145 static dbCatValArray *select_strings_from_database(dbDriver * driver,
146 struct field_info *f_info,
147 const char *column,
148 const char *where)
149 {
150 G_debug(1, "select_strings_from_database: column=%s", column);
151 dbCatValArray *column_values = G_malloc(sizeof(dbCatValArray));
152
153 /* check if column exists */
154 int ctype = db_column_Ctype(driver, f_info->table, column);
155
156 if (ctype == -1)
157 G_fatal_error(_("Column <%s> not found in table <%s>"),
158 column, f_info->table);
159 if (ctype != DB_C_TYPE_STRING)
160 G_fatal_error(_("Only numeric column type is supported"));
161
162 db_CatValArray_init(column_values);
163 int nrec =
164 db_select_CatValArray(driver, f_info->table, f_info->key, column,
165 where, column_values);
166
167 G_debug(2, "db_select_CatValArray() nrec = %d", nrec);
168 if (nrec < 0)
169 G_fatal_error(_("Unable to select data from table"));
170 return column_values;
171 }
172
173 /*! Get integer value in a column for a category
174 *
175 * Floating point numbers are casted to integers.
176 * If the column is not numerical, fatal error is issued.
177 *
178 * \returns The value of the column as an integer
179 */
get_integer_column_value(dbCatValArray * column_values,int cat)180 static int get_integer_column_value(dbCatValArray * column_values, int cat)
181 {
182 int val;
183 dbCatVal *catval;
184
185 if (db_CatValArray_get_value(column_values, cat, &catval) != DB_OK) {
186 G_fatal_error(_("No record for cat = %d"), cat);
187 }
188 if (catval->isNull) {
189 G_fatal_error(_("NULL value for cat = %d"), cat);
190 }
191
192 if (column_values->ctype == DB_C_TYPE_INT) {
193 val = catval->val.i;
194 }
195 else if (column_values->ctype == DB_C_TYPE_DOUBLE) {
196 val = catval->val.d;
197 } else {
198 G_fatal_error(_("Column type is not numeric (type = %d, cat = %d"),
199 column_values->ctype, cat);
200 }
201 return val;
202 }
203
204 /*! Get RGB in a column for a category as three integers
205 *
206 * Expects the column to be a string.
207 */
get_color_column_value(dbCatValArray * cvarr,int cat,int * red,int * green,int * blue)208 static void get_color_column_value(dbCatValArray * cvarr, int cat,
209 int *red, int *green, int *blue)
210 {
211 char colorstring[12]; /* RRR:GGG:BBB */
212 dbCatVal *value = NULL;
213
214 /* read RGB colors from db for current area # */
215 if (cvarr && db_CatValArray_get_value(cvarr, cat, &value) == DB_OK) {
216 sprintf(colorstring, "%s", db_get_string(value->val.s));
217 if (*colorstring != '\0') {
218 G_debug(5, "element: colorstring: %s", colorstring);
219 if (G_str_to_color(colorstring, red, green, blue) == 1) {
220 G_debug(5, "element: cat %d r:%d g:%d b:%d",
221 cat, *red, *green, *blue);
222 /* TODO: handle return code 2 for none? */
223 }
224 else {
225 G_debug(5, "Invalid color definition '%s' ignored",
226 colorstring);
227 }
228 }
229 else {
230 G_debug(5, "Invalid color definition '%s' ignored", colorstring);
231 }
232 }
233 }
234
load_columns(struct WriteContext * write_context,dbDriver * db_driver,struct field_info * f_info,struct LidarColumnNames * columns,const char * where)235 static void load_columns(struct WriteContext *write_context,
236 dbDriver * db_driver, struct field_info *f_info,
237 struct LidarColumnNames *columns, const char *where)
238 {
239 if (columns->return_n) {
240 write_context->return_column_values =
241 select_integers_from_database(db_driver, f_info,
242 columns->return_n, where);
243 }
244 if (columns->n_returns) {
245 write_context->n_returns_column_values =
246 select_integers_from_database(db_driver, f_info,
247 columns->n_returns, where);
248 }
249 if (columns->class_n) {
250 write_context->class_column_values =
251 select_integers_from_database(db_driver, f_info,
252 columns->class_n, where);
253 }
254 if (columns->grass_rgb) {
255 write_context->grass_rgb_column_values =
256 select_strings_from_database(db_driver, f_info,
257 columns->grass_rgb, where);
258 }
259 if (columns->red) {
260 write_context->red_column_values =
261 select_integers_from_database(db_driver, f_info,
262 columns->red, where);
263 }
264 if (columns->green) {
265 write_context->green_column_values =
266 select_integers_from_database(db_driver, f_info,
267 columns->green, where);
268 }
269 if (columns->blue) {
270 write_context->blue_column_values =
271 select_integers_from_database(db_driver, f_info,
272 columns->blue, where);
273 }
274 }
275
276 /*! Deallocate the memory for cat-value arrays */
free_columns(struct WriteContext * write_context)277 static void free_columns(struct WriteContext *write_context)
278 {
279 if (write_context->return_column_values) {
280 db_CatValArray_free(write_context->return_column_values);
281 G_free(write_context->return_column_values);
282 }
283 if (write_context->n_returns_column_values) {
284 db_CatValArray_free(write_context->n_returns_column_values);
285 G_free(write_context->n_returns_column_values);
286 }
287 if (write_context->class_column_values) {
288 db_CatValArray_free(write_context->class_column_values);
289 G_free(write_context->class_column_values);
290 }
291 if (write_context->grass_rgb_column_values) {
292 db_CatValArray_free(write_context->grass_rgb_column_values);
293 G_free(write_context->grass_rgb_column_values);
294 }
295 if (write_context->red_column_values) {
296 db_CatValArray_free(write_context->red_column_values);
297 G_free(write_context->red_column_values);
298 }
299 if (write_context->green_column_values) {
300 db_CatValArray_free(write_context->green_column_values);
301 G_free(write_context->green_column_values);
302 }
303 if (write_context->blue_column_values) {
304 db_CatValArray_free(write_context->blue_column_values);
305 G_free(write_context->blue_column_values);
306 }
307 }
308
309 /*! Set point attributes from the attribute table
310 *
311 * All tables are taken from the context structure. The point and color
312 * are also taken from there.
313 */
set_point_attributes_from_table(struct WriteContext * context,int cat)314 static void set_point_attributes_from_table(struct WriteContext *context,
315 int cat)
316 {
317 LASPointH las_point = context->las_point;
318
319 if (context->return_column_values) {
320 int return_n =
321 get_integer_column_value(context->return_column_values, cat);
322 LASPoint_SetReturnNumber(las_point, return_n);
323 }
324 if (context->n_returns_column_values) {
325 int val =
326 get_integer_column_value(context->n_returns_column_values, cat);
327 LASPoint_SetNumberOfReturns(las_point, val);
328 }
329 if (context->class_column_values) {
330 int val = get_integer_column_value(context->class_column_values, cat);
331
332 LASPoint_SetClassification(las_point, val);
333 }
334 if (context->grass_rgb_column_values || context->red_column_values ||
335 context->green_column_values || context->blue_column_values) {
336 LASColorH las_color = context->las_color;
337
338 if (context->grass_rgb_column_values) {
339 int red, green, blue;
340
341 get_color_column_value(context->grass_rgb_column_values, cat,
342 &red, &green, &blue);
343 LASColor_SetRed(las_color, red);
344 LASColor_SetGreen(las_color, green);
345 LASColor_SetBlue(las_color, blue);
346 }
347 if (context->red_column_values) {
348 int val =
349 get_integer_column_value(context->red_column_values, cat);
350 LASColor_SetRed(las_color, val);
351 }
352 if (context->green_column_values) {
353 int val =
354 get_integer_column_value(context->green_column_values, cat);
355 LASColor_SetGreen(las_color, val);
356 }
357 if (context->blue_column_values) {
358 int val =
359 get_integer_column_value(context->blue_column_values, cat);
360 LASColor_SetBlue(las_color, val);
361 }
362 LASPoint_SetColor(las_point, las_color);
363 }
364 }
365
write_point(struct WriteContext * context,int cat,double x,double y,double z,struct line_cats * cats)366 static void write_point(struct WriteContext *context, int cat, double x,
367 double y, double z, struct line_cats *cats)
368 {
369 LASPointH las_point = context->las_point;
370
371 LASPoint_SetX(las_point, x);
372 LASPoint_SetY(las_point, y);
373 LASPoint_SetZ(las_point, z);
374
375 /* only call when we actually using the attributes */
376 if (context->layer > 0)
377 set_point_attributes_from_table(context, cat);
378 /* after this point cat is used as a short term variable
379 * to store category to retrieve attributes */
380
381 /* read color table */
382 if (context->color_table) {
383 int red, green, blue;
384 LASColorH las_color = context->las_color;
385 if (Rast_get_c_color(&cat, &red, &green, &blue, context->color_table) == 1) {
386 LASColor_SetRed(las_color, red);
387 LASColor_SetGreen(las_color, green);
388 LASColor_SetBlue(las_color, blue);
389 LASPoint_SetColor(las_point, las_color);
390 }
391 /* TODO: what else, fail, skip or put some defaults? */
392 }
393
394 if (context->return_layer) {
395 if (!Vect_cat_get(cats, context->return_layer, &cat))
396 return; /* TODO: is this an error? */
397 if (cat == LAS_FIRST) {
398 LASPoint_SetReturnNumber(las_point, LAS_FIRST);
399 LASPoint_SetNumberOfReturns(las_point, LAS_FIRST);
400 } else if (cat == LAS_LAST) {
401 LASPoint_SetReturnNumber(las_point, LAS_LAST);
402 LASPoint_SetNumberOfReturns(las_point, LAS_LAST);
403 } else {
404 LASPoint_SetReturnNumber(las_point, LAS_MID);
405 LASPoint_SetNumberOfReturns(las_point, LAS_LAST);
406 }
407 }
408 if (context->class_layer) {
409 if (!Vect_cat_get(cats, context->class_layer, &cat))
410 return; /* TODO: is this an error? */
411 LASPoint_SetClassification(las_point, cat);
412 }
413 if (context->rgb_layer) {
414 LASColorH las_color = context->las_color;
415
416 /* TODO: defaults for the color are what? */
417 /* TODO: check the range for RGB? */
418 if (context->rgb_layer) {
419 if (!Vect_cat_get(cats, context->rgb_layer, &cat))
420 return; /* TODO: is this an error? */
421 /* cat 0 is not valid, so we are adding 1 when storing
422 * now we need to subtract 1 */
423 int rgb = cat - 1;
424 int red = (rgb >> 16) & 0xFF;
425 int green = (rgb >> 8) & 0xFF;
426 int blue = rgb & 0xFF;
427
428 LASColor_SetRed(las_color, red);
429 LASColor_SetGreen(las_color, green);
430 LASColor_SetBlue(las_color, blue);
431 } /* TODO: else all the others? */
432 LASPoint_SetColor(las_point, las_color);
433 }
434
435 LASError error = LASWriter_WritePoint(context->las_writer, las_point);
436
437 if (error)
438 G_fatal_error("Failure when writing a point");
439 }
440
441
442 /* TODO: these have overlap with vector lib, really needed? */
point_in_region_2d(struct Cell_head * region,double x,double y)443 static int point_in_region_2d(struct Cell_head *region, double x, double y)
444 {
445 if (x > region->east || x < region->west || y < region->south ||
446 y > region->north)
447 return FALSE;
448 return TRUE;
449 }
450
451
main(int argc,char ** argv)452 int main(int argc, char **argv)
453 {
454 struct GModule *module;
455 struct Option *map_opt, *foutput_opt;
456 struct Option *field_opt, *cats_opt;
457 struct Option *id_layer_opt;
458 struct Option *return_layer_opt;
459 struct Option *class_layer_opt;
460 struct Option *rgb_layer_opt;
461 struct Option *return_column_opt, *n_returns_column_opt;
462 struct Option *class_column_opt;
463 struct Option *grass_rgb_column_opt;
464 struct Option *red_column_opt, *green_column_opt, *blue_column_opt;
465 struct Option *where_opt;
466 struct Option *las_xyscale_opt, *las_zscale_opt;
467 struct Flag *region_flag, *no_color_table_flag;
468 struct Map_info vinput;
469
470 G_gisinit(argv[0]);
471
472 module = G_define_module();
473 G_add_keyword(_("vector"));
474 G_add_keyword(_("export"));
475 G_add_keyword(_("output"));
476 G_add_keyword(_("LIDAR"));
477 G_add_keyword(_("points"));
478 module->label = _("Exports vector points as LAS point cloud");
479 module->description = _("Converts LAS LiDAR point clouds to a GRASS"
480 " vector map with libLAS");
481
482 map_opt = G_define_standard_option(G_OPT_V_INPUT);
483
484 field_opt = G_define_standard_option(G_OPT_V_FIELD_ALL);
485 field_opt->required = NO;
486
487 foutput_opt = G_define_standard_option(G_OPT_F_OUTPUT);
488
489 cats_opt = G_define_standard_option(G_OPT_V_CATS);
490 cats_opt->guisection = _("Selection");
491
492 /* TODO: supported only when attributes are actually used */
493 where_opt = G_define_standard_option(G_OPT_DB_WHERE);
494 where_opt->guisection = _("Selection");
495
496 id_layer_opt = G_define_standard_option(G_OPT_V_FIELD);
497 id_layer_opt->key = "id_layer";
498 id_layer_opt->label =
499 _("Layer number to store generated point ID as category");
500 id_layer_opt->answer = NULL;
501 id_layer_opt->guisection = _("Categories");
502
503 return_layer_opt = G_define_standard_option(G_OPT_V_FIELD);
504 return_layer_opt->key = "return_layer";
505 return_layer_opt->label =
506 _("Layer number to store return number as category");
507 return_layer_opt->answer = NULL;
508 return_layer_opt->guisection = _("Categories");
509
510 class_layer_opt = G_define_standard_option(G_OPT_V_FIELD);
511 class_layer_opt->key = "class_layer";
512 class_layer_opt->label =
513 _("Layer number to store class number as category");
514 class_layer_opt->answer = NULL;
515 class_layer_opt->guisection = _("Categories");
516
517 rgb_layer_opt = G_define_standard_option(G_OPT_V_FIELD);
518 rgb_layer_opt->key = "rgb_layer";
519 rgb_layer_opt->label =
520 _("Layer number where RGB color is stored as category");
521 rgb_layer_opt->answer = NULL;
522 rgb_layer_opt->guisection = _("Categories");
523
524 /* TODO: probably replace the option by standardized/expected column names */
525
526 return_column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
527 return_column_opt->key = "return_column";
528 return_column_opt->label = _("Column with return number");
529 return_column_opt->required = NO;
530 return_column_opt->guisection = _("Columns");
531
532 n_returns_column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
533 n_returns_column_opt->key = "n_returns_column";
534 n_returns_column_opt->label = _("Column with return number");
535 n_returns_column_opt->required = NO;
536 n_returns_column_opt->guisection = _("Columns");
537
538 class_column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
539 class_column_opt->key = "class_column";
540 class_column_opt->label = _("Column with return number");
541 class_column_opt->required = NO;
542 class_column_opt->guisection = _("Columns");
543
544 grass_rgb_column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
545 grass_rgb_column_opt->key = "rgb_column";
546 grass_rgb_column_opt->label = _("RGB color definition column");
547 grass_rgb_column_opt->description = _("Color definition in R:G:B form");
548 grass_rgb_column_opt->required = NO;
549 grass_rgb_column_opt->guisection = _("Columns");
550
551 red_column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
552 red_column_opt->key = "red_column";
553 red_column_opt->label = _("Column with red color");
554 red_column_opt->required = NO;
555 red_column_opt->guisection = _("Columns");
556
557 green_column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
558 green_column_opt->key = "green_column";
559 green_column_opt->label = _("Column with green color");
560 green_column_opt->required = NO;
561 green_column_opt->guisection = _("Columns");
562
563 blue_column_opt = G_define_standard_option(G_OPT_DB_COLUMN);
564 blue_column_opt->key = "blue_column";
565 blue_column_opt->label = _("Column with blue color");
566 blue_column_opt->required = NO;
567 blue_column_opt->guisection = _("Columns");
568
569 las_xyscale_opt = G_define_option();
570 las_xyscale_opt->key = "las_xyscale";
571 las_xyscale_opt->type = TYPE_DOUBLE;
572 las_xyscale_opt->required = YES;
573 las_xyscale_opt->answer = "0.01";
574 las_xyscale_opt->label = _("Internal scale to apply to X and Y values");
575 las_xyscale_opt->description = _("This scale does not change"
576 " the values itself but only how precisely they are stored,"
577 " for example 0.01 will preserve two decimal places");
578
579 las_zscale_opt = G_define_option();
580 las_zscale_opt->key = "las_zscale";
581 las_zscale_opt->type = TYPE_DOUBLE;
582 las_zscale_opt->required = YES;
583 las_zscale_opt->answer = "0.01";
584 las_zscale_opt->label = _("Internal scale to apply to z values");
585 las_zscale_opt->description = _("This scale does not change"
586 " the values itself but only how precisely they are stored,"
587 " for example 0.01 will preserve two decimal places");
588
589 region_flag = G_define_flag();
590 region_flag->key = 'r';
591 region_flag->guisection = _("Selection");
592 region_flag->description = _("Limit export to the current region");
593
594 no_color_table_flag = G_define_flag();
595 no_color_table_flag->key = 'w';
596 no_color_table_flag->label = _("Ignore color table");
597 no_color_table_flag->description =
598 _("Ignore color table even when set and not other options are present");
599
600
601 if (G_parser(argc, argv))
602 exit(EXIT_FAILURE);
603
604 /* TODO: layer required > 0 with columns */
605
606 /* TODO: do some check */
607 /*Vect_check_input_output_name(map_opt->answer, voutput_opt->answer,
608 G_FATAL_EXIT);
609 */
610
611 if (Vect_open_old2(&vinput, map_opt->answer, "", field_opt->answer) < 0)
612 G_fatal_error(_("Unable to open vector map <%s>"), map_opt->answer);
613 int layer = Vect_get_field_number(&vinput, field_opt->answer);
614
615 struct cat_list *allowed_cats = NULL;
616
617 if (layer > 0)
618 allowed_cats = Vect_cats_set_constraint(&vinput, layer, NULL,
619 cats_opt->answer);
620
621 struct line_pnts *line = Vect_new_line_struct();
622 struct line_cats *cats = Vect_new_cats_struct();
623
624 struct Cell_head comp_region;
625
626 G_get_window(&comp_region);
627
628 struct WriteContext write_context;
629
630 write_context.return_layer = 0;
631 write_context.class_layer = 0;
632 write_context.rgb_layer = 0;
633 if (return_layer_opt->answer)
634 write_context.return_layer = atoi(return_layer_opt->answer);
635 if (class_layer_opt->answer)
636 write_context.class_layer = atoi(class_layer_opt->answer);
637 if (rgb_layer_opt->answer)
638 write_context.rgb_layer = atoi(rgb_layer_opt->answer);
639
640 /* get GRASS loc proj info */
641 struct Key_Value *proj_info;
642 struct Key_Value *proj_units;
643
644 /* TODO: should we test for PROJECTION_XY? */
645 proj_info = G_get_projinfo();
646 proj_units = G_get_projunits();
647 char *current_wkt = GPJ_grass_to_wkt(proj_info, proj_units, FALSE, FALSE);
648
649 G_free_key_value(proj_info);
650 G_free_key_value(proj_units);
651
652 /* TODO: ignoring errors */
653 LASWriterH las_writer;
654 LASHeaderH las_header = LASHeader_Create();
655 LASSRSH las_srs = LASSRS_Create();
656
657 LASSRS_SetWKT(las_srs, current_wkt);
658 LASHeader_SetSRS(las_header, las_srs);
659 LASHeader_SetScale(las_header, atof(las_xyscale_opt->answer),
660 atof(las_xyscale_opt->answer), atof(las_zscale_opt->answer));
661 /* TODO: support append mode */
662 int write_mode = 1;
663
664 las_writer = LASWriter_Create(foutput_opt->answer,
665 las_header, write_mode);
666 write_context.las_writer = las_writer;
667
668 /* to avoid allocation for each point we are writing */
669 write_context.las_point = LASPoint_Create();
670 LASPoint_SetHeader(write_context.las_point, las_header);
671 write_context.las_color = LASColor_Create();
672
673 write_context.layer = layer;
674 write_context.return_column_values = 0;
675 write_context.n_returns_column_values = 0;
676 write_context.class_column_values = 0;
677 write_context.grass_rgb_column_values = 0;
678 write_context.red_column_values = 0;
679 write_context.green_column_values = 0;
680 write_context.blue_column_values = 0;
681 /* TODO: limit select by the cat values */
682 /* TODO: limit select by 2D/3D region and zrange, possible? */
683
684 int use_color_attributes = FALSE;
685
686 if (return_column_opt->answer || n_returns_column_opt->answer
687 || class_column_opt->answer || grass_rgb_column_opt->answer
688 || red_column_opt->answer || green_column_opt->answer
689 || blue_column_opt->answer) {
690 dbDriver *db_driver;
691 struct field_info *f_info;
692
693 struct LidarColumnNames column_names;
694
695 column_names.return_n = return_column_opt->answer;
696 column_names.n_returns = n_returns_column_opt->answer;
697 column_names.class_n = class_column_opt->answer;
698 column_names.grass_rgb = grass_rgb_column_opt->answer;
699 column_names.red = red_column_opt->answer;
700 column_names.green = green_column_opt->answer;
701 column_names.blue = blue_column_opt->answer;
702
703 open_database(&vinput, layer, &db_driver, &f_info);
704 load_columns(&write_context, db_driver, f_info, &column_names,
705 where_opt->answer);
706 close_database(db_driver);
707
708 if ( grass_rgb_column_opt->answer || red_column_opt->answer || green_column_opt->answer || blue_column_opt->answer)
709 use_color_attributes = TRUE;
710 }
711
712 struct Colors color_table;
713 write_context.color_table = 0;
714 if (!use_color_attributes && !no_color_table_flag->answer
715 && !(write_context.rgb_layer)) {
716 int has_colors = Vect_read_colors(Vect_get_name(&vinput),
717 Vect_get_mapset(&vinput),
718 &color_table);
719 if (has_colors)
720 write_context.color_table = &color_table;
721 }
722
723 /* some constraints can be set on the map */
724 Vect_set_constraint_type(&vinput, GV_POINT);
725 /* noop for layer=-1 and non-native format, skips lines without cat */
726 Vect_set_constraint_field(&vinput, layer);
727 /* TODO: replace region checks by Vect_set_constraint_region? */
728
729 int ltype;
730 int cat;
731
732 while (TRUE) {
733 ltype = Vect_read_next_line(&vinput, line, cats);
734 if (ltype == -1)
735 G_fatal_error(_("Unable to read vector map"));
736 if (ltype == -2)
737 break; /* end of the map */
738
739 double x, y, z;
740
741 Vect_line_get_point(line, 0, &x, &y, &z);
742
743 /* selections/filters */
744 /* TODO: use region only when actually needed */
745 if (region_flag->answer && !point_in_region_2d(&comp_region, x, y))
746 continue;
747 if (layer > 0 && allowed_cats &&
748 !Vect_cats_in_constraint(cats, layer, allowed_cats))
749 continue;
750
751 /* TODO: test: skip points without category, unless layer=-1 */
752 /* Use cases:
753 * - all points have category (correct)
754 * - no categories for any point (correct, layer=-1 required)
755 * - some points miss category (not handled)
756 * Here we assume that there is only one set of attributes for one point.
757 * If no layer available, cat contains junk and shouldn't be used.
758 *
759 * TODO: done
760 */
761 cat = -1;
762 if (layer > 0) {
763 if (allowed_cats) {
764 int i;
765
766 for (i = 0; i < cats->n_cats; i++) {
767 if (cats->field[i] == layer &&
768 Vect_cat_in_cat_list(cats->cat[i], allowed_cats)) {
769 cat = cats->cat[i];
770 break;
771 }
772 }
773 }
774 else {
775 Vect_cat_get(cats, layer, &cat);
776 }
777 }
778
779 write_point(&write_context, cat, x, y, z, cats);
780 }
781
782 /* partially unnecessary as deallocated by the system */
783 Vect_destroy_line_struct(line);
784 Vect_destroy_cats_struct(cats);
785 Vect_close(&vinput);
786
787 free_columns(&write_context);
788
789 LASPoint_Destroy(write_context.las_point);
790 LASColor_Destroy(write_context.las_color);
791 LASWriter_Destroy(write_context.las_writer);
792 LASSRS_Destroy(las_srs);
793
794 return EXIT_SUCCESS;
795 }
796