main.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. /****************************************************************************
  2. *
  3. * MODULE: r.in.Lidar
  4. *
  5. * AUTHOR(S): Markus Metz
  6. * Vaclav Petras (base raster addition and refactoring)
  7. * Based on r.in.xyz by Hamish Bowman, Volker Wichmann
  8. *
  9. * PURPOSE: Imports LAS LiDAR point clouds to a raster map using
  10. * aggregate statistics.
  11. *
  12. * COPYRIGHT: (C) 2011-2015 Markus Metz and the The GRASS Development Team
  13. *
  14. * This program is free software under the GNU General Public
  15. * License (>=v2). Read the file COPYING that comes with GRASS
  16. * for details.
  17. *
  18. *****************************************************************************/
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <math.h>
  24. #include <sys/types.h>
  25. #include <grass/gis.h>
  26. #include <grass/raster.h>
  27. #include <grass/segment.h>
  28. #include <grass/gprojects.h>
  29. #include <grass/glocale.h>
  30. #include <liblas/capi/liblas.h>
  31. #include "local_proto.h"
  32. #include "rast_segment.h"
  33. #include "point_binning.h"
  34. #include "filters.h"
  35. int main(int argc, char *argv[])
  36. {
  37. int out_fd, base_raster;
  38. char *infile, *outmap;
  39. int percent;
  40. double zrange_min, zrange_max, d_tmp;
  41. double irange_min, irange_max;
  42. unsigned long estimated_lines;
  43. RASTER_MAP_TYPE rtype, base_raster_data_type;
  44. struct History history;
  45. char title[64];
  46. SEGMENT base_segment;
  47. struct PointBinning point_binning;
  48. void *base_array;
  49. void *raster_row;
  50. struct Cell_head region;
  51. struct Cell_head input_region;
  52. int rows, last_rows, row0, cols; /* scan box size */
  53. int row; /* counters */
  54. int pass, npasses;
  55. unsigned long line, line_total;
  56. unsigned int counter;
  57. unsigned long n_invalid;
  58. char buff[BUFFSIZE];
  59. double x, y, z;
  60. double intensity;
  61. int arr_row, arr_col;
  62. unsigned long count, count_total;
  63. int point_class;
  64. double zscale = 1.0;
  65. double iscale = 1.0;
  66. double res = 0.0;
  67. struct BinIndex bin_index_nodes;
  68. bin_index_nodes.num_nodes = 0;
  69. bin_index_nodes.max_nodes = 0;
  70. bin_index_nodes.nodes = 0;
  71. struct GModule *module;
  72. struct Option *input_opt, *output_opt, *percent_opt, *type_opt, *filter_opt, *class_opt;
  73. struct Option *method_opt, *base_raster_opt;
  74. struct Option *zrange_opt, *zscale_opt;
  75. struct Option *irange_opt, *iscale_opt;
  76. struct Option *trim_opt, *pth_opt, *res_opt;
  77. struct Option *file_list_opt;
  78. struct Flag *print_flag, *scan_flag, *shell_style, *over_flag, *extents_flag;
  79. struct Flag *intens_flag, *intens_import_flag;
  80. struct Flag *set_region_flag;
  81. struct Flag *base_rast_res_flag;
  82. struct Flag *only_valid_flag;
  83. /* LAS */
  84. LASReaderH LAS_reader;
  85. LASHeaderH LAS_header;
  86. LASSRSH LAS_srs;
  87. LASPointH LAS_point;
  88. int return_filter;
  89. const char *projstr;
  90. struct Cell_head cellhd, loc_wind;
  91. unsigned int n_filtered;
  92. G_gisinit(argv[0]);
  93. module = G_define_module();
  94. G_add_keyword(_("raster"));
  95. G_add_keyword(_("import"));
  96. G_add_keyword(_("LIDAR"));
  97. G_add_keyword(_("statistics"));
  98. G_add_keyword(_("conversion"));
  99. G_add_keyword(_("aggregation"));
  100. G_add_keyword(_("binning"));
  101. module->description =
  102. _("Creates a raster map from LAS LiDAR points using univariate statistics.");
  103. input_opt = G_define_standard_option(G_OPT_F_BIN_INPUT);
  104. input_opt->required = NO;
  105. input_opt->label = _("LAS input file");
  106. input_opt->description = _("LiDAR input files in LAS format (*.las or *.laz)");
  107. input_opt->guisection = _("Input");
  108. output_opt = G_define_standard_option(G_OPT_R_OUTPUT);
  109. output_opt->required = NO;
  110. output_opt->guisection = _("Output");
  111. file_list_opt = G_define_standard_option(G_OPT_F_INPUT);
  112. file_list_opt->key = "file";
  113. file_list_opt->label = _("File containing names of LAS input files");
  114. file_list_opt->description = _("LiDAR input files in LAS format (*.las or *.laz)");
  115. file_list_opt->required = NO;
  116. file_list_opt->guisection = _("Input");
  117. method_opt = G_define_option();
  118. method_opt->key = "method";
  119. method_opt->type = TYPE_STRING;
  120. method_opt->required = NO;
  121. method_opt->description = _("Statistic to use for raster values");
  122. method_opt->options =
  123. "n,min,max,range,sum,mean,stddev,variance,coeff_var,median,percentile,skewness,trimmean";
  124. method_opt->answer = "mean";
  125. method_opt->guisection = _("Statistic");
  126. G_asprintf((char **)&(method_opt->descriptions),
  127. "n;%s;"
  128. "min;%s;"
  129. "max;%s;"
  130. "range;%s;"
  131. "sum;%s;"
  132. "mean;%s;"
  133. "stddev;%s;"
  134. "variance;%s;"
  135. "coeff_var;%s;"
  136. "median;%s;"
  137. "percentile;%s;"
  138. "skewness;%s;"
  139. "trimmean;%s",
  140. _("Number of points in cell"),
  141. _("Minimum value of point values in cell"),
  142. _("Maximum value of point values in cell"),
  143. _("Range of point values in cell"),
  144. _("Sum of point values in cell"),
  145. _("Mean (average) value of point values in cell"),
  146. _("Standard deviation of point values in cell"),
  147. _("Variance of point values in cell"),
  148. _("Coefficient of variance of point values in cell"),
  149. _("Median value of point values in cell"),
  150. _("pth (nth) percentile of point values in cell"),
  151. _("Skewness of point values in cell"),
  152. _("Trimmed mean of point values in cell"));
  153. type_opt = G_define_standard_option(G_OPT_R_TYPE);
  154. type_opt->required = NO;
  155. type_opt->answer = "FCELL";
  156. base_raster_opt = G_define_standard_option(G_OPT_R_INPUT);
  157. base_raster_opt->key = "base_raster";
  158. base_raster_opt->required = NO;
  159. base_raster_opt->label =
  160. _("Subtract raster values from the Z coordinates");
  161. base_raster_opt->description =
  162. _("The scale for Z is applied beforehand, the range filter for"
  163. " Z afterwards");
  164. base_raster_opt->guisection = _("Transform");
  165. zrange_opt = G_define_option();
  166. zrange_opt->key = "zrange";
  167. zrange_opt->type = TYPE_DOUBLE;
  168. zrange_opt->required = NO;
  169. zrange_opt->key_desc = "min,max";
  170. zrange_opt->label = _("Filter range for Z data (min,max)");
  171. zrange_opt->description = _("Applied after base_raster transformation step");
  172. zrange_opt->guisection = _("Selection");
  173. zscale_opt = G_define_option();
  174. zscale_opt->key = "zscale";
  175. zscale_opt->type = TYPE_DOUBLE;
  176. zscale_opt->required = NO;
  177. zscale_opt->answer = "1.0";
  178. zscale_opt->description = _("Scale to apply to Z data");
  179. zscale_opt->guisection = _("Transform");
  180. irange_opt = G_define_option();
  181. irange_opt->key = "intensity_range";
  182. irange_opt->type = TYPE_DOUBLE;
  183. irange_opt->required = NO;
  184. irange_opt->key_desc = "min,max";
  185. irange_opt->description = _("Filter range for intensity values (min,max)");
  186. irange_opt->guisection = _("Selection");
  187. iscale_opt = G_define_option();
  188. iscale_opt->key = "intensity_scale";
  189. iscale_opt->type = TYPE_DOUBLE;
  190. iscale_opt->required = NO;
  191. iscale_opt->answer = "1.0";
  192. iscale_opt->description = _("Scale to apply to intensity values");
  193. iscale_opt->guisection = _("Transform");
  194. percent_opt = G_define_option();
  195. percent_opt->key = "percent";
  196. percent_opt->type = TYPE_INTEGER;
  197. percent_opt->required = NO;
  198. percent_opt->answer = "100";
  199. percent_opt->options = "1-100";
  200. percent_opt->description = _("Percent of map to keep in memory");
  201. /* I would prefer to call the following "percentile", but that has too
  202. * much namespace overlap with the "percent" option above */
  203. pth_opt = G_define_option();
  204. pth_opt->key = "pth";
  205. pth_opt->type = TYPE_INTEGER;
  206. pth_opt->required = NO;
  207. pth_opt->options = "1-100";
  208. pth_opt->description = _("pth percentile of the values");
  209. pth_opt->guisection = _("Statistic");
  210. trim_opt = G_define_option();
  211. trim_opt->key = "trim";
  212. trim_opt->type = TYPE_DOUBLE;
  213. trim_opt->required = NO;
  214. trim_opt->options = "0-50";
  215. trim_opt->label = _("Discard given percentage of the smallest and largest values");
  216. trim_opt->description =
  217. _("Discard <trim> percent of the smallest and <trim> percent of the largest observations");
  218. trim_opt->guisection = _("Statistic");
  219. res_opt = G_define_option();
  220. res_opt->key = "resolution";
  221. res_opt->type = TYPE_DOUBLE;
  222. res_opt->required = NO;
  223. res_opt->description =
  224. _("Output raster resolution");
  225. res_opt->guisection = _("Output");
  226. filter_opt = G_define_option();
  227. filter_opt->key = "return_filter";
  228. filter_opt->type = TYPE_STRING;
  229. filter_opt->required = NO;
  230. filter_opt->label = _("Only import points of selected return type");
  231. filter_opt->description = _("If not specified, all points are imported");
  232. filter_opt->options = "first,last,mid";
  233. filter_opt->guisection = _("Selection");
  234. class_opt = G_define_option();
  235. class_opt->key = "class_filter";
  236. class_opt->type = TYPE_INTEGER;
  237. class_opt->multiple = YES;
  238. class_opt->required = NO;
  239. class_opt->label = _("Only import points of selected class(es)");
  240. class_opt->description = _("Input is comma separated integers. "
  241. "If not specified, all points are imported.");
  242. class_opt->guisection = _("Selection");
  243. print_flag = G_define_flag();
  244. print_flag->key = 'p';
  245. print_flag->description =
  246. _("Print LAS file info and exit");
  247. extents_flag = G_define_flag();
  248. extents_flag->key = 'e';
  249. extents_flag->label =
  250. _("Use the extent of the input for the raster extent");
  251. extents_flag->description =
  252. _("Set internally computational region extents based on the"
  253. " point cloud");
  254. extents_flag->guisection = _("Output");
  255. set_region_flag = G_define_flag();
  256. set_region_flag->key = 'n';
  257. set_region_flag->label =
  258. _("Set computation region to match the new raster map");
  259. set_region_flag->description =
  260. _("Set computation region to match the 2D extent and resolution"
  261. " of the newly created new raster map");
  262. set_region_flag->guisection = _("Output");
  263. over_flag = G_define_flag();
  264. over_flag->key = 'o';
  265. over_flag->label =
  266. _("Override projection check (use current location's projection)");
  267. over_flag->description =
  268. _("Assume that the dataset has same projection as the current location");
  269. scan_flag = G_define_flag();
  270. scan_flag->key = 's';
  271. scan_flag->description = _("Scan data file for extent then exit");
  272. shell_style = G_define_flag();
  273. shell_style->key = 'g';
  274. shell_style->description =
  275. _("In scan mode, print using shell script style");
  276. intens_flag = G_define_flag();
  277. intens_flag->key = 'i';
  278. intens_flag->label =
  279. _("Use intensity values rather than Z values");
  280. intens_flag->description =
  281. _("Uses intensity values everywhere as if they would be Z"
  282. " coordinates");
  283. intens_import_flag = G_define_flag();
  284. intens_import_flag->key = 'j';
  285. intens_import_flag->description =
  286. _("Use Z values for filtering, but intensity values for statistics");
  287. base_rast_res_flag = G_define_flag();
  288. base_rast_res_flag->key = 'd';
  289. base_rast_res_flag->label =
  290. _("Use base raster resolution instead of computational region");
  291. base_rast_res_flag->description =
  292. _("For getting values from base raster, use its actual"
  293. " resolution instead of computational region resolution");
  294. base_rast_res_flag->guisection = _("Transform");
  295. only_valid_flag = G_define_flag();
  296. only_valid_flag->key = 'v';
  297. only_valid_flag->label = _("Use only valid points");
  298. only_valid_flag->description =
  299. _("Points invalid according to APSRS LAS specification will be"
  300. " filtered out");
  301. only_valid_flag->guisection = _("Selection");
  302. G_option_required(input_opt, file_list_opt, NULL);
  303. G_option_exclusive(input_opt, file_list_opt, NULL);
  304. G_option_required(output_opt, print_flag, scan_flag, shell_style, NULL);
  305. G_option_exclusive(intens_flag, intens_import_flag, NULL);
  306. G_option_requires(base_rast_res_flag, base_raster_opt, NULL);
  307. if (G_parser(argc, argv))
  308. exit(EXIT_FAILURE);
  309. int only_valid = FALSE;
  310. n_invalid = 0;
  311. if (only_valid_flag->answer)
  312. only_valid = TRUE;
  313. /* we could use rules but this gives more info and allows continuing */
  314. if (set_region_flag->answer && !(extents_flag->answer || res_opt->answer)) {
  315. G_warning(_("Flag %c makes sense only with %s option or -%c flag"),
  316. set_region_flag->key, res_opt->key, extents_flag->key);
  317. /* avoid the call later on */
  318. set_region_flag->answer = '\0';
  319. }
  320. /* Trim option is used only for trimmean method */
  321. if (trim_opt->answer != NULL && strcmp(method_opt->answer, "trimmean") != 0) {
  322. G_fatal_error(_("Trim option can be used only with trimmean method"));
  323. }
  324. struct StringList infiles;
  325. if (file_list_opt->answer) {
  326. if (access(file_list_opt->answer, F_OK) != 0)
  327. G_fatal_error(_("File <%s> does not exist"), file_list_opt->answer);
  328. string_list_from_file(&infiles, file_list_opt->answer);
  329. }
  330. else {
  331. string_list_from_one_item(&infiles, input_opt->answer);
  332. }
  333. /* parse input values */
  334. outmap = output_opt->answer;
  335. if (shell_style->answer && !scan_flag->answer) {
  336. scan_flag->answer = 1; /* pointer not int, so set = shell_style->answer ? */
  337. }
  338. /* check zrange and extent relation */
  339. if (scan_flag->answer || extents_flag->answer) {
  340. if (zrange_opt->answer)
  341. G_warning(_("zrange will not be taken into account during scan"));
  342. }
  343. Rast_get_window(&region);
  344. /* G_get_window seems to be unreliable if the location has been changed */
  345. G_get_set_window(&loc_wind); /* TODO: v.in.lidar uses G_get_default_window() */
  346. estimated_lines = 0;
  347. int i;
  348. for (i = 0; i < infiles.num_items; i++) {
  349. infile = infiles.items[i];
  350. /* don't if file not found */
  351. if (access(infile, F_OK) != 0)
  352. G_fatal_error(_("Input file <%s> does not exist"), infile);
  353. /* Open LAS file*/
  354. LAS_reader = LASReader_Create(infile);
  355. if (LAS_reader == NULL)
  356. G_fatal_error(_("Unable to open file <%s> as a LiDAR point cloud"),
  357. infile);
  358. LAS_header = LASReader_GetHeader(LAS_reader);
  359. if (LAS_header == NULL) {
  360. G_fatal_error(_("Unable to read LAS header of <%s>"), infile);
  361. }
  362. LAS_srs = LASHeader_GetSRS(LAS_header);
  363. /* print info or check projection if we are actually importing */
  364. if (print_flag->answer) {
  365. /* print filename when there is more than one file */
  366. if (infiles.num_items > 1)
  367. fprintf(stdout, "File: %s\n", infile);
  368. /* Print LAS header */
  369. print_lasinfo(LAS_header, LAS_srs);
  370. }
  371. else {
  372. /* report that we are checking more files */
  373. if (i == 1)
  374. G_message(_("First file's projection checked,"
  375. " checking projection of the other files..."));
  376. /* Fetch input map projection in GRASS form. */
  377. projstr = LASSRS_GetWKT_CompoundOK(LAS_srs);
  378. /* we are printing the non-warning messages only for first file */
  379. projection_check_wkt(cellhd, loc_wind, projstr, over_flag->answer,
  380. shell_style->answer || i);
  381. /* if there is a problem in some other file, first OK message
  382. * is printed but than a warning, this is not ideal but hopefully
  383. * not so confusing when importing multiple files */
  384. }
  385. if (scan_flag->answer || extents_flag->answer) {
  386. /* we assign to the first one (i==0) but update for the rest */
  387. scan_bounds(LAS_reader, shell_style->answer, extents_flag->answer, i,
  388. zscale, &region);
  389. }
  390. /* number of estimated point across all files */
  391. /* TODO: this should be ull which won't work with percent report */
  392. estimated_lines += LASHeader_GetPointRecordsCount(LAS_header);
  393. /* We are closing all again and we will be opening them later,
  394. * so we don't have to worry about limit for open files. */
  395. LASSRS_Destroy(LAS_srs);
  396. LASHeader_Destroy(LAS_header);
  397. LASReader_Destroy(LAS_reader);
  398. }
  399. /* if we are not importing, end */
  400. if (print_flag->answer || scan_flag->answer)
  401. exit(EXIT_SUCCESS);
  402. return_filter = LAS_ALL;
  403. if (filter_opt->answer) {
  404. if (strcmp(filter_opt->answer, "first") == 0)
  405. return_filter = LAS_FIRST;
  406. else if (strcmp(filter_opt->answer, "last") == 0)
  407. return_filter = LAS_LAST;
  408. else if (strcmp(filter_opt->answer, "mid") == 0)
  409. return_filter = LAS_MID;
  410. else
  411. G_fatal_error(_("Unknown filter option <%s>"), filter_opt->answer);
  412. }
  413. struct ReturnFilter return_filter_struct;
  414. return_filter_struct.filter = return_filter;
  415. struct ClassFilter class_filter;
  416. class_filter_create_from_strings(&class_filter, class_opt->answers);
  417. percent = atoi(percent_opt->answer);
  418. /* TODO: we already used zscale */
  419. /* TODO: we don't report intensity range */
  420. if (zscale_opt->answer)
  421. zscale = atof(zscale_opt->answer);
  422. if (iscale_opt->answer)
  423. iscale = atof(iscale_opt->answer);
  424. /* parse zrange */
  425. if (zrange_opt->answer != NULL) {
  426. if (zrange_opt->answers[0] == NULL)
  427. G_fatal_error(_("Invalid zrange"));
  428. sscanf(zrange_opt->answers[0], "%lf", &zrange_min);
  429. sscanf(zrange_opt->answers[1], "%lf", &zrange_max);
  430. if (zrange_min > zrange_max) {
  431. d_tmp = zrange_max;
  432. zrange_max = zrange_min;
  433. zrange_min = d_tmp;
  434. }
  435. }
  436. /* parse irange */
  437. if (irange_opt->answer != NULL) {
  438. if (irange_opt->answers[0] == NULL)
  439. G_fatal_error(_("Invalid %s"), irange_opt->key);
  440. sscanf(irange_opt->answers[0], "%lf", &irange_min);
  441. sscanf(irange_opt->answers[1], "%lf", &irange_max);
  442. if (irange_min > irange_max) {
  443. d_tmp = irange_max;
  444. irange_max = irange_min;
  445. irange_min = d_tmp;
  446. }
  447. }
  448. point_binning_set(&point_binning, method_opt->answer, pth_opt->answer,
  449. trim_opt->answer, FALSE);
  450. base_array = NULL;
  451. if (strcmp("CELL", type_opt->answer) == 0)
  452. rtype = CELL_TYPE;
  453. else if (strcmp("DCELL", type_opt->answer) == 0)
  454. rtype = DCELL_TYPE;
  455. else
  456. rtype = FCELL_TYPE;
  457. if (point_binning.method == METHOD_N)
  458. rtype = CELL_TYPE;
  459. if (res_opt->answer) {
  460. /* align to resolution */
  461. res = atof(res_opt->answer);
  462. if (!G_scan_resolution(res_opt->answer, &res, region.proj))
  463. G_fatal_error(_("Invalid input <%s=%s>"), res_opt->key, res_opt->answer);
  464. if (res <= 0)
  465. G_fatal_error(_("Option '%s' must be > 0.0"), res_opt->key);
  466. region.ns_res = region.ew_res = res;
  467. region.north = ceil(region.north / res) * res;
  468. region.south = floor(region.south / res) * res;
  469. region.east = ceil(region.east / res) * res;
  470. region.west = floor(region.west / res) * res;
  471. G_adjust_Cell_head(&region, 0, 0);
  472. }
  473. else if (extents_flag->answer) {
  474. /* align to current region */
  475. Rast_align_window(&region, &loc_wind);
  476. }
  477. Rast_set_output_window(&region);
  478. rows = last_rows = region.rows;
  479. npasses = 1;
  480. if (percent < 100) {
  481. rows = (int)(region.rows * (percent / 100.0));
  482. npasses = region.rows / rows;
  483. last_rows = region.rows - npasses * rows;
  484. if (last_rows)
  485. npasses++;
  486. else
  487. last_rows = rows;
  488. }
  489. cols = region.cols;
  490. G_debug(2, "region.n=%f region.s=%f region.ns_res=%f", region.north,
  491. region.south, region.ns_res);
  492. G_debug(2, "region.rows=%d [box_rows=%d] region.cols=%d", region.rows,
  493. rows, region.cols);
  494. /* using row-based chunks (used for output) when input and output
  495. * region matches and using segment library when they don't */
  496. int use_segment = 0;
  497. int use_base_raster_res = 0;
  498. /* TODO: see if the input region extent is smaller than the raster
  499. * if yes, the we need to load the whole base raster if the -e
  500. * flag was defined (alternatively clip the regions) */
  501. if (base_rast_res_flag->answer)
  502. use_base_raster_res = 1;
  503. if (base_raster_opt->answer && (res_opt->answer || use_base_raster_res
  504. || extents_flag->answer))
  505. use_segment = 1;
  506. if (base_raster_opt->answer && !use_segment) {
  507. /* TODO: do we need to test existence first? mapset? */
  508. base_raster = Rast_open_old(base_raster_opt->answer, "");
  509. base_raster_data_type = Rast_get_map_type(base_raster);
  510. base_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(base_raster_data_type));
  511. }
  512. if (base_raster_opt->answer && use_segment) {
  513. if (use_base_raster_res) {
  514. /* read raster actual extent and resolution */
  515. Rast_get_cellhd(base_raster_opt->answer, "", &input_region);
  516. /* TODO: make it only as small as the output is or points are */
  517. Rast_set_input_window(&input_region); /* we have split window */
  518. } else {
  519. Rast_get_input_window(&input_region);
  520. }
  521. rast_segment_open(&base_segment, base_raster_opt->answer, &base_raster_data_type);
  522. }
  523. if (!scan_flag->answer) {
  524. if (!check_rows_cols_fit_to_size_t(rows, cols))
  525. G_fatal_error(_("Unable to process the hole map at once. "
  526. "Please set the '%s' option to some value lower than 100."),
  527. percent_opt->key);
  528. point_binning_memory_test(&point_binning, rows, cols, rtype);
  529. }
  530. /* open output map */
  531. out_fd = Rast_open_new(outmap, rtype);
  532. /* allocate memory for a single row of output data */
  533. raster_row = Rast_allocate_output_buf(rtype);
  534. G_message(_("Reading data..."));
  535. count_total = line_total = 0;
  536. /* main binning loop(s) */
  537. for (pass = 1; pass <= npasses; pass++) {
  538. if (npasses > 1)
  539. G_message(_("Pass #%d (of %d)..."), pass, npasses);
  540. /* figure out segmentation */
  541. row0 = (pass - 1) * rows;
  542. if (pass == npasses) {
  543. rows = last_rows;
  544. }
  545. if (base_array) {
  546. G_debug(2, "filling base raster array");
  547. for (row = 0; row < rows; row++) {
  548. Rast_get_row(base_raster, base_array + ((size_t) row * cols * Rast_cell_size(base_raster_data_type)), row, base_raster_data_type);
  549. }
  550. }
  551. G_debug(2, "pass=%d/%d rows=%d", pass, npasses, rows);
  552. point_binning_allocate(&point_binning, rows, cols, rtype);
  553. line = 0;
  554. count = 0;
  555. counter = 0;
  556. G_percent_reset();
  557. /* loop of input files */
  558. for (i = 0; i < infiles.num_items; i++) {
  559. infile = infiles.items[i];
  560. /* we already know file is there, so just do basic checks */
  561. LAS_reader = LASReader_Create(infile);
  562. if (LAS_reader == NULL)
  563. G_fatal_error(_("Unable to open file <%s>"), infile);
  564. while ((LAS_point = LASReader_GetNextPoint(LAS_reader)) != NULL) {
  565. line++;
  566. counter++;
  567. if (counter == 100000) { /* speed */
  568. if (line < estimated_lines)
  569. G_percent(line, estimated_lines, 3);
  570. counter = 0;
  571. }
  572. /* We always count them and report because behavior
  573. * changed in between 7.0 and 7.2 from undefined (but skipping
  574. * invalid points) to filtering them out only when requested. */
  575. if (!LASPoint_IsValid(LAS_point)) {
  576. n_invalid++;
  577. if (only_valid)
  578. continue;
  579. }
  580. x = LASPoint_GetX(LAS_point);
  581. y = LASPoint_GetY(LAS_point);
  582. if (intens_flag->answer)
  583. /* use intensity as z here to allow all filters (and
  584. * modifications) below to be applied for intensity */
  585. z = LASPoint_GetIntensity(LAS_point);
  586. else
  587. z = LASPoint_GetZ(LAS_point);
  588. int return_n = LASPoint_GetReturnNumber(LAS_point);
  589. int n_returns = LASPoint_GetNumberOfReturns(LAS_point);
  590. if (return_filter_is_out(&return_filter_struct, return_n, n_returns)) {
  591. n_filtered++;
  592. continue;
  593. }
  594. point_class = (int) LASPoint_GetClassification(LAS_point);
  595. if (class_filter_is_out(&class_filter, point_class))
  596. continue;
  597. if (y <= region.south || y > region.north) {
  598. continue;
  599. }
  600. if (x < region.west || x >= region.east) {
  601. continue;
  602. }
  603. /* find the bin in the current array box */
  604. arr_row = (int)((region.north - y) / region.ns_res) - row0;
  605. if (arr_row < 0 || arr_row >= rows)
  606. continue;
  607. arr_col = (int)((x - region.west) / region.ew_res);
  608. z = z * zscale;
  609. if (base_array) {
  610. double base_z;
  611. if (row_array_get_value_row_col(base_array, arr_row, arr_col,
  612. cols, base_raster_data_type,
  613. &base_z))
  614. z -= base_z;
  615. else
  616. continue;
  617. }
  618. else if (use_segment) {
  619. double base_z;
  620. if (rast_segment_get_value_xy(&base_segment, &input_region,
  621. base_raster_data_type, x, y,
  622. &base_z))
  623. z -= base_z;
  624. else
  625. continue;
  626. }
  627. if (zrange_opt->answer) {
  628. if (z < zrange_min || z > zrange_max) {
  629. continue;
  630. }
  631. }
  632. if (intens_import_flag->answer || irange_opt->answer) {
  633. intensity = LASPoint_GetIntensity(LAS_point);
  634. intensity *= iscale;
  635. if (irange_opt->answer) {
  636. if (intensity < irange_min || intensity > irange_max) {
  637. continue;
  638. }
  639. }
  640. /* use intensity for statistics */
  641. if (intens_import_flag->answer)
  642. z = intensity;
  643. }
  644. count++;
  645. /* G_debug(5, "x: %f, y: %f, z: %f", x, y, z); */
  646. update_value(&point_binning, &bin_index_nodes, cols,
  647. arr_row, arr_col, rtype, x, y, z);
  648. } /* while !EOF of one input file */
  649. /* close input LAS file */
  650. LASReader_Destroy(LAS_reader);
  651. } /* end of loop for all input files files */
  652. G_percent(1, 1, 1); /* flush */
  653. G_debug(2, "pass %d finished, %lu coordinates in box", pass, count);
  654. count_total += count;
  655. line_total += line;
  656. /* calc stats and output */
  657. G_message(_("Writing output raster map..."));
  658. for (row = 0; row < rows; row++) {
  659. /* potentially vector writing can be independent on the binning */
  660. write_values(&point_binning, &bin_index_nodes, raster_row, row,
  661. cols, rtype, NULL);
  662. G_percent(row, rows, 10);
  663. /* write out line of raster data */
  664. Rast_put_row(out_fd, raster_row, rtype);
  665. }
  666. /* free memory */
  667. point_binning_free(&point_binning, &bin_index_nodes);
  668. } /* passes loop */
  669. if (base_array)
  670. Rast_close(base_raster);
  671. if (use_segment)
  672. Segment_close(&base_segment);
  673. G_percent(1, 1, 1); /* flush */
  674. G_free(raster_row);
  675. G_message(_("%lu points found in input file(s)"), line_total);
  676. /* close raster file & write history */
  677. Rast_close(out_fd);
  678. sprintf(title, "Raw X,Y,Z data binned into a raster grid by cell %s",
  679. method_opt->answer);
  680. Rast_put_cell_title(outmap, title);
  681. Rast_short_history(outmap, "raster", &history);
  682. Rast_command_history(&history);
  683. Rast_set_history(&history, HIST_DATSRC_1, infile);
  684. Rast_write_history(outmap, &history);
  685. /* set computation region to the new raster map */
  686. /* TODO: should be in the done message */
  687. if (set_region_flag->answer)
  688. G_put_window(&region);
  689. if (n_invalid && only_valid)
  690. G_message(_("%lu input points were invalid and filtered out"),
  691. n_invalid);
  692. if (n_invalid && !only_valid)
  693. G_message(_("%lu input points were invalid, use -%c flag to filter"
  694. " them out"), n_invalid, only_valid_flag->key);
  695. if (infiles.num_items > 1) {
  696. sprintf(buff, _("Raster map <%s> created."
  697. " %lu points from %d files found in region."),
  698. outmap, count_total, infiles.num_items);
  699. }
  700. else {
  701. sprintf(buff, _("Raster map <%s> created."
  702. " %lu points found in region."),
  703. outmap, count_total);
  704. }
  705. G_done_msg("%s", buff);
  706. G_debug(1, "Processed %lu points.", line_total);
  707. string_list_free(&infiles);
  708. exit(EXIT_SUCCESS);
  709. }