1
/****************************************************************************
5
* AUTHOR(S): Markus Metz
6
* Based on r.in.xyz by Hamish Bowman, Volker Wichmann
8
* PURPOSE: Imports LAS LiDAR point clouds to a raster map using
9
* aggregate statistics.
11
* COPYRIGHT: (C) 2011 Markus Metz and the The GRASS Development Team
13
* This program is free software under the GNU General Public
14
* License (>=v2). Read the file COPYING that comes with GRASS
17
*****************************************************************************/
24
#include <sys/types.h>
25
#include <grass/gis.h>
26
#include <grass/raster.h>
27
#include <grass/gprojects.h>
28
#include <grass/glocale.h>
29
#include <liblas/capi/liblas.h>
30
#include "local_proto.h"
38
#define SIZE_INCREMENT 10
52
if (num_nodes >= max_nodes) {
53
max_nodes += SIZE_INCREMENT;
54
nodes = G_realloc(nodes, (size_t)max_nodes * sizeof(struct node));
61
/* add node to sorted, single linked list
62
* returns id if head has to be saved to index array, otherwise -1 */
63
int add_node(int head, double z)
65
int node_id, last_id, newnode_id, head_id;
71
while (node_id != -1 && nodes[node_id].z < z) {
73
node_id = nodes[node_id].next;
76
/* end of list, simply append */
78
newnode_id = new_node();
79
nodes[newnode_id].next = -1;
80
nodes[newnode_id].z = z;
81
nodes[last_id].next = newnode_id;
84
else if (node_id == head_id) { /* pole position, insert as head */
85
newnode_id = new_node();
86
nodes[newnode_id].next = head_id;
88
nodes[newnode_id].z = z;
91
else { /* somewhere in the middle, insert */
92
newnode_id = new_node();
93
nodes[newnode_id].z = z;
94
nodes[newnode_id].next = node_id;
95
nodes[last_id].next = newnode_id;
100
int main(int argc, char *argv[])
103
char *infile, *outmap;
106
int bin_n, bin_min, bin_max, bin_sum, bin_sumsq, bin_index;
107
double zrange_min, zrange_max, d_tmp;
108
unsigned long estimated_lines;
110
RASTER_MAP_TYPE rtype;
111
struct History history;
113
void *n_array, *min_array, *max_array, *sum_array, *sumsq_array,
115
void *raster_row, *ptr;
116
struct Cell_head region;
117
int rows, cols; /* scan box size */
118
int row, col; /* counters */
121
unsigned long line, line_total;
122
unsigned int counter;
125
double pass_north, pass_south;
126
int arr_row, arr_col;
127
unsigned long count, count_total;
131
double min = 0.0 / 0.0; /* init as nan */
132
double max = 0.0 / 0.0; /* init as nan */
134
size_t offset, n_offset;
138
double variance, mean, skew, sumdev;
144
int head_id, node_id;
147
struct GModule *module;
148
struct Option *input_opt, *output_opt, *percent_opt, *type_opt, *filter_opt, *class_opt;
149
struct Option *method_opt, *zrange_opt, *zscale_opt;
150
struct Option *trim_opt, *pth_opt, *res_opt;
151
struct Flag *print_flag, *scan_flag, *shell_style, *over_flag, *extents_flag, *intens_flag;
154
LASReaderH LAS_reader;
155
LASHeaderH LAS_header;
160
struct Key_Value *loc_proj_info = NULL, *loc_proj_units = NULL;
161
struct Key_Value *proj_info, *proj_units;
163
struct Cell_head cellhd, loc_wind;
165
unsigned int n_filtered;
169
module = G_define_module();
170
G_add_keyword(_("raster"));
171
G_add_keyword(_("import"));
172
G_add_keyword(_("LIDAR"));
173
module->description =
174
_("Creates a raster map from LAS LiDAR points using univariate statistics.");
176
input_opt = G_define_standard_option(G_OPT_F_INPUT);
177
input_opt->label = _("LAS input file");
178
input_opt->description = _("LiDAR input files in LAS format (*.las or *.laz)");
180
output_opt = G_define_standard_option(G_OPT_R_OUTPUT);
182
method_opt = G_define_option();
183
method_opt->key = "method";
184
method_opt->type = TYPE_STRING;
185
method_opt->required = NO;
186
method_opt->description = _("Statistic to use for raster values");
187
method_opt->options =
188
"n,min,max,range,sum,mean,stddev,variance,coeff_var,median,percentile,skewness,trimmean";
189
method_opt->answer = "mean";
190
method_opt->guisection = _("Statistic");
192
type_opt = G_define_option();
193
type_opt->key = "type";
194
type_opt->type = TYPE_STRING;
195
type_opt->required = NO;
196
type_opt->options = "CELL,FCELL,DCELL";
197
type_opt->answer = "FCELL";
198
type_opt->description = _("Storage type for resultant raster map");
200
zrange_opt = G_define_option();
201
zrange_opt->key = "zrange";
202
zrange_opt->type = TYPE_DOUBLE;
203
zrange_opt->required = NO;
204
zrange_opt->key_desc = "min,max";
205
zrange_opt->description = _("Filter range for z data (min,max)");
207
zscale_opt = G_define_option();
208
zscale_opt->key = "zscale";
209
zscale_opt->type = TYPE_DOUBLE;
210
zscale_opt->required = NO;
211
zscale_opt->answer = "1.0";
212
zscale_opt->description = _("Scale to apply to z data");
214
percent_opt = G_define_option();
215
percent_opt->key = "percent";
216
percent_opt->type = TYPE_INTEGER;
217
percent_opt->required = NO;
218
percent_opt->answer = "100";
219
percent_opt->options = "1-100";
220
percent_opt->description = _("Percent of map to keep in memory");
222
/* I would prefer to call the following "percentile", but that has too
223
* much namespace overlap with the "percent" option above */
224
pth_opt = G_define_option();
225
pth_opt->key = "pth";
226
pth_opt->type = TYPE_INTEGER;
227
pth_opt->required = NO;
228
pth_opt->options = "1-100";
229
pth_opt->description = _("pth percentile of the values");
230
pth_opt->guisection = _("Statistic");
232
trim_opt = G_define_option();
233
trim_opt->key = "trim";
234
trim_opt->type = TYPE_DOUBLE;
235
trim_opt->required = NO;
236
trim_opt->options = "0-50";
237
trim_opt->description =
238
_("Discard <trim> percent of the smallest and <trim> percent of the largest observations");
239
trim_opt->guisection = _("Statistic");
241
res_opt = G_define_option();
242
res_opt->key = "resolution";
243
res_opt->type = TYPE_DOUBLE;
244
res_opt->required = NO;
245
res_opt->description =
246
_("Output raster resolution");
248
filter_opt = G_define_option();
249
filter_opt->key = "return_filter";
250
filter_opt->type = TYPE_STRING;
251
filter_opt->required = NO;
252
filter_opt->label = _("Only import points of selected return type");
253
filter_opt->description = _("If not specified, all points are imported");
254
filter_opt->options = "first,last,mid";
256
class_opt = G_define_option();
257
class_opt->key = "class_filter";
258
class_opt->type = TYPE_INTEGER;
259
class_opt->multiple = YES;
260
class_opt->required = NO;
261
class_opt->label = _("Only import points of selected class(es)");
262
class_opt->description = _("Input is comma separated integers. "
263
"If not specified, all points are imported.");
265
print_flag = G_define_flag();
266
print_flag->key = 'p';
267
print_flag->description =
268
_("Print LAS file info and exit");
269
print_flag->suppress_required = YES;
271
extents_flag = G_define_flag();
272
extents_flag->key = 'e';
273
extents_flag->description =
274
_("Extend region extents based on new dataset");
276
over_flag = G_define_flag();
277
over_flag->key = 'o';
278
over_flag->description =
279
_("Override dataset projection (use location's projection)");
281
scan_flag = G_define_flag();
282
scan_flag->key = 's';
283
scan_flag->description = _("Scan data file for extent then exit");
284
scan_flag->suppress_required = YES;
286
shell_style = G_define_flag();
287
shell_style->key = 'g';
288
shell_style->description =
289
_("In scan mode, print using shell script style");
291
intens_flag = G_define_flag();
292
intens_flag->key = 'i';
293
intens_flag->description =
294
_("Import intensity values rather than z values");
296
if (G_parser(argc, argv))
300
/* parse input values */
301
infile = input_opt->answer;
302
outmap = output_opt->answer;
304
if (shell_style->answer && !scan_flag->answer) {
305
scan_flag->answer = 1; /* pointer not int, so set = shell_style->answer ? */
308
/* Don't crash on cmd line if file not found */
309
if (access(infile, F_OK) != 0) {
310
G_fatal_error(_("Input file <%s> does not exist"), infile);
313
LAS_reader = LASReader_Create(infile);
314
if (LAS_reader == NULL) {
315
G_fatal_error(_("Unable to open file <%s>"), infile);
318
LAS_header = LASReader_GetHeader(LAS_reader);
319
if (LAS_header == NULL) {
320
G_fatal_error(_("Input file <%s> is not a LAS LiDAR point cloud"),
324
LAS_srs = LASHeader_GetSRS(LAS_header);
326
/* Print LAS header */
327
if (print_flag->answer) {
329
print_lasinfo(LAS_header, LAS_srs);
331
LASSRS_Destroy(LAS_srs);
332
LASHeader_Destroy(LAS_header);
333
LASReader_Destroy(LAS_reader);
338
return_filter = LAS_ALL;
339
if (filter_opt->answer) {
340
if (strcmp(filter_opt->answer, "first") == 0)
341
return_filter = LAS_FIRST;
342
else if (strcmp(filter_opt->answer, "last") == 0)
343
return_filter = LAS_LAST;
344
else if (strcmp(filter_opt->answer, "mid") == 0)
345
return_filter = LAS_MID;
347
G_fatal_error(_("Unknown filter option <%s>"), filter_opt->answer);
350
/* Fetch input map projection in GRASS form. */
353
projstr = LASSRS_GetWKT_CompoundOK(LAS_srs);
357
char error_msg[8192];
359
/* Projection only required for checking so convert non-interactively */
360
if (GPJ_wkt_to_grass(&cellhd, &proj_info,
361
&proj_units, projstr, 0) < 0)
362
G_warning(_("Unable to convert input map projection information to "
363
"GRASS format for checking"));
365
/* Does the projection of the current location match the dataset? */
366
/* G_get_window seems to be unreliable if the location has been changed */
367
G_get_set_window(&loc_wind); /* TODO: v.in.lidar uses G_get_default_window() */
368
/* fetch LOCATION PROJ info */
369
if (loc_wind.proj != PROJECTION_XY) {
370
loc_proj_info = G_get_projinfo();
371
loc_proj_units = G_get_projunits();
374
if (over_flag->answer) {
375
cellhd.proj = loc_wind.proj;
376
cellhd.zone = loc_wind.zone;
377
G_message(_("Over-riding projection check"));
379
else if (loc_wind.proj != cellhd.proj
381
G_compare_projections(loc_proj_info, loc_proj_units,
382
proj_info, proj_units)) != TRUE) {
386
_("Projection of dataset does not"
387
" appear to match current location.\n\n"));
389
/* TODO: output this info sorted by key: */
390
if (loc_wind.proj != cellhd.proj || err != -2) {
391
if (loc_proj_info != NULL) {
392
strcat(error_msg, _("GRASS LOCATION PROJ_INFO is:\n"));
393
for (i_value = 0; i_value < loc_proj_info->nitems;
395
sprintf(error_msg + strlen(error_msg), "%s: %s\n",
396
loc_proj_info->key[i_value],
397
loc_proj_info->value[i_value]);
398
strcat(error_msg, "\n");
401
if (proj_info != NULL) {
402
strcat(error_msg, _("Import dataset PROJ_INFO is:\n"));
403
for (i_value = 0; i_value < proj_info->nitems; i_value++)
404
sprintf(error_msg + strlen(error_msg), "%s: %s\n",
405
proj_info->key[i_value],
406
proj_info->value[i_value]);
409
strcat(error_msg, _("Import dataset PROJ_INFO is:\n"));
410
if (cellhd.proj == PROJECTION_XY)
411
sprintf(error_msg + strlen(error_msg),
412
"Dataset proj = %d (unreferenced/unknown)\n",
414
else if (cellhd.proj == PROJECTION_LL)
415
sprintf(error_msg + strlen(error_msg),
416
"Dataset proj = %d (lat/long)\n",
418
else if (cellhd.proj == PROJECTION_UTM)
419
sprintf(error_msg + strlen(error_msg),
420
"Dataset proj = %d (UTM), zone = %d\n",
421
cellhd.proj, cellhd.zone);
423
sprintf(error_msg + strlen(error_msg),
424
"Dataset proj = %d (unknown), zone = %d\n",
425
cellhd.proj, cellhd.zone);
429
if (loc_proj_units != NULL) {
430
strcat(error_msg, "GRASS LOCATION PROJ_UNITS is:\n");
431
for (i_value = 0; i_value < loc_proj_units->nitems;
433
sprintf(error_msg + strlen(error_msg), "%s: %s\n",
434
loc_proj_units->key[i_value],
435
loc_proj_units->value[i_value]);
436
strcat(error_msg, "\n");
439
if (proj_units != NULL) {
440
strcat(error_msg, "Import dataset PROJ_UNITS is:\n");
441
for (i_value = 0; i_value < proj_units->nitems; i_value++)
442
sprintf(error_msg + strlen(error_msg), "%s: %s\n",
443
proj_units->key[i_value],
444
proj_units->value[i_value]);
447
sprintf(error_msg + strlen(error_msg),
448
_("\nYou can use the -o flag to %s to override this projection check.\n"),
451
_("Consider generating a new location with 'location' parameter"
452
" from input data set.\n"));
453
G_fatal_error(error_msg);
455
else if (!shell_style->answer) {
456
G_message(_("Projection of input dataset and current location "
461
percent = atoi(percent_opt->answer);
462
zscale = atof(zscale_opt->answer);
465
if (zrange_opt->answer != NULL) {
466
if (zrange_opt->answers[0] == NULL)
467
G_fatal_error(_("Invalid zrange"));
469
sscanf(zrange_opt->answers[0], "%lf", &zrange_min);
470
sscanf(zrange_opt->answers[1], "%lf", &zrange_max);
472
if (zrange_min > zrange_max) {
474
zrange_max = zrange_min;
479
/* figure out what maps we need in memory */
483
range min max max - min
486
stddev sum sumsq n sqrt((sumsq - sum*sum/n)/n)
487
variance sum sumsq n (sumsq - sum*sum/n)/n
488
coeff_var sum sumsq n sqrt((sumsq - sum*sum/n)/n) / (sum/n)
489
median n array index to linked list
490
percentile n array index to linked list
491
skewness n array index to linked list
492
trimmean n array index to linked list
508
if (strcmp(method_opt->answer, "n") == 0) {
512
if (strcmp(method_opt->answer, "min") == 0) {
516
if (strcmp(method_opt->answer, "max") == 0) {
520
if (strcmp(method_opt->answer, "range") == 0) {
521
method = METHOD_RANGE;
525
if (strcmp(method_opt->answer, "sum") == 0) {
529
if (strcmp(method_opt->answer, "mean") == 0) {
530
method = METHOD_MEAN;
534
if (strcmp(method_opt->answer, "stddev") == 0) {
535
method = METHOD_STDDEV;
540
if (strcmp(method_opt->answer, "variance") == 0) {
541
method = METHOD_VARIANCE;
546
if (strcmp(method_opt->answer, "coeff_var") == 0) {
547
method = METHOD_COEFF_VAR;
552
if (strcmp(method_opt->answer, "median") == 0) {
553
method = METHOD_MEDIAN;
556
if (strcmp(method_opt->answer, "percentile") == 0) {
557
if (pth_opt->answer != NULL)
558
pth = atoi(pth_opt->answer);
560
G_fatal_error(_("Unable to calculate percentile without the pth option specified!"));
561
method = METHOD_PERCENTILE;
564
if (strcmp(method_opt->answer, "skewness") == 0) {
565
method = METHOD_SKEWNESS;
568
if (strcmp(method_opt->answer, "trimmean") == 0) {
569
if (trim_opt->answer != NULL)
570
trim = atof(trim_opt->answer) / 100.0;
572
G_fatal_error(_("Unable to calculate trimmed mean without the trim option specified!"));
573
method = METHOD_TRIMMEAN;
577
if (strcmp("CELL", type_opt->answer) == 0)
579
else if (strcmp("DCELL", type_opt->answer) == 0)
584
if (method == METHOD_N)
588
Rast_get_window(®ion);
591
if (scan_flag->answer || extents_flag->answer) {
592
if (zrange_opt->answer)
593
G_warning(_("zrange will not be taken into account during scan"));
595
scan_bounds(LAS_reader, shell_style->answer, extents_flag->answer,
598
if (!extents_flag->answer) {
599
LASHeader_Destroy(LAS_header);
600
LASReader_Destroy(LAS_reader);
606
if (res_opt->answer) {
607
/* align to resolution */
608
res = atof(res_opt->answer);
610
if (!G_scan_resolution(res_opt->answer, &res, region.proj))
611
G_fatal_error(_("Invalid input <%s=%s>"), res_opt->key, res_opt->answer);
614
G_fatal_error(_("Option '%s' must be > 0.0"), res_opt->key);
616
region.ns_res = region.ew_res = res;
618
region.north = ceil(region.north / res) * res;
619
region.south = floor(region.south / res) * res;
620
region.east = ceil(region.east / res) * res;
621
region.west = floor(region.west / res) * res;
623
G_adjust_Cell_head(®ion, 0, 0);
625
else if (extents_flag->answer) {
626
/* align to current region */
627
Rast_align_window(®ion, &loc_wind);
629
Rast_set_output_window(®ion);
631
rows = (int)(region.rows * (percent / 100.0));
634
G_debug(2, "region.n=%f region.s=%f region.ns_res=%f", region.north,
635
region.south, region.ns_res);
636
G_debug(2, "region.rows=%d [box_rows=%d] region.cols=%d", region.rows,
639
npasses = (int)ceil(1.0 * region.rows / rows);
641
if (!scan_flag->answer) {
642
/* check if rows * (cols + 1) go into a size_t */
643
if (sizeof(size_t) < 8) {
644
double dsize = rows * (cols + 1);
646
if (dsize != (size_t)rows * (cols + 1))
647
G_fatal_error(_("Unable to process the hole map at once. "
648
"Please set the %s option to some value lower than 100."),
651
/* allocate memory (test for enough before we start) */
653
n_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
655
min_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
657
max_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
659
sum_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
661
sumsq_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
664
G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
666
/* and then free it again */
680
/** end memory test **/
684
/* open output map */
685
out_fd = Rast_open_new(outmap, rtype);
687
estimated_lines = LASHeader_GetPointRecordsCount(LAS_header);
689
/* allocate memory for a single row of output data */
690
raster_row = Rast_allocate_output_buf(rtype);
692
G_message(_("Reading data ..."));
694
count_total = line_total = 0;
696
/* init northern border */
697
pass_south = region.north;
699
/* main binning loop(s) */
700
for (pass = 1; pass <= npasses; pass++) {
704
G_message(_("Pass #%d (of %d) ..."), pass, npasses);
706
LAS_error = LASReader_Seek(LAS_reader, 0);
708
if (LAS_error != LE_None)
709
G_fatal_error(_("Could not rewind input file"));
711
/* figure out segmentation */
712
pass_north = pass_south; /* exact copy to avoid fp errors */
713
pass_south = region.north - pass * rows * region.ns_res;
714
if (pass == npasses) {
715
rows = region.rows - (pass - 1) * rows;
716
pass_south = region.south; /* exact copy to avoid fp errors */
719
G_debug(2, "pass=%d/%d pass_n=%f pass_s=%f rows=%d",
720
pass, npasses, pass_north, pass_south, rows);
724
G_debug(2, "allocating n_array");
725
n_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
726
blank_array(n_array, rows, cols, CELL_TYPE, 0);
729
G_debug(2, "allocating min_array");
730
min_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
731
blank_array(min_array, rows, cols, rtype, -1); /* fill with NULLs */
734
G_debug(2, "allocating max_array");
735
max_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
736
blank_array(max_array, rows, cols, rtype, -1); /* fill with NULLs */
739
G_debug(2, "allocating sum_array");
740
sum_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
741
blank_array(sum_array, rows, cols, rtype, 0);
744
G_debug(2, "allocating sumsq_array");
745
sumsq_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
746
blank_array(sumsq_array, rows, cols, rtype, 0);
749
G_debug(2, "allocating index_array");
751
G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
752
blank_array(index_array, rows, cols, CELL_TYPE, -1); /* fill with NULLs */
760
while ((LAS_point = LASReader_GetNextPoint(LAS_reader)) != NULL) {
764
if (counter == 100000) { /* speed */
765
if (line < estimated_lines)
766
G_percent(line, estimated_lines, 3);
770
if (!LASPoint_IsValid(LAS_point)) {
774
x = LASPoint_GetX(LAS_point);
775
y = LASPoint_GetY(LAS_point);
776
if (intens_flag->answer)
777
/* use z variable here to allow for scaling of intensity below */
778
z = LASPoint_GetIntensity(LAS_point);
780
z = LASPoint_GetZ(LAS_point);
782
if (return_filter != LAS_ALL) {
783
int return_no = LASPoint_GetReturnNumber(LAS_point);
784
int n_returns = LASPoint_GetNumberOfReturns(LAS_point);
787
switch (return_filter) {
793
if (return_no > 1 && return_no < n_returns)
797
if (n_returns > 1 && return_no == n_returns)
807
if (class_opt->answer) {
808
point_class = (int) LASPoint_GetClassification(LAS_point);
811
while (class_opt->answers[i]) {
812
if (point_class == atoi(class_opt->answers[i])) {
823
if (y <= pass_south || y > pass_north) {
826
if (x < region.west || x >= region.east) {
832
if (zrange_opt->answer) {
833
if (z < zrange_min || z > zrange_max) {
839
/* G_debug(5, "x: %f, y: %f, z: %f", x, y, z); */
841
/* find the bin in the current array box */
842
arr_row = (int)((pass_north - y) / region.ns_res);
843
arr_col = (int)((x - region.west) / region.ew_res);
846
update_n(n_array, cols, arr_row, arr_col);
848
update_min(min_array, cols, arr_row, arr_col, rtype, z);
850
update_max(max_array, cols, arr_row, arr_col, rtype, z);
852
update_sum(sum_array, cols, arr_row, arr_col, rtype, z);
854
update_sumsq(sumsq_array, cols, arr_row, arr_col, rtype, z);
860
arr_col) * Rast_cell_size(CELL_TYPE));
862
if (Rast_is_null_value(ptr, CELL_TYPE)) { /* first node */
863
head_id = new_node();
864
nodes[head_id].next = -1;
865
nodes[head_id].z = z;
866
Rast_set_c_value(ptr, head_id, CELL_TYPE); /* store index to head */
868
else { /* head is already there */
870
head_id = Rast_get_c_value(ptr, CELL_TYPE); /* get index to head */
871
head_id = add_node(head_id, z);
873
Rast_set_c_value(ptr, head_id, CELL_TYPE); /* store index to head */
878
G_percent(1, 1, 1); /* flush */
879
G_debug(2, "pass %d finished, %lu coordinates in box", pass, count);
880
count_total += count;
883
/* calc stats and output */
884
G_message(_("Writing to map ..."));
885
for (row = 0; row < rows; row++) {
888
case METHOD_N: /* n is a straight copy */
889
Rast_raster_cpy(raster_row,
891
(row * cols * Rast_cell_size(CELL_TYPE)), cols,
896
Rast_raster_cpy(raster_row,
897
min_array + (row * cols * Rast_cell_size(rtype)),
902
Rast_raster_cpy(raster_row,
903
max_array + (row * cols * Rast_cell_size(rtype)),
908
Rast_raster_cpy(raster_row,
909
sum_array + (row * cols * Rast_cell_size(rtype)),
913
case METHOD_RANGE: /* (max-min) */
915
for (col = 0; col < cols; col++) {
916
offset = (row * cols + col) * Rast_cell_size(rtype);
917
min = Rast_get_d_value(min_array + offset, rtype);
918
max = Rast_get_d_value(max_array + offset, rtype);
919
Rast_set_d_value(ptr, max - min, rtype);
920
ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
924
case METHOD_MEAN: /* (sum / n) */
926
for (col = 0; col < cols; col++) {
927
offset = (row * cols + col) * Rast_cell_size(rtype);
928
n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
929
n = Rast_get_c_value(n_array + n_offset, CELL_TYPE);
930
sum = Rast_get_d_value(sum_array + offset, rtype);
933
Rast_set_null_value(ptr, 1, rtype);
935
Rast_set_d_value(ptr, (sum / n), rtype);
937
ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
941
case METHOD_STDDEV: /* sqrt(variance) */
942
case METHOD_VARIANCE: /* (sumsq - sum*sum/n)/n */
943
case METHOD_COEFF_VAR: /* 100 * stdev / mean */
945
for (col = 0; col < cols; col++) {
946
offset = (row * cols + col) * Rast_cell_size(rtype);
947
n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
948
n = Rast_get_c_value(n_array + n_offset, CELL_TYPE);
949
sum = Rast_get_d_value(sum_array + offset, rtype);
950
sumsq = Rast_get_d_value(sumsq_array + offset, rtype);
953
Rast_set_null_value(ptr, 1, rtype);
955
Rast_set_d_value(ptr, 0.0, rtype);
957
variance = (sumsq - sum * sum / n) / n;
958
if (variance < GRASS_EPSILON)
962
if (variance != variance)
963
Rast_set_null_value(ptr, 1, rtype);
966
if (method == METHOD_STDDEV)
967
variance = sqrt(variance);
969
else if (method == METHOD_COEFF_VAR)
970
variance = 100 * sqrt(variance) / (sum / n);
973
if (variance != variance)
974
variance = 0.0; /* OK for n > 0 ?*/
976
Rast_set_d_value(ptr, variance, rtype);
980
ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
983
case METHOD_MEDIAN: /* median, if only one point in cell we will use that */
985
for (col = 0; col < cols; col++) {
986
n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
987
if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
988
Rast_set_null_value(ptr, 1, rtype);
989
else { /* one or more points in cell */
992
Rast_get_c_value(index_array + n_offset,
998
while (node_id != -1) { /* count number of points in cell */
1000
node_id = nodes[node_id].next;
1003
if (n == 1) /* only one point, use that */
1004
Rast_set_d_value(ptr, nodes[head_id].z,
1006
else if (n % 2 != 0) { /* odd number of points: median_i = (n + 1) / 2 */
1009
for (j = 1; j < n; j++) /* get "median element" */
1010
node_id = nodes[node_id].next;
1012
Rast_set_d_value(ptr, nodes[node_id].z,
1015
else { /* even number of points: median = (val_below + val_above) / 2 */
1020
for (j = 1; j < n; j++) /* get element "below" */
1021
node_id = nodes[node_id].next;
1023
z = (nodes[node_id].z +
1024
nodes[nodes[node_id].next].z) / 2;
1025
Rast_set_d_value(ptr, z, rtype);
1028
ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
1031
case METHOD_PERCENTILE: /* rank = (pth*(n+1))/100; interpolate linearly */
1033
for (col = 0; col < cols; col++) {
1034
n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
1035
if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
1036
Rast_set_null_value(ptr, 1, rtype);
1039
Rast_get_c_value(index_array + n_offset,
1044
while (node_id != -1) { /* count number of points in cell */
1046
node_id = nodes[node_id].next;
1049
z = (pth * (n + 1)) / 100.0;
1050
r_low = floor(z); /* lower rank */
1056
r_up = ceil(z); /* upper rank */
1061
for (j = 1; j < r_low; j++) /* search lower value */
1062
node_id = nodes[node_id].next;
1064
z = nodes[node_id].z; /* save lower value */
1066
for (j = 1; j < r_up; j++) /* search upper value */
1067
node_id = nodes[node_id].next;
1069
z = (z + nodes[node_id].z) / 2;
1070
Rast_set_d_value(ptr, z, rtype);
1072
ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
1075
case METHOD_SKEWNESS: /* skewness = sum(xi-mean)^3/(N-1)*s^3 */
1077
for (col = 0; col < cols; col++) {
1078
n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
1079
if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
1080
Rast_set_null_value(ptr, 1, rtype);
1083
Rast_get_c_value(index_array + n_offset,
1088
sum = 0.0; /* sum */
1089
sumsq = 0.0; /* sum of squares */
1090
sumdev = 0.0; /* sum of (xi - mean)^3 */
1091
skew = 0.0; /* skewness */
1093
while (node_id != -1) {
1094
z = nodes[node_id].z;
1098
node_id = nodes[node_id].next;
1101
if (n > 1) { /* if n == 1, skew is "0.0" */
1104
while (node_id != -1) {
1105
z = nodes[node_id].z;
1106
sumdev += pow((z - mean), 3);
1107
node_id = nodes[node_id].next;
1110
variance = (sumsq - sum * sum / n) / n;
1111
if (variance < GRASS_EPSILON)
1116
pow(sqrt(variance), 3));
1118
Rast_set_d_value(ptr, skew, rtype);
1120
ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
1123
case METHOD_TRIMMEAN:
1125
for (col = 0; col < cols; col++) {
1126
n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
1127
if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
1128
Rast_set_null_value(ptr, 1, rtype);
1131
Rast_get_c_value(index_array + n_offset,
1136
while (node_id != -1) { /* count number of points in cell */
1138
node_id = nodes[node_id].next;
1142
mean = nodes[head_id].z;
1144
k = floor(trim * n + 0.5); /* number of ranks to discard on each tail */
1146
if (k > 0 && (n - 2 * k) > 0) { /* enough elements to discard */
1148
for (j = 0; j < k; j++) /* move to first rank to consider */
1149
node_id = nodes[node_id].next;
1156
while (j <= k) { /* get values in interval */
1158
sum += nodes[node_id].z;
1159
node_id = nodes[node_id].next;
1167
while (node_id != -1) {
1169
sum += nodes[node_id].z;
1170
node_id = nodes[node_id].next;
1175
Rast_set_d_value(ptr, mean, rtype);
1177
ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
1185
/* write out line of raster data */
1186
Rast_put_row(out_fd, raster_row, rtype);
1199
G_free(sumsq_array);
1201
G_free(index_array);
1210
G_percent(1, 1, 1); /* flush */
1213
/* close input LAS file */
1214
LASHeader_Destroy(LAS_header);
1215
LASReader_Destroy(LAS_reader);
1217
/* close raster file & write history */
1220
sprintf(title, "Raw x,y,z data binned into a raster grid by cell %s",
1221
method_opt->answer);
1222
Rast_put_cell_title(outmap, title);
1224
Rast_short_history(outmap, "raster", &history);
1225
Rast_command_history(&history);
1226
Rast_set_history(&history, HIST_DATSRC_1, infile);
1227
Rast_write_history(outmap, &history);
1230
sprintf(buff, _("%lu points found in region."), count_total);
1232
G_debug(1, "Processed %lu points.", line_total);
1238
void print_lasinfo(LASHeaderH LAS_header, LASSRSH LAS_srs)
1240
char *las_srs_proj4 = LASSRS_GetProj4(LAS_srs);
1241
int las_point_format = LASHeader_GetDataFormatId(LAS_header);
1243
fprintf(stdout, "\nUsing LAS Library Version '%s'\n\n",
1244
LAS_GetFullVersion());
1245
fprintf(stdout, "LAS File Version: %d.%d\n",
1246
LASHeader_GetVersionMajor(LAS_header),
1247
LASHeader_GetVersionMinor(LAS_header));
1248
fprintf(stdout, "System ID: '%s'\n",
1249
LASHeader_GetSystemId(LAS_header));
1250
fprintf(stdout, "Generating Software: '%s'\n",
1251
LASHeader_GetSoftwareId(LAS_header));
1252
fprintf(stdout, "File Creation Day/Year: %d/%d\n",
1253
LASHeader_GetCreationDOY(LAS_header),
1254
LASHeader_GetCreationYear(LAS_header));
1255
fprintf(stdout, "Point Data Format: %d\n",
1257
fprintf(stdout, "Number of Point Records: %d\n",
1258
LASHeader_GetPointRecordsCount(LAS_header));
1259
fprintf(stdout, "Number of Points by Return: %d %d %d %d %d\n",
1260
LASHeader_GetPointRecordsByReturnCount(LAS_header, 0),
1261
LASHeader_GetPointRecordsByReturnCount(LAS_header, 1),
1262
LASHeader_GetPointRecordsByReturnCount(LAS_header, 2),
1263
LASHeader_GetPointRecordsByReturnCount(LAS_header, 3),
1264
LASHeader_GetPointRecordsByReturnCount(LAS_header, 4));
1265
fprintf(stdout, "Scale Factor X Y Z: %g %g %g\n",
1266
LASHeader_GetScaleX(LAS_header),
1267
LASHeader_GetScaleY(LAS_header),
1268
LASHeader_GetScaleZ(LAS_header));
1269
fprintf(stdout, "Offset X Y Z: %g %g %g\n",
1270
LASHeader_GetOffsetX(LAS_header),
1271
LASHeader_GetOffsetY(LAS_header),
1272
LASHeader_GetOffsetZ(LAS_header));
1273
fprintf(stdout, "Min X Y Z: %g %g %g\n",
1274
LASHeader_GetMinX(LAS_header),
1275
LASHeader_GetMinY(LAS_header),
1276
LASHeader_GetMinZ(LAS_header));
1277
fprintf(stdout, "Max X Y Z: %g %g %g\n",
1278
LASHeader_GetMaxX(LAS_header),
1279
LASHeader_GetMaxY(LAS_header),
1280
LASHeader_GetMaxZ(LAS_header));
1281
if (las_srs_proj4 && strlen(las_srs_proj4) > 0) {
1282
fprintf(stdout, "Spatial Reference:\n");
1283
fprintf(stdout, "%s\n", las_srs_proj4);
1286
fprintf(stdout, "Spatial Reference: None\n");
1289
fprintf(stdout, "\nData Fields:\n");
1290
fprintf(stdout, " 'X'\n 'Y'\n 'Z'\n 'Intensity'\n 'Return Number'\n");
1291
fprintf(stdout, " 'Number of Returns'\n 'Scan Direction'\n");
1292
fprintf(stdout, " 'Flighline Edge'\n 'Classification'\n 'Scan Angle Rank'\n");
1293
fprintf(stdout, " 'User Data'\n 'Point Source ID'\n");
1294
if (las_point_format == 1 || las_point_format == 3 || las_point_format == 4 || las_point_format == 5) {
1295
fprintf(stdout, " 'GPS Time'\n");
1297
if (las_point_format == 2 || las_point_format == 3 || las_point_format == 5) {
1298
fprintf(stdout, " 'Red'\n 'Green'\n 'Blue'\n");
1300
fprintf(stdout, "\n");
1307
int scan_bounds(LASReaderH LAS_reader, int shell_style, int extents,
1308
double zscale, struct Cell_head *region)
1312
double min_x, max_x, min_y, max_y, min_z, max_z;
1314
LASPointH LAS_point;
1319
/* init to nan in case no points are found */
1320
min_x = max_x = min_y = max_y = min_z = max_z = 0.0 / 0.0;
1322
G_verbose_message(_("Scanning data ..."));
1324
LASReader_Seek(LAS_reader, 0);
1326
while ((LAS_point = LASReader_GetNextPoint(LAS_reader)) != NULL) {
1329
if (!LASPoint_IsValid(LAS_point)) {
1333
x = LASPoint_GetX(LAS_point);
1334
y = LASPoint_GetY(LAS_point);
1335
z = LASPoint_GetZ(LAS_point);
1364
fprintf(stderr, _("Range: min max\n"));
1365
fprintf(stdout, "x: %11f %11f\n", min_x, max_x);
1366
fprintf(stdout, "y: %11f %11f\n", min_y, max_y);
1367
fprintf(stdout, "z: %11f %11f\n", min_z * zscale, max_z * zscale);
1370
fprintf(stdout, "n=%f s=%f e=%f w=%f b=%f t=%f\n",
1371
max_y, min_y, max_x, min_x, min_z * zscale, max_z * zscale);
1373
G_debug(1, "Processed %lu points.", line);
1374
G_debug(1, "region template: g.region n=%f s=%f e=%f w=%f",
1375
max_y, min_y, max_x, min_x);
1378
region->east = max_x;
1379
region->west = min_x;
1380
region->north = max_y;
1381
region->south = min_y;