1 /*
2 ===============================================================================
3 
4   FILE:  lasmerge.cpp
5 
6   CONTENTS:
7 
8     This tool merges multiple LAS file into a single LAS file and outputs it in
9     the LAS format. As input the user either provides multiple LAS file names or
10     a text file containing a list of LAS file names.
11 
12   PROGRAMMERS:
13 
14     martin.isenburg@rapidlasso.com  -  http://rapidlasso.com
15 
16   COPYRIGHT:
17 
18     (c) 2007-12, martin isenburg, rapidlasso - fast tools to catch reality
19 
20     This is free software; you can redistribute and/or modify it under the
21     terms of the GNU Lesser General Licence as published by the Free Software
22     Foundation. See the LICENSE.txt file for more information.
23 
24     This software is distributed WITHOUT ANY WARRANTY and without even the
25     implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
26 
27   CHANGE HISTORY:
28 
29     20 August 2014 -- new option '-keep_lastiling' to preserve the LAStiling VLR
30     20 August 2014 -- copy VLRs from empty (zero points) LAS/LAZ files to others
31      5 August 2011 -- possible to add/change projection info in command line
32     13 May 2011 -- moved indexing, filtering, transforming into LASreader
33     21 January 2011 -- added LASreadOpener and reading of multiple LAS files
34      7 January 2011 -- added the LASfilter to drop or keep points
35     12 March 2009 -- updated to ask for input if started without arguments
36     07 November 2007 -- created after email from luis.viveros@digimapas.cl
37 
38 ===============================================================================
39 */
40 
41 #include <time.h>
42 #include <stdlib.h>
43 #include <string.h>
44 
45 #include "lasreader.hpp"
46 #include "laswriter.hpp"
47 #include "geoprojectionconverter.hpp"
48 
usage(bool error=false,bool wait=false)49 void usage(bool error=false, bool wait=false)
50 {
51   fprintf(stderr,"usage:\n");
52   fprintf(stderr,"lasmerge -i *.las -o out.las\n");
53   fprintf(stderr,"lasmerge -lof lasfiles.txt -o out.las\n");
54   fprintf(stderr,"lasmerge -i *.las -o out0000.laz -split 1000000000\n");
55   fprintf(stderr,"lasmerge -i file1.las file2.las file3.las -o out.las\n");
56   fprintf(stderr,"lasmerge -i file1.las file2.las -reoffset 600000 4000000 0 -olas > out.las\n");
57   fprintf(stderr,"lasmerge -lof lasfiles.txt -rescale 0.01 0.01 0.01 -verbose -o out.las\n");
58   fprintf(stderr,"lasmerge -h\n");
59   if (wait)
60   {
61     fprintf(stderr,"<press ENTER>\n");
62     getc(stdin);
63   }
64   exit(error);
65 }
66 
byebye(bool error=false,bool wait=false)67 static void byebye(bool error=false, bool wait=false)
68 {
69   if (wait)
70   {
71     fprintf(stderr,"<press ENTER>\n");
72     getc(stdin);
73   }
74   exit(error);
75 }
76 
taketime()77 static double taketime()
78 {
79   return (double)(clock())/CLOCKS_PER_SEC;
80 }
81 
82 #ifdef COMPILE_WITH_GUI
83 extern int lasmerge_gui(int argc, char *argv[], LASreadOpener* lasreadopener);
84 #endif
85 
main(int argc,char * argv[])86 int main(int argc, char *argv[])
87 {
88   int i;
89 #ifdef COMPILE_WITH_GUI
90   bool gui = false;
91 #endif
92   bool verbose = false;
93   bool keep_lastiling = false;
94   U32 chopchop = 0;
95   bool projection_was_set = false;
96   double start_time = 0;
97 
98   LASreadOpener lasreadopener;
99   GeoProjectionConverter geoprojectionconverter;
100   LASwriteOpener laswriteopener;
101 
102   if (argc == 1)
103   {
104 #ifdef COMPILE_WITH_GUI
105     return lasmerge_gui(argc, argv, 0);
106 #else
107     fprintf(stderr,"%s is better run in the command line\n", argv[0]);
108     char file_name[256];
109     fprintf(stderr,"enter input file 1: "); fgets(file_name, 256, stdin);
110     file_name[strlen(file_name)-1] = '\0';
111     lasreadopener.add_file_name(file_name);
112     fprintf(stderr,"enter input file 2: "); fgets(file_name, 256, stdin);
113     file_name[strlen(file_name)-1] = '\0';
114     lasreadopener.add_file_name(file_name);
115     fprintf(stderr,"enter output file: "); fgets(file_name, 256, stdin);
116     file_name[strlen(file_name)-1] = '\0';
117     laswriteopener.set_file_name(file_name);
118 #endif
119   }
120   else
121   {
122     for (i = 1; i < argc; i++)
123     {
124       if (argv[i][0] == '�') argv[i][0] = '-';
125     }
126     if (!geoprojectionconverter.parse(argc, argv)) byebye(true);
127     if (!lasreadopener.parse(argc, argv)) byebye(true);
128     if (!laswriteopener.parse(argc, argv)) byebye(true);
129   }
130 
131   for (i = 1; i < argc; i++)
132   {
133     if (argv[i][0] == '\0')
134     {
135       continue;
136     }
137     else if (strcmp(argv[i],"-h") == 0 || strcmp(argv[i],"-help") == 0)
138     {
139       fprintf(stderr, "LAStools (by martin@rapidlasso.com) version %d\n", LAS_TOOLS_VERSION);
140       usage();
141     }
142     else if (strcmp(argv[i],"-v") == 0 || strcmp(argv[i],"-verbose") == 0)
143     {
144       verbose = true;
145     }
146     else if (strcmp(argv[i],"-version") == 0)
147     {
148       fprintf(stderr, "LAStools (by martin@rapidlasso.com) version %d\n", LAS_TOOLS_VERSION);
149       byebye();
150     }
151     else if (strcmp(argv[i],"-gui") == 0)
152     {
153 #ifdef COMPILE_WITH_GUI
154       gui = true;
155 #else
156       fprintf(stderr, "WARNING: not compiled with GUI support. ignoring '-gui' ...\n");
157 #endif
158     }
159     else if (strcmp(argv[i],"-split") == 0)
160     {
161       if ((i+1) >= argc)
162       {
163         fprintf(stderr,"ERROR: '%s' needs 1 argument: size\n", argv[i]);
164         byebye(true);
165       }
166       i++;
167       chopchop = atoi(argv[i]);
168     }
169     else if (strcmp(argv[i],"-keep_lastiling") == 0)
170     {
171       keep_lastiling = true;
172     }
173     else if ((argv[i][0] != '-') && (lasreadopener.get_file_name_number() == 0))
174     {
175       lasreadopener.add_file_name(argv[i]);
176       argv[i][0] = '\0';
177     }
178     else
179     {
180       fprintf(stderr, "ERROR: cannot understand argument '%s'\n", argv[i]);
181       byebye(true);
182     }
183   }
184 
185 #ifdef COMPILE_WITH_GUI
186   if (gui)
187   {
188     return lasmerge_gui(argc, argv, &lasreadopener);
189   }
190 #endif
191 
192   // read all the input files merged
193 
194   lasreadopener.set_merged(TRUE);
195 
196   // maybe we want to keep the lastiling
197 
198   if (keep_lastiling)
199   {
200     lasreadopener.set_keep_lastiling(TRUE);
201   }
202 
203   // we need to precompute the bounding box
204 
205   lasreadopener.set_populate_header(TRUE);
206 
207   // check input and output
208 
209   if (!lasreadopener.active())
210   {
211     fprintf(stderr, "ERROR: no input specified\n");
212     byebye(true, argc==1);
213   }
214 
215   if (!laswriteopener.active())
216   {
217     fprintf(stderr, "ERROR: no output specified\n");
218     byebye(true, argc==1);
219   }
220 
221   // make sure we do not corrupt the input file
222 
223   if (lasreadopener.get_file_name() && laswriteopener.get_file_name() && (strcmp(lasreadopener.get_file_name(), laswriteopener.get_file_name()) == 0))
224   {
225     fprintf(stderr, "ERROR: input and output file name are identical\n");
226     usage(true);
227   }
228 
229   // check if projection info was set in the command line
230 
231   int number_of_keys;
232   GeoProjectionGeoKeys* geo_keys = 0;
233   int num_geo_double_params;
234   double* geo_double_params = 0;
235 
236   if (geoprojectionconverter.has_projection())
237   {
238     projection_was_set = geoprojectionconverter.get_geo_keys_from_projection(number_of_keys, &geo_keys, num_geo_double_params, &geo_double_params);
239   }
240 
241   if (verbose) start_time = taketime();
242 
243   LASreader* lasreader = lasreadopener.open();
244   if (lasreader == 0)
245   {
246     fprintf(stderr, "ERROR: could not open lasreader\n");
247     byebye(true, argc==1);
248   }
249 
250 #ifdef _WIN32
251   if (verbose) { fprintf(stderr,"merging headers took %g sec. there are %I64d points in total.\n", taketime()-start_time, lasreader->npoints); start_time = taketime(); }
252 #else
253   if (verbose) { fprintf(stderr,"merging headers took %g sec. there are %lld points in total.\n", taketime()-start_time, lasreader->npoints); start_time = taketime(); }
254 #endif
255 
256   // prepare the header for the surviving points
257 
258   strncpy(lasreader->header.system_identifier, "LAStools (c) by rapidlasso GmbH", 32);
259   lasreader->header.system_identifier[31] = '\0';
260   char temp[64];
261   sprintf(temp, "lasmerge (version %d)", LAS_TOOLS_VERSION);
262   strncpy(lasreader->header.generating_software, temp, 32);
263   lasreader->header.generating_software[31] = '\0';
264 
265   if (projection_was_set)
266   {
267     lasreader->header.set_geo_keys(number_of_keys, (LASvlr_key_entry*)geo_keys);
268     free(geo_keys);
269     if (geo_double_params)
270     {
271       lasreader->header.set_geo_double_params(num_geo_double_params, geo_double_params);
272       free(geo_double_params);
273     }
274     else
275     {
276       lasreader->header.del_geo_double_params();
277     }
278     lasreader->header.del_geo_ascii_params();
279   }
280 
281   if (chopchop)
282   {
283     I32 file_number = 0;
284     LASwriter* laswriter = 0;
285     // loop over the points
286     while (lasreader->read_point())
287     {
288       if (laswriter == 0)
289       {
290         // open the next writer
291         laswriteopener.make_file_name(0, file_number);
292         file_number++;
293         laswriter = laswriteopener.open(&lasreader->header);
294       }
295       laswriter->write_point(&lasreader->point);
296       laswriter->update_inventory(&lasreader->point);
297       if (laswriter->p_count == chopchop)
298       {
299         // close the current writer
300         laswriter->update_header(&lasreader->header, TRUE);
301         laswriter->close();
302         if (verbose) { fprintf(stderr,"splitting file '%s' took %g sec.\n", laswriteopener.get_file_name(), taketime()-start_time); start_time = taketime(); }
303         delete laswriter;
304         laswriter = 0;
305       }
306     }
307     if (laswriter && laswriter->p_count)
308     {
309       // close the current writer
310       laswriter->update_header(&lasreader->header, TRUE);
311       laswriter->close();
312       if (verbose) { fprintf(stderr,"splitting file '%s' took %g sec.\n", laswriteopener.get_file_name(), taketime()-start_time); start_time = taketime(); }
313       delete laswriter;
314       laswriter = 0;
315     }
316   }
317   else
318   {
319     if (lasreader->npoints > U32_MAX)
320     {
321       if (lasreader->header.version_minor < 4)
322       {
323 #ifdef _WIN32
324         fprintf(stderr, "ERROR: cannot merge %I64d points into single LAS 1.%d file. maximum is %u\n", lasreader->npoints, lasreader->header.version_minor, U32_MAX);
325 #else
326         fprintf(stderr, "ERROR: cannot merge %lld points into single LAS 1.%d file. maximum is %u\n", lasreader->npoints, lasreader->header.version_minor, U32_MAX);
327 #endif
328         byebye(true, argc==1);
329       }
330     }
331 
332     // open the writer
333     LASwriter* laswriter = laswriteopener.open(&lasreader->header);
334     if (laswriter == 0)
335     {
336       fprintf(stderr, "ERROR: could not open laswriter\n");
337       byebye(true, argc==1);
338     }
339     // loop over the points
340     while (lasreader->read_point())
341     {
342       laswriter->write_point(&lasreader->point);
343       laswriter->update_inventory(&lasreader->point);
344     }
345     // close the writer
346     laswriter->update_header(&lasreader->header, TRUE);
347     laswriter->close();
348     if (verbose) fprintf(stderr,"merging files took %g sec.\n", taketime()-start_time);
349     delete laswriter;
350   }
351 
352   lasreader->close();
353   delete lasreader;
354 
355   byebye(false, argc==1);
356 
357   return 0;
358 }
359