main.c 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. /****************************************************************************
  2. *
  3. * MODULE: r.in.Lidar
  4. *
  5. * AUTHOR(S): Markus Metz
  6. * Based on r.in.xyz by Hamish Bowman, Volker Wichmann
  7. *
  8. * PURPOSE: Imports LAS LiDAR point clouds to a raster map using
  9. * aggregate statistics.
  10. *
  11. * COPYRIGHT: (C) 2011 Markus Metz and the The GRASS Development Team
  12. *
  13. * This program is free software under the GNU General Public
  14. * License (>=v2). Read the file COPYING that comes with GRASS
  15. * for details.
  16. *
  17. *****************************************************************************/
  18. #include <stdio.h>
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <unistd.h>
  22. #include <math.h>
  23. #include <sys/types.h>
  24. #include <grass/gis.h>
  25. #include <grass/raster.h>
  26. #include <grass/gprojects.h>
  27. #include <grass/glocale.h>
  28. #include "local_proto.h"
  29. struct node
  30. {
  31. int next;
  32. double z;
  33. };
  34. #define SIZE_INCREMENT 10
  35. int num_nodes = 0;
  36. int max_nodes = 0;
  37. struct node *nodes;
  38. int new_node(void)
  39. {
  40. int n = num_nodes++;
  41. if (num_nodes >= max_nodes) {
  42. max_nodes += SIZE_INCREMENT;
  43. nodes = G_realloc(nodes, (size_t)max_nodes * sizeof(struct node));
  44. }
  45. return n;
  46. }
  47. /* add node to sorted, single linked list
  48. * returns id if head has to be saved to index array, otherwise -1 */
  49. int add_node(int head, double z)
  50. {
  51. int node_id, last_id, newnode_id, head_id;
  52. head_id = head;
  53. node_id = head_id;
  54. last_id = head_id;
  55. while (node_id != -1 && nodes[node_id].z < z) {
  56. last_id = node_id;
  57. node_id = nodes[node_id].next;
  58. }
  59. /* end of list, simply append */
  60. if (node_id == -1) {
  61. newnode_id = new_node();
  62. nodes[newnode_id].next = -1;
  63. nodes[newnode_id].z = z;
  64. nodes[last_id].next = newnode_id;
  65. return -1;
  66. }
  67. else if (node_id == head_id) { /* pole position, insert as head */
  68. newnode_id = new_node();
  69. nodes[newnode_id].next = head_id;
  70. head_id = newnode_id;
  71. nodes[newnode_id].z = z;
  72. return (head_id);
  73. }
  74. else { /* somewhere in the middle, insert */
  75. newnode_id = new_node();
  76. nodes[newnode_id].z = z;
  77. nodes[newnode_id].next = node_id;
  78. nodes[last_id].next = newnode_id;
  79. return -1;
  80. }
  81. }
  82. int main(int argc, char *argv[])
  83. {
  84. int out_fd;
  85. char *infile, *outmap;
  86. int percent;
  87. int method = -1;
  88. int bin_n, bin_min, bin_max, bin_sum, bin_sumsq, bin_index;
  89. double zrange_min, zrange_max, d_tmp;
  90. unsigned long estimated_lines;
  91. RASTER_MAP_TYPE rtype;
  92. struct History history;
  93. char title[64];
  94. void *n_array, *min_array, *max_array, *sum_array, *sumsq_array,
  95. *index_array;
  96. void *raster_row, *ptr;
  97. struct Cell_head region;
  98. int rows, cols; /* scan box size */
  99. int row, col; /* counters */
  100. int pass, npasses;
  101. unsigned long line, line_total;
  102. unsigned int counter;
  103. char buff[BUFFSIZE];
  104. double x, y, z;
  105. double pass_north, pass_south;
  106. int arr_row, arr_col;
  107. unsigned long count, count_total;
  108. double min = 0.0 / 0.0; /* init as nan */
  109. double max = 0.0 / 0.0; /* init as nan */
  110. double zscale = 1.0;
  111. size_t offset, n_offset;
  112. int n = 0;
  113. double sum = 0.;
  114. double sumsq = 0.;
  115. double variance, mean, skew, sumdev;
  116. int pth = 0;
  117. double trim = 0.0;
  118. double res = 0.0;
  119. int j, k;
  120. int head_id, node_id;
  121. int r_low, r_up;
  122. struct GModule *module;
  123. struct Option *input_opt, *output_opt, *percent_opt, *type_opt;
  124. struct Option *method_opt, *zrange_opt, *zscale_opt;
  125. struct Option *trim_opt, *pth_opt, *res_opt;
  126. struct Flag *print_flag, *scan_flag, *shell_style, *over_flag, *extents_flag;
  127. /* LAS */
  128. LASReaderH LAS_reader;
  129. LASHeaderH LAS_header;
  130. LASSRSH LAS_srs;
  131. LASPointH LAS_point;
  132. struct Key_Value *loc_proj_info = NULL, *loc_proj_units = NULL;
  133. struct Key_Value *proj_info, *proj_units;
  134. const char *projstr;
  135. struct Cell_head cellhd, loc_wind;
  136. G_gisinit(argv[0]);
  137. module = G_define_module();
  138. G_add_keyword(_("raster"));
  139. G_add_keyword(_("import"));
  140. G_add_keyword(_("LIDAR"));
  141. module->description =
  142. _("Creates a raster map from LAS LiDAR points using univariate statistics.");
  143. input_opt = G_define_standard_option(G_OPT_F_INPUT);
  144. input_opt->label = _("LAS input file");
  145. input_opt->description = _("LiDAR input files in LAS format (*.las or *.laz)");
  146. output_opt = G_define_standard_option(G_OPT_R_OUTPUT);
  147. method_opt = G_define_option();
  148. method_opt->key = "method";
  149. method_opt->type = TYPE_STRING;
  150. method_opt->required = NO;
  151. method_opt->description = _("Statistic to use for raster values");
  152. method_opt->options =
  153. "n,min,max,range,sum,mean,stddev,variance,coeff_var,median,percentile,skewness,trimmean";
  154. method_opt->answer = "mean";
  155. method_opt->guisection = _("Statistic");
  156. type_opt = G_define_option();
  157. type_opt->key = "type";
  158. type_opt->type = TYPE_STRING;
  159. type_opt->required = NO;
  160. type_opt->options = "CELL,FCELL,DCELL";
  161. type_opt->answer = "FCELL";
  162. type_opt->description = _("Storage type for resultant raster map");
  163. zrange_opt = G_define_option();
  164. zrange_opt->key = "zrange";
  165. zrange_opt->type = TYPE_DOUBLE;
  166. zrange_opt->required = NO;
  167. zrange_opt->key_desc = "min,max";
  168. zrange_opt->description = _("Filter range for z data (min,max)");
  169. zscale_opt = G_define_option();
  170. zscale_opt->key = "zscale";
  171. zscale_opt->type = TYPE_DOUBLE;
  172. zscale_opt->required = NO;
  173. zscale_opt->answer = "1.0";
  174. zscale_opt->description = _("Scale to apply to z data");
  175. percent_opt = G_define_option();
  176. percent_opt->key = "percent";
  177. percent_opt->type = TYPE_INTEGER;
  178. percent_opt->required = NO;
  179. percent_opt->answer = "100";
  180. percent_opt->options = "1-100";
  181. percent_opt->description = _("Percent of map to keep in memory");
  182. /* I would prefer to call the following "percentile", but that has too
  183. * much namespace overlap with the "percent" option above */
  184. pth_opt = G_define_option();
  185. pth_opt->key = "pth";
  186. pth_opt->type = TYPE_INTEGER;
  187. pth_opt->required = NO;
  188. pth_opt->options = "1-100";
  189. pth_opt->description = _("pth percentile of the values");
  190. pth_opt->guisection = _("Statistic");
  191. trim_opt = G_define_option();
  192. trim_opt->key = "trim";
  193. trim_opt->type = TYPE_DOUBLE;
  194. trim_opt->required = NO;
  195. trim_opt->options = "0-50";
  196. trim_opt->description =
  197. _("Discard <trim> percent of the smallest and <trim> percent of the largest observations");
  198. trim_opt->guisection = _("Statistic");
  199. res_opt = G_define_option();
  200. res_opt->key = "resolution";
  201. res_opt->type = TYPE_DOUBLE;
  202. res_opt->required = NO;
  203. res_opt->description =
  204. _("Output raster resolution");
  205. print_flag = G_define_flag();
  206. print_flag->key = 'p';
  207. print_flag->description =
  208. _("Print LAS file info and exit");
  209. print_flag->suppress_required = YES;
  210. extents_flag = G_define_flag();
  211. extents_flag->key = 'e';
  212. extents_flag->description =
  213. _("Extend region extents based on new dataset");
  214. over_flag = G_define_flag();
  215. over_flag->key = 'o';
  216. over_flag->description =
  217. _("Override dataset projection (use location's projection)");
  218. scan_flag = G_define_flag();
  219. scan_flag->key = 's';
  220. scan_flag->description = _("Scan data file for extent then exit");
  221. scan_flag->suppress_required = YES;
  222. shell_style = G_define_flag();
  223. shell_style->key = 'g';
  224. shell_style->description =
  225. _("In scan mode, print using shell script style");
  226. if (G_parser(argc, argv))
  227. exit(EXIT_FAILURE);
  228. /* parse input values */
  229. infile = input_opt->answer;
  230. outmap = output_opt->answer;
  231. if (shell_style->answer && !scan_flag->answer) {
  232. scan_flag->answer = 1; /* pointer not int, so set = shell_style->answer ? */
  233. }
  234. /* Don't crash on cmd line if file not found */
  235. if (access(infile, F_OK) != 0) {
  236. G_fatal_error(_("Input file <%s> does not exist"), infile);
  237. }
  238. /* Open LAS file*/
  239. LAS_reader = LASReader_Create(infile);
  240. if (LAS_reader == NULL) {
  241. G_fatal_error(_("Unable to open file <%s>"), infile);
  242. }
  243. LAS_header = LASReader_GetHeader(LAS_reader);
  244. if (LAS_header == NULL) {
  245. G_fatal_error(_("Input file <%s> is not a LAS LiDAR point cloud"),
  246. infile);
  247. }
  248. LAS_srs = LASHeader_GetSRS(LAS_header);
  249. /* Print LAS header */
  250. if (print_flag->answer) {
  251. /* print... */
  252. print_lasinfo(LAS_header, LAS_srs);
  253. LASSRS_Destroy(LAS_srs);
  254. LASHeader_Destroy(LAS_header);
  255. LASReader_Destroy(LAS_reader);
  256. exit(EXIT_SUCCESS);
  257. }
  258. /* Fetch input map projection in GRASS form. */
  259. proj_info = NULL;
  260. proj_units = NULL;
  261. projstr = LASSRS_GetWKT_CompoundOK(LAS_srs);
  262. if (TRUE) {
  263. int err = 0;
  264. char error_msg[8192];
  265. /* Projection only required for checking so convert non-interactively */
  266. if (GPJ_wkt_to_grass(&cellhd, &proj_info,
  267. &proj_units, projstr, 0) < 0)
  268. G_warning(_("Unable to convert input map projection information to "
  269. "GRASS format for checking"));
  270. /* Does the projection of the current location match the dataset? */
  271. /* G_get_window seems to be unreliable if the location has been changed */
  272. G_get_set_window(&loc_wind);
  273. /* fetch LOCATION PROJ info */
  274. if (loc_wind.proj != PROJECTION_XY) {
  275. loc_proj_info = G_get_projinfo();
  276. loc_proj_units = G_get_projunits();
  277. }
  278. if (over_flag->answer) {
  279. cellhd.proj = loc_wind.proj;
  280. cellhd.zone = loc_wind.zone;
  281. G_message(_("Over-riding projection check"));
  282. }
  283. else if (loc_wind.proj != cellhd.proj
  284. || (err =
  285. G_compare_projections(loc_proj_info, loc_proj_units,
  286. proj_info, proj_units)) != TRUE) {
  287. int i_value;
  288. strcpy(error_msg,
  289. _("Projection of dataset does not"
  290. " appear to match current location.\n\n"));
  291. /* TODO: output this info sorted by key: */
  292. if (loc_wind.proj != cellhd.proj || err != -2) {
  293. if (loc_proj_info != NULL) {
  294. strcat(error_msg, _("GRASS LOCATION PROJ_INFO is:\n"));
  295. for (i_value = 0; i_value < loc_proj_info->nitems;
  296. i_value++)
  297. sprintf(error_msg + strlen(error_msg), "%s: %s\n",
  298. loc_proj_info->key[i_value],
  299. loc_proj_info->value[i_value]);
  300. strcat(error_msg, "\n");
  301. }
  302. if (proj_info != NULL) {
  303. strcat(error_msg, _("Import dataset PROJ_INFO is:\n"));
  304. for (i_value = 0; i_value < proj_info->nitems; i_value++)
  305. sprintf(error_msg + strlen(error_msg), "%s: %s\n",
  306. proj_info->key[i_value],
  307. proj_info->value[i_value]);
  308. }
  309. else {
  310. strcat(error_msg, _("Import dataset PROJ_INFO is:\n"));
  311. if (cellhd.proj == PROJECTION_XY)
  312. sprintf(error_msg + strlen(error_msg),
  313. "Dataset proj = %d (unreferenced/unknown)\n",
  314. cellhd.proj);
  315. else if (cellhd.proj == PROJECTION_LL)
  316. sprintf(error_msg + strlen(error_msg),
  317. "Dataset proj = %d (lat/long)\n",
  318. cellhd.proj);
  319. else if (cellhd.proj == PROJECTION_UTM)
  320. sprintf(error_msg + strlen(error_msg),
  321. "Dataset proj = %d (UTM), zone = %d\n",
  322. cellhd.proj, cellhd.zone);
  323. else if (cellhd.proj == PROJECTION_SP)
  324. sprintf(error_msg + strlen(error_msg),
  325. "Dataset proj = %d (State Plane), zone = %d\n",
  326. cellhd.proj, cellhd.zone);
  327. else
  328. sprintf(error_msg + strlen(error_msg),
  329. "Dataset proj = %d (unknown), zone = %d\n",
  330. cellhd.proj, cellhd.zone);
  331. }
  332. }
  333. else {
  334. if (loc_proj_units != NULL) {
  335. strcat(error_msg, "GRASS LOCATION PROJ_UNITS is:\n");
  336. for (i_value = 0; i_value < loc_proj_units->nitems;
  337. i_value++)
  338. sprintf(error_msg + strlen(error_msg), "%s: %s\n",
  339. loc_proj_units->key[i_value],
  340. loc_proj_units->value[i_value]);
  341. strcat(error_msg, "\n");
  342. }
  343. if (proj_units != NULL) {
  344. strcat(error_msg, "Import dataset PROJ_UNITS is:\n");
  345. for (i_value = 0; i_value < proj_units->nitems; i_value++)
  346. sprintf(error_msg + strlen(error_msg), "%s: %s\n",
  347. proj_units->key[i_value],
  348. proj_units->value[i_value]);
  349. }
  350. }
  351. sprintf(error_msg + strlen(error_msg),
  352. _("\nYou can use the -o flag to %s to override this projection check.\n"),
  353. G_program_name());
  354. strcat(error_msg,
  355. _("Consider generating a new location with 'location' parameter"
  356. " from input data set.\n"));
  357. G_fatal_error(error_msg);
  358. }
  359. else if (!shell_style->answer) {
  360. G_message(_("Projection of input dataset and current location "
  361. "appear to match"));
  362. }
  363. }
  364. percent = atoi(percent_opt->answer);
  365. zscale = atof(zscale_opt->answer);
  366. /* parse zrange */
  367. if (zrange_opt->answer != NULL) {
  368. if (zrange_opt->answers[0] == NULL)
  369. G_fatal_error(_("Invalid zrange"));
  370. sscanf(zrange_opt->answers[0], "%lf", &zrange_min);
  371. sscanf(zrange_opt->answers[1], "%lf", &zrange_max);
  372. if (zrange_min > zrange_max) {
  373. d_tmp = zrange_max;
  374. zrange_max = zrange_min;
  375. zrange_min = d_tmp;
  376. }
  377. }
  378. /* figure out what maps we need in memory */
  379. /* n n
  380. min min
  381. max max
  382. range min max max - min
  383. sum sum
  384. mean sum n sum/n
  385. stddev sum sumsq n sqrt((sumsq - sum*sum/n)/n)
  386. variance sum sumsq n (sumsq - sum*sum/n)/n
  387. coeff_var sum sumsq n sqrt((sumsq - sum*sum/n)/n) / (sum/n)
  388. median n array index to linked list
  389. percentile n array index to linked list
  390. skewness n array index to linked list
  391. trimmean n array index to linked list
  392. */
  393. bin_n = FALSE;
  394. bin_min = FALSE;
  395. bin_max = FALSE;
  396. bin_sum = FALSE;
  397. bin_sumsq = FALSE;
  398. bin_index = FALSE;
  399. n_array = NULL;
  400. min_array = NULL;
  401. max_array = NULL;
  402. sum_array = NULL;
  403. sumsq_array = NULL;
  404. index_array = NULL;
  405. if (strcmp(method_opt->answer, "n") == 0) {
  406. method = METHOD_N;
  407. bin_n = TRUE;
  408. }
  409. if (strcmp(method_opt->answer, "min") == 0) {
  410. method = METHOD_MIN;
  411. bin_min = TRUE;
  412. }
  413. if (strcmp(method_opt->answer, "max") == 0) {
  414. method = METHOD_MAX;
  415. bin_max = TRUE;
  416. }
  417. if (strcmp(method_opt->answer, "range") == 0) {
  418. method = METHOD_RANGE;
  419. bin_min = TRUE;
  420. bin_max = TRUE;
  421. }
  422. if (strcmp(method_opt->answer, "sum") == 0) {
  423. method = METHOD_SUM;
  424. bin_sum = TRUE;
  425. }
  426. if (strcmp(method_opt->answer, "mean") == 0) {
  427. method = METHOD_MEAN;
  428. bin_sum = TRUE;
  429. bin_n = TRUE;
  430. }
  431. if (strcmp(method_opt->answer, "stddev") == 0) {
  432. method = METHOD_STDDEV;
  433. bin_sum = TRUE;
  434. bin_sumsq = TRUE;
  435. bin_n = TRUE;
  436. }
  437. if (strcmp(method_opt->answer, "variance") == 0) {
  438. method = METHOD_VARIANCE;
  439. bin_sum = TRUE;
  440. bin_sumsq = TRUE;
  441. bin_n = TRUE;
  442. }
  443. if (strcmp(method_opt->answer, "coeff_var") == 0) {
  444. method = METHOD_COEFF_VAR;
  445. bin_sum = TRUE;
  446. bin_sumsq = TRUE;
  447. bin_n = TRUE;
  448. }
  449. if (strcmp(method_opt->answer, "median") == 0) {
  450. method = METHOD_MEDIAN;
  451. bin_index = TRUE;
  452. }
  453. if (strcmp(method_opt->answer, "percentile") == 0) {
  454. if (pth_opt->answer != NULL)
  455. pth = atoi(pth_opt->answer);
  456. else
  457. G_fatal_error(_("Unable to calculate percentile without the pth option specified!"));
  458. method = METHOD_PERCENTILE;
  459. bin_index = TRUE;
  460. }
  461. if (strcmp(method_opt->answer, "skewness") == 0) {
  462. method = METHOD_SKEWNESS;
  463. bin_index = TRUE;
  464. }
  465. if (strcmp(method_opt->answer, "trimmean") == 0) {
  466. if (trim_opt->answer != NULL)
  467. trim = atof(trim_opt->answer) / 100.0;
  468. else
  469. G_fatal_error(_("Unable to calculate trimmed mean without the trim option specified!"));
  470. method = METHOD_TRIMMEAN;
  471. bin_index = TRUE;
  472. }
  473. if (strcmp("CELL", type_opt->answer) == 0)
  474. rtype = CELL_TYPE;
  475. else if (strcmp("DCELL", type_opt->answer) == 0)
  476. rtype = DCELL_TYPE;
  477. else
  478. rtype = FCELL_TYPE;
  479. if (method == METHOD_N)
  480. rtype = CELL_TYPE;
  481. Rast_get_window(&region);
  482. if (scan_flag->answer || extents_flag->answer) {
  483. if (zrange_opt->answer)
  484. G_warning(_("zrange will not be taken into account during scan"));
  485. scan_bounds(LAS_reader, shell_style->answer, extents_flag->answer,
  486. zscale, &region);
  487. if (!extents_flag->answer) {
  488. LASHeader_Destroy(LAS_header);
  489. LASReader_Destroy(LAS_reader);
  490. exit(EXIT_SUCCESS);
  491. }
  492. }
  493. if (res_opt->answer) {
  494. /* align to resolution */
  495. res = atof(res_opt->answer);
  496. if (!G_scan_resolution(res_opt->answer, &res, region.proj))
  497. G_fatal_error(_("Invalid input <%s=%s>"), res_opt->key, res_opt->answer);
  498. if (res <= 0)
  499. G_fatal_error(_("Option '%s' must be > 0.0"), res_opt->key);
  500. region.ns_res = region.ew_res = res;
  501. region.north = ceil(region.north / res) * res;
  502. region.south = floor(region.south / res) * res;
  503. region.east = ceil(region.east / res) * res;
  504. region.west = floor(region.west / res) * res;
  505. G_adjust_Cell_head(&region, 0, 0);
  506. }
  507. else if (extents_flag->answer) {
  508. /* align to current region */
  509. Rast_align_window(&region, &loc_wind);
  510. }
  511. Rast_set_output_window(&region);
  512. rows = (int)(region.rows * (percent / 100.0));
  513. cols = region.cols;
  514. G_debug(2, "region.n=%f region.s=%f region.ns_res=%f", region.north,
  515. region.south, region.ns_res);
  516. G_debug(2, "region.rows=%d [box_rows=%d] region.cols=%d", region.rows,
  517. rows, region.cols);
  518. npasses = (int)ceil(1.0 * region.rows / rows);
  519. if (!scan_flag->answer) {
  520. /* check if rows * (cols + 1) go into a size_t */
  521. if (sizeof(size_t) < 8) {
  522. double dsize = rows * (cols + 1);
  523. if (dsize != (size_t)rows * (cols + 1))
  524. G_fatal_error(_("Unable to process the hole map at once. "
  525. "Please set the %s option to some value lower than 100."),
  526. percent_opt->key);
  527. }
  528. /* allocate memory (test for enough before we start) */
  529. if (bin_n)
  530. n_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
  531. if (bin_min)
  532. min_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  533. if (bin_max)
  534. max_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  535. if (bin_sum)
  536. sum_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  537. if (bin_sumsq)
  538. sumsq_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  539. if (bin_index)
  540. index_array =
  541. G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
  542. /* and then free it again */
  543. if (bin_n)
  544. G_free(n_array);
  545. if (bin_min)
  546. G_free(min_array);
  547. if (bin_max)
  548. G_free(max_array);
  549. if (bin_sum)
  550. G_free(sum_array);
  551. if (bin_sumsq)
  552. G_free(sumsq_array);
  553. if (bin_index)
  554. G_free(index_array);
  555. /** end memory test **/
  556. }
  557. /* open output map */
  558. out_fd = Rast_open_new(outmap, rtype);
  559. estimated_lines = LASHeader_GetPointRecordsCount(LAS_header);
  560. /* allocate memory for a single row of output data */
  561. raster_row = Rast_allocate_output_buf(rtype);
  562. G_message(_("Reading data ..."));
  563. count_total = line_total = 0;
  564. /* init northern border */
  565. pass_south = region.north;
  566. /* main binning loop(s) */
  567. for (pass = 1; pass <= npasses; pass++) {
  568. LASError LAS_error;
  569. if (npasses > 1)
  570. G_message(_("Pass #%d (of %d) ..."), pass, npasses);
  571. LAS_error = LASReader_Seek(LAS_reader, 0);
  572. if (LAS_error != LE_None)
  573. G_fatal_error(_("Could not rewind input file"));
  574. /* figure out segmentation */
  575. pass_north = pass_south; /* exact copy to avoid fp errors */
  576. pass_south = region.north - pass * rows * region.ns_res;
  577. if (pass == npasses) {
  578. rows = region.rows - (pass - 1) * rows;
  579. pass_south = region.south; /* exact copy to avoid fp errors */
  580. }
  581. G_debug(2, "pass=%d/%d pass_n=%f pass_s=%f rows=%d",
  582. pass, npasses, pass_north, pass_south, rows);
  583. if (bin_n) {
  584. G_debug(2, "allocating n_array");
  585. n_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
  586. blank_array(n_array, rows, cols, CELL_TYPE, 0);
  587. }
  588. if (bin_min) {
  589. G_debug(2, "allocating min_array");
  590. min_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  591. blank_array(min_array, rows, cols, rtype, -1); /* fill with NULLs */
  592. }
  593. if (bin_max) {
  594. G_debug(2, "allocating max_array");
  595. max_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  596. blank_array(max_array, rows, cols, rtype, -1); /* fill with NULLs */
  597. }
  598. if (bin_sum) {
  599. G_debug(2, "allocating sum_array");
  600. sum_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  601. blank_array(sum_array, rows, cols, rtype, 0);
  602. }
  603. if (bin_sumsq) {
  604. G_debug(2, "allocating sumsq_array");
  605. sumsq_array = G_calloc((size_t)rows * (cols + 1), Rast_cell_size(rtype));
  606. blank_array(sumsq_array, rows, cols, rtype, 0);
  607. }
  608. if (bin_index) {
  609. G_debug(2, "allocating index_array");
  610. index_array =
  611. G_calloc((size_t)rows * (cols + 1), Rast_cell_size(CELL_TYPE));
  612. blank_array(index_array, rows, cols, CELL_TYPE, -1); /* fill with NULLs */
  613. }
  614. line = 0;
  615. count = 0;
  616. counter = 0;
  617. G_percent_reset();
  618. while ((LAS_point = LASReader_GetNextPoint(LAS_reader)) != NULL) {
  619. line++;
  620. counter++;
  621. if (counter == 10000) { /* speed */
  622. if (line < estimated_lines)
  623. G_percent(line, estimated_lines, 3);
  624. counter = 0;
  625. }
  626. if (!LASPoint_IsValid(LAS_point)) {
  627. continue;
  628. }
  629. x = LASPoint_GetX(LAS_point);
  630. y = LASPoint_GetY(LAS_point);
  631. z = LASPoint_GetZ(LAS_point);
  632. if (y <= pass_south || y > pass_north) {
  633. continue;
  634. }
  635. if (x < region.west || x >= region.east) {
  636. continue;
  637. }
  638. z = z * zscale;
  639. if (zrange_opt->answer) {
  640. if (z < zrange_min || z > zrange_max) {
  641. continue;
  642. }
  643. }
  644. count++;
  645. /* G_debug(5, "x: %f, y: %f, z: %f", x, y, z); */
  646. /* find the bin in the current array box */
  647. arr_row = (int)((pass_north - y) / region.ns_res);
  648. arr_col = (int)((x - region.west) / region.ew_res);
  649. if (bin_n)
  650. update_n(n_array, cols, arr_row, arr_col);
  651. if (bin_min)
  652. update_min(min_array, cols, arr_row, arr_col, rtype, z);
  653. if (bin_max)
  654. update_max(max_array, cols, arr_row, arr_col, rtype, z);
  655. if (bin_sum)
  656. update_sum(sum_array, cols, arr_row, arr_col, rtype, z);
  657. if (bin_sumsq)
  658. update_sumsq(sumsq_array, cols, arr_row, arr_col, rtype, z);
  659. if (bin_index) {
  660. ptr = index_array;
  661. ptr =
  662. G_incr_void_ptr(ptr,
  663. ((arr_row * cols) +
  664. arr_col) * Rast_cell_size(CELL_TYPE));
  665. if (Rast_is_null_value(ptr, CELL_TYPE)) { /* first node */
  666. head_id = new_node();
  667. nodes[head_id].next = -1;
  668. nodes[head_id].z = z;
  669. Rast_set_c_value(ptr, head_id, CELL_TYPE); /* store index to head */
  670. }
  671. else { /* head is already there */
  672. head_id = Rast_get_c_value(ptr, CELL_TYPE); /* get index to head */
  673. head_id = add_node(head_id, z);
  674. if (head_id != -1)
  675. Rast_set_c_value(ptr, head_id, CELL_TYPE); /* store index to head */
  676. }
  677. }
  678. } /* while !EOF */
  679. G_percent(1, 1, 1); /* flush */
  680. G_debug(2, "pass %d finished, %lu coordinates in box", pass, count);
  681. count_total += count;
  682. line_total += line;
  683. /* calc stats and output */
  684. G_message(_("Writing to map ..."));
  685. for (row = 0; row < rows; row++) {
  686. switch (method) {
  687. case METHOD_N: /* n is a straight copy */
  688. Rast_raster_cpy(raster_row,
  689. n_array +
  690. (row * cols * Rast_cell_size(CELL_TYPE)), cols,
  691. CELL_TYPE);
  692. break;
  693. case METHOD_MIN:
  694. Rast_raster_cpy(raster_row,
  695. min_array + (row * cols * Rast_cell_size(rtype)),
  696. cols, rtype);
  697. break;
  698. case METHOD_MAX:
  699. Rast_raster_cpy(raster_row,
  700. max_array + (row * cols * Rast_cell_size(rtype)),
  701. cols, rtype);
  702. break;
  703. case METHOD_SUM:
  704. Rast_raster_cpy(raster_row,
  705. sum_array + (row * cols * Rast_cell_size(rtype)),
  706. cols, rtype);
  707. break;
  708. case METHOD_RANGE: /* (max-min) */
  709. ptr = raster_row;
  710. for (col = 0; col < cols; col++) {
  711. offset = (row * cols + col) * Rast_cell_size(rtype);
  712. min = Rast_get_d_value(min_array + offset, rtype);
  713. max = Rast_get_d_value(max_array + offset, rtype);
  714. Rast_set_d_value(ptr, max - min, rtype);
  715. ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
  716. }
  717. break;
  718. case METHOD_MEAN: /* (sum / n) */
  719. ptr = raster_row;
  720. for (col = 0; col < cols; col++) {
  721. offset = (row * cols + col) * Rast_cell_size(rtype);
  722. n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
  723. n = Rast_get_c_value(n_array + n_offset, CELL_TYPE);
  724. sum = Rast_get_d_value(sum_array + offset, rtype);
  725. if (n == 0)
  726. Rast_set_null_value(ptr, 1, rtype);
  727. else
  728. Rast_set_d_value(ptr, (sum / n), rtype);
  729. ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
  730. }
  731. break;
  732. case METHOD_STDDEV: /* sqrt(variance) */
  733. case METHOD_VARIANCE: /* (sumsq - sum*sum/n)/n */
  734. case METHOD_COEFF_VAR: /* 100 * stdev / mean */
  735. ptr = raster_row;
  736. for (col = 0; col < cols; col++) {
  737. offset = (row * cols + col) * Rast_cell_size(rtype);
  738. n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
  739. n = Rast_get_c_value(n_array + n_offset, CELL_TYPE);
  740. sum = Rast_get_d_value(sum_array + offset, rtype);
  741. sumsq = Rast_get_d_value(sumsq_array + offset, rtype);
  742. if (n == 0)
  743. Rast_set_null_value(ptr, 1, rtype);
  744. else if (n == 1)
  745. Rast_set_d_value(ptr, 0.0, rtype);
  746. else {
  747. variance = (sumsq - sum * sum / n) / n;
  748. if (variance < GRASS_EPSILON)
  749. variance = 0.0;
  750. /* nan test */
  751. if (variance != variance)
  752. Rast_set_null_value(ptr, 1, rtype);
  753. else {
  754. if (method == METHOD_STDDEV)
  755. variance = sqrt(variance);
  756. else if (method == METHOD_COEFF_VAR)
  757. variance = 100 * sqrt(variance) / (sum / n);
  758. /* nan test */
  759. if (variance != variance)
  760. variance = 0.0; /* OK for n > 0 ?*/
  761. Rast_set_d_value(ptr, variance, rtype);
  762. }
  763. }
  764. ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
  765. }
  766. break;
  767. case METHOD_MEDIAN: /* median, if only one point in cell we will use that */
  768. ptr = raster_row;
  769. for (col = 0; col < cols; col++) {
  770. n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
  771. if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
  772. Rast_set_null_value(ptr, 1, rtype);
  773. else { /* one or more points in cell */
  774. head_id =
  775. Rast_get_c_value(index_array + n_offset,
  776. CELL_TYPE);
  777. node_id = head_id;
  778. n = 0;
  779. while (node_id != -1) { /* count number of points in cell */
  780. n++;
  781. node_id = nodes[node_id].next;
  782. }
  783. if (n == 1) /* only one point, use that */
  784. Rast_set_d_value(ptr, nodes[head_id].z,
  785. rtype);
  786. else if (n % 2 != 0) { /* odd number of points: median_i = (n + 1) / 2 */
  787. n = (n + 1) / 2;
  788. node_id = head_id;
  789. for (j = 1; j < n; j++) /* get "median element" */
  790. node_id = nodes[node_id].next;
  791. Rast_set_d_value(ptr, nodes[node_id].z,
  792. rtype);
  793. }
  794. else { /* even number of points: median = (val_below + val_above) / 2 */
  795. z = (n + 1) / 2.0;
  796. n = floor(z);
  797. node_id = head_id;
  798. for (j = 1; j < n; j++) /* get element "below" */
  799. node_id = nodes[node_id].next;
  800. z = (nodes[node_id].z +
  801. nodes[nodes[node_id].next].z) / 2;
  802. Rast_set_d_value(ptr, z, rtype);
  803. }
  804. }
  805. ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
  806. }
  807. break;
  808. case METHOD_PERCENTILE: /* rank = (pth*(n+1))/100; interpolate linearly */
  809. ptr = raster_row;
  810. for (col = 0; col < cols; col++) {
  811. n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
  812. if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
  813. Rast_set_null_value(ptr, 1, rtype);
  814. else {
  815. head_id =
  816. Rast_get_c_value(index_array + n_offset,
  817. CELL_TYPE);
  818. node_id = head_id;
  819. n = 0;
  820. while (node_id != -1) { /* count number of points in cell */
  821. n++;
  822. node_id = nodes[node_id].next;
  823. }
  824. z = (pth * (n + 1)) / 100.0;
  825. r_low = floor(z); /* lower rank */
  826. if (r_low < 1)
  827. r_low = 1;
  828. else if (r_low > n)
  829. r_low = n;
  830. r_up = ceil(z); /* upper rank */
  831. if (r_up > n)
  832. r_up = n;
  833. node_id = head_id;
  834. for (j = 1; j < r_low; j++) /* search lower value */
  835. node_id = nodes[node_id].next;
  836. z = nodes[node_id].z; /* save lower value */
  837. node_id = head_id;
  838. for (j = 1; j < r_up; j++) /* search upper value */
  839. node_id = nodes[node_id].next;
  840. z = (z + nodes[node_id].z) / 2;
  841. Rast_set_d_value(ptr, z, rtype);
  842. }
  843. ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
  844. }
  845. break;
  846. case METHOD_SKEWNESS: /* skewness = sum(xi-mean)^3/(N-1)*s^3 */
  847. ptr = raster_row;
  848. for (col = 0; col < cols; col++) {
  849. n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
  850. if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
  851. Rast_set_null_value(ptr, 1, rtype);
  852. else {
  853. head_id =
  854. Rast_get_c_value(index_array + n_offset,
  855. CELL_TYPE);
  856. node_id = head_id;
  857. n = 0; /* count */
  858. sum = 0.0; /* sum */
  859. sumsq = 0.0; /* sum of squares */
  860. sumdev = 0.0; /* sum of (xi - mean)^3 */
  861. skew = 0.0; /* skewness */
  862. while (node_id != -1) {
  863. z = nodes[node_id].z;
  864. n++;
  865. sum += z;
  866. sumsq += (z * z);
  867. node_id = nodes[node_id].next;
  868. }
  869. if (n > 1) { /* if n == 1, skew is "0.0" */
  870. mean = sum / n;
  871. node_id = head_id;
  872. while (node_id != -1) {
  873. z = nodes[node_id].z;
  874. sumdev += pow((z - mean), 3);
  875. node_id = nodes[node_id].next;
  876. }
  877. variance = (sumsq - sum * sum / n) / n;
  878. if (variance < GRASS_EPSILON)
  879. skew = 0.0;
  880. else
  881. skew =
  882. sumdev / ((n - 1) *
  883. pow(sqrt(variance), 3));
  884. }
  885. Rast_set_d_value(ptr, skew, rtype);
  886. }
  887. ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
  888. }
  889. break;
  890. case METHOD_TRIMMEAN:
  891. ptr = raster_row;
  892. for (col = 0; col < cols; col++) {
  893. n_offset = (row * cols + col) * Rast_cell_size(CELL_TYPE);
  894. if (Rast_is_null_value(index_array + n_offset, CELL_TYPE)) /* no points in cell */
  895. Rast_set_null_value(ptr, 1, rtype);
  896. else {
  897. head_id =
  898. Rast_get_c_value(index_array + n_offset,
  899. CELL_TYPE);
  900. node_id = head_id;
  901. n = 0;
  902. while (node_id != -1) { /* count number of points in cell */
  903. n++;
  904. node_id = nodes[node_id].next;
  905. }
  906. if (1 == n)
  907. mean = nodes[head_id].z;
  908. else {
  909. k = floor(trim * n + 0.5); /* number of ranks to discard on each tail */
  910. if (k > 0 && (n - 2 * k) > 0) { /* enough elements to discard */
  911. node_id = head_id;
  912. for (j = 0; j < k; j++) /* move to first rank to consider */
  913. node_id = nodes[node_id].next;
  914. j = k + 1;
  915. k = n - k;
  916. n = 0;
  917. sum = 0.0;
  918. while (j <= k) { /* get values in interval */
  919. n++;
  920. sum += nodes[node_id].z;
  921. node_id = nodes[node_id].next;
  922. j++;
  923. }
  924. }
  925. else {
  926. node_id = head_id;
  927. n = 0;
  928. sum = 0.0;
  929. while (node_id != -1) {
  930. n++;
  931. sum += nodes[node_id].z;
  932. node_id = nodes[node_id].next;
  933. }
  934. }
  935. mean = sum / n;
  936. }
  937. Rast_set_d_value(ptr, mean, rtype);
  938. }
  939. ptr = G_incr_void_ptr(ptr, Rast_cell_size(rtype));
  940. }
  941. break;
  942. default:
  943. G_fatal_error("?");
  944. }
  945. /* write out line of raster data */
  946. Rast_put_row(out_fd, raster_row, rtype);
  947. }
  948. /* free memory */
  949. if (bin_n)
  950. G_free(n_array);
  951. if (bin_min)
  952. G_free(min_array);
  953. if (bin_max)
  954. G_free(max_array);
  955. if (bin_sum)
  956. G_free(sum_array);
  957. if (bin_sumsq)
  958. G_free(sumsq_array);
  959. if (bin_index) {
  960. G_free(index_array);
  961. G_free(nodes);
  962. num_nodes = 0;
  963. max_nodes = 0;
  964. nodes = NULL;
  965. }
  966. } /* passes loop */
  967. G_percent(1, 1, 1); /* flush */
  968. G_free(raster_row);
  969. /* close input LAS file */
  970. LASHeader_Destroy(LAS_header);
  971. LASReader_Destroy(LAS_reader);
  972. /* close raster file & write history */
  973. Rast_close(out_fd);
  974. sprintf(title, "Raw x,y,z data binned into a raster grid by cell %s",
  975. method_opt->answer);
  976. Rast_put_cell_title(outmap, title);
  977. Rast_short_history(outmap, "raster", &history);
  978. Rast_command_history(&history);
  979. Rast_set_history(&history, HIST_DATSRC_1, infile);
  980. Rast_write_history(outmap, &history);
  981. sprintf(buff, _("%lu points found in region."), count_total);
  982. G_done_msg(buff);
  983. G_debug(1, "Processed %lu points.", line_total);
  984. exit(EXIT_SUCCESS);
  985. }
  986. void print_lasinfo(LASHeaderH LAS_header, LASSRSH LAS_srs)
  987. {
  988. char *las_srs_proj4 = LASSRS_GetProj4(LAS_srs);
  989. int las_point_format = LASHeader_GetDataFormatId(LAS_header);
  990. fprintf(stdout, "\nUsing LAS Library Version '%s'\n\n",
  991. LAS_GetFullVersion());
  992. fprintf(stdout, "LAS File Version: %d.%d\n",
  993. LASHeader_GetVersionMajor(LAS_header),
  994. LASHeader_GetVersionMinor(LAS_header));
  995. fprintf(stdout, "System ID: '%s'\n",
  996. LASHeader_GetSystemId(LAS_header));
  997. fprintf(stdout, "Generating Software: '%s'\n",
  998. LASHeader_GetSoftwareId(LAS_header));
  999. fprintf(stdout, "File Creation Day/Year: %d/%d\n",
  1000. LASHeader_GetCreationDOY(LAS_header),
  1001. LASHeader_GetCreationYear(LAS_header));
  1002. fprintf(stdout, "Point Data Format: %d\n",
  1003. las_point_format);
  1004. fprintf(stdout, "Number of Point Records: %d\n",
  1005. LASHeader_GetPointRecordsCount(LAS_header));
  1006. fprintf(stdout, "Number of Points by Return: %d %d %d %d %d\n",
  1007. LASHeader_GetPointRecordsByReturnCount(LAS_header, 0),
  1008. LASHeader_GetPointRecordsByReturnCount(LAS_header, 1),
  1009. LASHeader_GetPointRecordsByReturnCount(LAS_header, 2),
  1010. LASHeader_GetPointRecordsByReturnCount(LAS_header, 3),
  1011. LASHeader_GetPointRecordsByReturnCount(LAS_header, 4));
  1012. fprintf(stdout, "Scale Factor X Y Z: %g %g %g\n",
  1013. LASHeader_GetScaleX(LAS_header),
  1014. LASHeader_GetScaleY(LAS_header),
  1015. LASHeader_GetScaleZ(LAS_header));
  1016. fprintf(stdout, "Offset X Y Z: %g %g %g\n",
  1017. LASHeader_GetOffsetX(LAS_header),
  1018. LASHeader_GetOffsetY(LAS_header),
  1019. LASHeader_GetOffsetZ(LAS_header));
  1020. fprintf(stdout, "Min X Y Z: %g %g %g\n",
  1021. LASHeader_GetMinX(LAS_header),
  1022. LASHeader_GetMinY(LAS_header),
  1023. LASHeader_GetMinZ(LAS_header));
  1024. fprintf(stdout, "Max X Y Z: %g %g %g\n",
  1025. LASHeader_GetMaxX(LAS_header),
  1026. LASHeader_GetMaxY(LAS_header),
  1027. LASHeader_GetMaxZ(LAS_header));
  1028. if (las_srs_proj4 && strlen(las_srs_proj4) > 0) {
  1029. fprintf(stdout, "Spatial Reference:\n");
  1030. fprintf(stdout, "%s\n", las_srs_proj4);
  1031. }
  1032. else {
  1033. fprintf(stdout, "Spatial Reference: None\n");
  1034. }
  1035. fprintf(stdout, "\nData Fields:\n");
  1036. fprintf(stdout, " 'X'\n 'Y'\n 'Z'\n 'Intensity'\n 'Return Number'\n");
  1037. fprintf(stdout, " 'Number of Returns'\n 'Scan Direction'\n");
  1038. fprintf(stdout, " 'Flighline Edge'\n 'Classification'\n 'Scan Angle Rank'\n");
  1039. fprintf(stdout, " 'User Data'\n 'Point Source ID'\n");
  1040. if (las_point_format == 1 || las_point_format == 3 || las_point_format == 4 || las_point_format == 5) {
  1041. fprintf(stdout, " 'GPS Time'\n");
  1042. }
  1043. if (las_point_format == 2 || las_point_format == 3 || las_point_format == 5) {
  1044. fprintf(stdout, " 'Red'\n 'Green'\n 'Blue'\n");
  1045. }
  1046. fprintf(stdout, "\n");
  1047. fflush(stdout);
  1048. return;
  1049. }
  1050. int scan_bounds(LASReaderH LAS_reader, int shell_style, int extents,
  1051. double zscale, struct Cell_head *region)
  1052. {
  1053. unsigned long line;
  1054. int first;
  1055. double min_x, max_x, min_y, max_y, min_z, max_z;
  1056. double x, y, z;
  1057. LASPointH LAS_point;
  1058. line = 0;
  1059. first = TRUE;
  1060. /* init to nan in case no points are found */
  1061. min_x = max_x = min_y = max_y = min_z = max_z = 0.0 / 0.0;
  1062. G_verbose_message(_("Scanning data ..."));
  1063. LASReader_Seek(LAS_reader, 0);
  1064. while ((LAS_point = LASReader_GetNextPoint(LAS_reader)) != NULL) {
  1065. line++;
  1066. if (!LASPoint_IsValid(LAS_point)) {
  1067. continue;
  1068. }
  1069. x = LASPoint_GetX(LAS_point);
  1070. y = LASPoint_GetY(LAS_point);
  1071. z = LASPoint_GetZ(LAS_point);
  1072. if (first) {
  1073. min_x = x;
  1074. max_x = x;
  1075. min_y = y;
  1076. max_y = y;
  1077. min_z = z;
  1078. max_z = z;
  1079. first = FALSE;
  1080. }
  1081. else {
  1082. if (x < min_x)
  1083. min_x = x;
  1084. if (x > max_x)
  1085. max_x = x;
  1086. if (y < min_y)
  1087. min_y = y;
  1088. if (y > max_y)
  1089. max_y = y;
  1090. if (z < min_z)
  1091. min_z = z;
  1092. if (z > max_z)
  1093. max_z = z;
  1094. }
  1095. }
  1096. if (!extents) {
  1097. if (!shell_style) {
  1098. fprintf(stderr, _("Range: min max\n"));
  1099. fprintf(stdout, "x: %11f %11f\n", min_x, max_x);
  1100. fprintf(stdout, "y: %11f %11f\n", min_y, max_y);
  1101. fprintf(stdout, "z: %11f %11f\n", min_z * zscale, max_z * zscale);
  1102. }
  1103. else
  1104. fprintf(stdout, "n=%f s=%f e=%f w=%f b=%f t=%f\n",
  1105. max_y, min_y, max_x, min_x, min_z * zscale, max_z * zscale);
  1106. G_debug(1, "Processed %lu points.", line);
  1107. G_debug(1, "region template: g.region n=%f s=%f e=%f w=%f",
  1108. max_y, min_y, max_x, min_x);
  1109. }
  1110. else {
  1111. region->east = max_x;
  1112. region->west = min_x;
  1113. region->north = max_y;
  1114. region->south = min_y;
  1115. }
  1116. return 0;
  1117. }