1 /***************************************************************************
2  *
3  * Project: libLAS -- C/C++ read/write library for LAS LIDAR data
4  * Purpose: LAS translation to PostgreSQL binary format with optional configuration
5  * Author:  Oscar Martinez Rubi o.rubi@esciencecenter.nl
6  ***************************************************************************
7  * This tool has been developed by the Netherlands eScience Center
8  * (https://www.esciencecenter.nl/)
9  *
10  * Copyright (c) 2016, Oscar Martinez Rubi o.rubi@esciencecenter.nl
11  *
12  * See LICENSE.txt in this source distribution for more information.
13  **************************************************************************/
14 
15 #include <time.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <netinet/in.h>
20 #include <stdint.h>
21 #include <limits.h>
22 #include <inttypes.h>
23 #if defined(__linux__) || defined(__CYGWIN__) || defined(__FreeBSD_kernel__) || defined(__GNU__)
24 
25 #   include <endian.h>
26 
27 #elif defined(__APPLE__)
28 
29 #   include <libkern/OSByteOrder.h>
30 
31 #   define htobe16(x) OSSwapHostToBigInt16(x)
32 #   define htole16(x) OSSwapHostToLittleInt16(x)
33 #   define be16toh(x) OSSwapBigToHostInt16(x)
34 #   define le16toh(x) OSSwapLittleToHostInt16(x)
35 
36 #   define htobe32(x) OSSwapHostToBigInt32(x)
37 #   define htole32(x) OSSwapHostToLittleInt32(x)
38 #   define be32toh(x) OSSwapBigToHostInt32(x)
39 #   define le32toh(x) OSSwapLittleToHostInt32(x)
40 
41 #   define htobe64(x) OSSwapHostToBigInt64(x)
42 #   define htole64(x) OSSwapHostToLittleInt64(x)
43 #   define be64toh(x) OSSwapBigToHostInt64(x)
44 #   define le64toh(x) OSSwapLittleToHostInt64(x)
45 
46 #   define __BYTE_ORDER    BYTE_ORDER
47 #   define __BIG_ENDIAN    BIG_ENDIAN
48 #   define __LITTLE_ENDIAN LITTLE_ENDIAN
49 #   define __PDP_ENDIAN    PDP_ENDIAN
50 
51 #elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
52 
53 #   include <sys/endian.h>
54 
55 #elif defined(__NetBSD__)
56 
57 #   include <sys/endian.h>
58 
59 #   define be16toh(x) betoh16(x)
60 #   define le16toh(x) letoh16(x)
61 
62 #   define be32toh(x) betoh32(x)
63 #   define le32toh(x) letoh32(x)
64 
65 #   define be64toh(x) betoh64(x)
66 #   define le64toh(x) letoh64(x)
67 
68 #elif defined(__WINDOWS__)
69 
70 #   include <winsock2.h>
71 #   include <sys/param.h>
72 
73 #   if BYTE_ORDER == LITTLE_ENDIAN
74 
75 #       define htobe16(x) htons(x)
76 #       define htole16(x) (x)
77 #       define be16toh(x) ntohs(x)
78 #       define le16toh(x) (x)
79 
80 #       define htobe32(x) htonl(x)
81 #       define htole32(x) (x)
82 #       define be32toh(x) ntohl(x)
83 #       define le32toh(x) (x)
84 
85 #       define htobe64(x) htonll(x)
86 #       define htole64(x) (x)
87 #       define be64toh(x) ntohll(x)
88 #       define le64toh(x) (x)
89 
90 #   elif BYTE_ORDER == BIG_ENDIAN
91 
92         /* that would be xbox 360 */
93 #       define htobe16(x) (x)
94 #       define htole16(x) __builtin_bswap16(x)
95 #       define be16toh(x) (x)
96 #       define le16toh(x) __builtin_bswap16(x)
97 
98 #       define htobe32(x) (x)
99 #       define htole32(x) __builtin_bswap32(x)
100 #       define be32toh(x) (x)
101 #       define le32toh(x) __builtin_bswap32(x)
102 
103 #       define htobe64(x) (x)
104 #       define htole64(x) __builtin_bswap64(x)
105 #       define be64toh(x) (x)
106 #       define le64toh(x) __builtin_bswap64(x)
107 
108 #   else
109 
110 #       error byte order not supported
111 
112 #   endif
113 
114 #   define __BYTE_ORDER    BYTE_ORDER
115 #   define __BIG_ENDIAN    BIG_ENDIAN
116 #   define __LITTLE_ENDIAN LITTLE_ENDIAN
117 #   define __PDP_ENDIAN    PDP_ENDIAN
118 
119 #else
120 
121 #   error platform not supported
122 
123 #endif
124 
125 #include <math.h>
126 
127 #include "liblas.h"
128 
129 #ifndef _BSD_SOURCE
130 #define _BSD_SOURCE
131 #endif
132 
133 #define combine(a,b,c) ( (a) = ((unsigned long long)(b) << 32) | (c) )
134 
135 unsigned int unity = 1;
136 #define is_littleEndian() (*(unsigned char *)&unity) // will return 1 if little endian, otherwise 0
137 
138 #define TOLERANCE 0.0000001
139 #define MAX_INT_31 2147483648.0
140 
141 #define VERSION "1.1"
142 
143 
144 struct postHeader {
145     char *s;
146     uint32_t i1;
147     uint32_t i2;
148 };
149 
150 struct postRow {
151     uint16_t h;
152     uint32_t varSize;
153     uint32_t vardSize;
154 };
155 
156 void print_header(FILE *file, LASHeaderH header, const char* file_name);
157 
usage()158 void usage()
159 {
160     fprintf(stderr,"----------------------------------------------------------\n");
161     fprintf(stderr,"    las2pg (version %s) usage:\n", VERSION);
162     fprintf(stderr,"----------------------------------------------------------\n");
163     fprintf(stderr,"\n");
164 
165     fprintf(stderr,"Convert a las/laz file into PostgreSQL binary dump format, outputs <input_file>:\n");
166     fprintf(stderr,"  las2pg -i <input_file>.las\n");
167     fprintf(stderr,"\n");
168 
169     fprintf(stderr,"Convert a las/laz file into PostgreSQL binary dump format, outputs <output_name>:\n");
170     fprintf(stderr,"  las2pg -i <input_file>.las -o <output_name>\n");
171     fprintf(stderr,"Use flag --stdout to write to standard output (recommended use together with a pipe, see below).\n");
172     fprintf(stderr,"\n\n");
173 
174     fprintf(stderr,"----------------------------------------------------------\n");
175     fprintf(stderr," The '--parse txyzia' flag specifies what exactly to\n");
176     fprintf(stderr," write for each row (default is --parse xyz). For example, 'txyzia'\n");
177     fprintf(stderr," means that the first field of each row will be the\n");
178     fprintf(stderr," gpstime, the next three fields will be the x, y, and\n");
179     fprintf(stderr," z coordinates, the next field will be the intensity\n");
180     fprintf(stderr," and the next field will be the scan angle.\n");
181     fprintf(stderr," The supported entries are:\n");
182     fprintf(stderr,"   t - gpstime as double\n");
183     fprintf(stderr,"   x - x coordinate as double\n");
184     fprintf(stderr,"   y - y coordinate as double\n");
185     fprintf(stderr,"   z - z coordinate as double\n");
186     fprintf(stderr,"   a - scan angle as integer\n");
187     fprintf(stderr,"   i - intensity as integer\n");
188     fprintf(stderr,"   n - number of returns for given pulse as integer\n");
189     fprintf(stderr,"   r - number of this return as integer\n");
190     fprintf(stderr,"   c - classification number as integer\n");
191     fprintf(stderr,"   u - user data as integer\n");
192     fprintf(stderr,"   p - point source ID as integer\n");
193     fprintf(stderr,"   e - edge of flight line as integer\n");
194     fprintf(stderr,"   d - direction of scan flag as integer\n");
195     fprintf(stderr,"   R - red channel of RGB color as integer\n");
196     fprintf(stderr,"   G - green channel of RGB color as integer\n");
197     fprintf(stderr,"   B - blue channel of RGB color as integer\n");
198     fprintf(stderr,"   M - vertex index number as integer\n");
199     fprintf(stderr,"   k - Morton 2D code using X and Y (unscaled and no offset) as bigint\n\n");
200 
201     fprintf(stderr," The moffset flag (for example '--moffset 8600000,40000000') specifies a global offset to subtract to X and Y \n");
202     fprintf(stderr," to be used when computing the Morton 2D code. Values must be unscaled \n\n");
203 
204     fprintf(stderr," The check flag (for example '--check 0.01,0.01') checks suitability to compute Morton 2D codes \n");
205     fprintf(stderr," It checks specified scale matches the one in input file. \n");
206     fprintf(stderr," If moffset is provided it also checks that obtained Morton 2D codes \n");
207     fprintf(stderr," will be consistent, i.e. global X,Y within [0,2^31] \n\n");
208 
209     fprintf(stderr,"----------------------------------------------------------\n");
210 
211     fprintf(stderr," The intended use of this tool is by using the --stdout flag and a pipe to avoid storing intermediate files. Example: \n");
212     fprintf(stderr,"    las2pg 1.2-with-color.laz --parse xyzRGBi --stdout | psql -c \"copy flat from stdin with binary\" \n");
213     fprintf(stderr," This obviously require a table called flat to be created in a PostgreSQL DB beforehand. The table must have \n");
214     fprintf(stderr," the columns in the same order as specified by the --parse option, and the column types must be the ones specified above. Example: \n");
215     fprintf(stderr,"    psql -c \"create table flat (x double precision, y double precision, z double precision, r integer, g integer, b integer, i integer)\" \n\n");
216 }
217 
218 
bigEndian_double(double a)219 uint64_t bigEndian_double(double a)
220 {
221     uint64_t b;
222     unsigned char *src = (unsigned char *)&a,
223     *dst = (unsigned char *)&b;
224 
225     if (is_littleEndian())
226     {
227         dst[0] = src[7];
228         dst[1] = src[6];
229         dst[2] = src[5];
230         dst[3] = src[4];
231         dst[4] = src[3];
232         dst[5] = src[2];
233         dst[6] = src[1];
234         dst[7] = src[0];
235     }
236     else
237         b = *(uint64_t *)&a;
238 
239         return b;
240 }
241 
S64(const char * s)242 int64_t S64(const char *s) {
243     int64_t i;
244     char c ;
245     int scanned = sscanf(s, "%" SCNd64 "%c", &i, &c);
246     if (scanned == 1) return i;
247     fprintf(stderr, "ERROR: parsing string to int64_t.\n");
248     exit(1);
249 }
250 
Expand1(uint32_t a)251 static uint64_t Expand1(uint32_t a)
252 {
253   uint64_t b = a & 0x7fffffff;               // b = ---- ---- ---- ---- ---- ---- ---- ---- 0edc ba98 7654 3210 fedc ba98 7654 3210
254   b = (b ^ (b <<  16)) & 0x0000ffff0000ffff; // b = ---- ---- ---- ---- 0edc ba98 7654 3210 ---- ---- ---- ---- fedc ba98 7654 3210
255   b = (b ^ (b <<  8))  & 0x00ff00ff00ff00ff; // b = ---- ---- 0edc ba98 ---- ---- 7654 3210 ---- ---- fedc ba98 ---- ---- 7654 3210
256   b = (b ^ (b <<  4))  & 0x0f0f0f0f0f0f0f0f; // b = ---- 0edc ---- ba98 ---- 7654 ---- 3210 ---- fedc ---- ba98 ---- 7654 ---- 3210
257   b = (b ^ (b <<  2))  & 0x3333333333333333; // b = --0e --dc --ba --98 --76 --54 --32 --10 --fe --dc --ba --98 --76 --54 --32 --10
258   b = (b ^ (b <<  1))  & 0x5555555555555555; // b = -0-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
259   return b;
260 }
261 
EncodeMorton2D(uint32_t x,uint32_t y)262 static uint64_t EncodeMorton2D(uint32_t x, uint32_t y)
263 {
264   return (Expand1(x) << 1) + Expand1(y);
265 }
266 
main(int argc,char * argv[])267 int main(int argc, char *argv[])
268 {
269     int i;
270     int j;
271     char* buffer;
272     int use_stdout = FALSE;
273     int skip_invalid = FALSE;
274     int num_entries = 0;
275 
276     int verbose = FALSE;
277     char* file_name_in = 0;
278     char* file_name_out = 0;
279     char separator_sign = ' ';
280     char* parse_string = "xyz";
281 
282     int64_t global_offset_x = 0;
283     int64_t global_offset_y = 0;
284     int check = FALSE;
285     double scale_x;
286     double scale_y;
287 
288     LASReaderH reader = NULL;
289     LASHeaderH header = NULL;
290     LASPointH p = NULL;
291     FILE* file_out;
292     int len;
293 
294     unsigned int index = 0;
295     if (argc == 1) {
296         usage();
297         exit(0);
298     }
299 
300     for (i = 1; i < argc; i++)
301     {
302         if (    strcmp(argv[i],"-h") == 0 ||
303                 strcmp(argv[i],"-help") == 0 ||
304                 strcmp(argv[i],"--help") == 0
305            )
306         {
307             usage();
308             exit(0);
309         }
310         else if (   strcmp(argv[i],"-v") == 0 ||
311                 strcmp(argv[i],"--verbose") == 0
312                 )
313         {
314             verbose = TRUE;
315         }
316         else if (   strcmp(argv[i],"-s") == 0 ||
317                 strcmp(argv[i],"--skip_invalid") == 0
318                 )
319         {
320             skip_invalid = TRUE;
321         }
322         else if (   strcmp(argv[i], "--parse") == 0 ||
323                     strcmp(argv[i], "-parse") == 0
324                 )
325         {
326             i++;
327             parse_string = argv[i];
328         }
329         else if (   strcmp(argv[i], "--moffset") == 0 ||
330                     strcmp(argv[i], "-moffset") == 0
331                 )
332         {
333             i++;
334             buffer = strtok (argv[i], ",");
335             j = 0;
336             while (buffer) {
337                 if (j == 0) {
338                     global_offset_x = S64(buffer);
339                 }
340                 else if (j == 1) {
341                     global_offset_y = S64(buffer);
342                 }
343                 j++;
344                 buffer = strtok (NULL, ",");
345                 while (buffer && *buffer == '\040')
346                     buffer++;
347             }
348             if (j != 2){
349                 fprintf(stderr, "Only two int64_t are required in moffset option!\n");
350                 exit(1);
351             }
352 
353         }
354         else if (   strcmp(argv[i], "--check") == 0 ||
355                     strcmp(argv[i], "-check") == 0
356                 )
357         {
358             i++;
359             check = TRUE;
360             buffer = strtok (argv[i], ",");
361             j = 0;
362             while (buffer) {
363                 if (j == 0) {
364                     sscanf(buffer, "%lf", &scale_x);
365                 }
366                 else if (j == 1) {
367                     sscanf(buffer, "%lf", &scale_y);
368                 }
369                 j++;
370                 buffer = strtok (NULL, ",");
371                 while (buffer && *buffer == '\040')
372                     buffer++;
373             }
374             if (j != 2){
375                 fprintf(stderr, "Only two doubles are required in moffset option!\n");
376                 exit(1);
377             }
378         }
379         else if (   strcmp(argv[i], "--stdout") == 0
380                 )
381         {
382             use_stdout = TRUE;
383         }
384         else if (   strcmp(argv[i],"--input") == 0  ||
385                 strcmp(argv[i],"-input") == 0   ||
386                 strcmp(argv[i],"-i") == 0       ||
387                 strcmp(argv[i],"-in") == 0
388                 )
389         {
390             i++;
391             file_name_in = argv[i];
392         }
393         else if (   strcmp(argv[i],"--output") == 0  ||
394                     strcmp(argv[i],"--out") == 0     ||
395                     strcmp(argv[i],"-out") == 0     ||
396                     strcmp(argv[i],"-o") == 0
397                 )
398         {
399             i++;
400             file_name_out = argv[i];
401         }
402         else if (file_name_in == 0 && file_name_out == 0)
403         {
404             file_name_in = argv[i];
405         }
406         else if (file_name_in && file_name_out == 0)
407         {
408             file_name_out = argv[i];
409         }
410         else
411         {
412             fprintf(stderr, "ERROR: unknown argument '%s'\n",argv[i]);
413             usage();
414             exit(1);
415         }
416     } /* end looping through argc/argv */
417     num_entries = strlen(parse_string);
418 
419     if (use_stdout == TRUE && file_name_out){
420       LASError_Print("If an output file is specified, --stdout must not be used!");
421       exit(1);
422     }
423 
424     reader = LASReader_Create(file_name_in);
425     if (!reader) {
426         LASError_Print("Unable to read file");
427         exit(1);
428     }
429 
430     header = LASReader_GetHeader(reader);
431     if (!header) {
432         LASError_Print("Unable to fetch header for file");
433         exit(1);
434     }
435 
436     if (use_stdout)
437     {
438         file_out = stdout;
439     }
440     else
441     {
442         if (file_name_out == NULL)
443         {
444             if (file_name_in == NULL)
445             {
446                 LASError_Print("No input filename was specified");
447                 usage();
448                 exit(1);
449             }
450 
451             len = (int)strlen(file_name_in);
452             file_name_out = LASCopyString(file_name_in);
453             if (file_name_out[len-3] == '.' && file_name_out[len-2] == 'g' && file_name_out[len-1] == 'z')
454             {
455                 len = len - 4;
456             }
457             while (len > 0 && file_name_out[len] != '.')
458             {
459                 len--;
460             }
461             file_name_out[len] = '\0';
462         }
463         file_out = fopen(file_name_out, "wb");
464     }
465 
466     if (file_out == 0)
467     {
468         LASError_Print("Could not open file for write");
469         usage();
470         exit(1);
471     }
472 
473     if (verbose)
474     {
475         print_header(stderr, header, file_name_in);
476     }
477 
478     // Compute factors to add to X and Y and check sanity of generated codes
479     double file_scale_x = LASHeader_GetScaleX(header);
480     double file_scale_y = LASHeader_GetScaleY(header);
481 
482     if (check)
483     {
484         // Check specified scales are like in the LAS file
485         if (fabs(scale_x - file_scale_x) > TOLERANCE){
486         fprintf(stderr, "ERROR: x scale in input file (%lf) does not match specified x scale (%lf)\n",file_scale_x, scale_x);
487             exit(1);
488         }
489         if (fabs(scale_y - file_scale_y) > TOLERANCE){
490             fprintf(stderr, "ERROR: y scale in input file (%lf) does not match specified y scale (%lf)\n",file_scale_y, scale_y);
491             exit(1);
492         }
493         /* Check that the extent of the file (taking into account the global offset)
494          * is within 0,2^31 */
495         double check_min_x = 1.0 + LASHeader_GetMinX(header) - (((double) global_offset_x) * scale_x);
496         if (check_min_x < TOLERANCE) {
497             fprintf(stderr, "ERROR: Specied X global offset is too large. (MinX - (GlobalX*ScaleX)) < 0\n");
498             exit(1);
499         }
500         double check_min_y = 1.0 + LASHeader_GetMinY(header) - (((double) global_offset_y) * scale_y);
501         if (check_min_y < TOLERANCE) {
502             fprintf(stderr, "ERROR: Specied Y global offset is too large. (MinY - (GlobalY*ScaleY)) < 0\n");
503             exit(1);
504         }
505         double check_max_x = LASHeader_GetMaxX(header) - (((double) global_offset_x) * scale_x);
506         if (check_max_x > (MAX_INT_31 * scale_x)) {
507             fprintf(stderr, "ERROR: Specied X global offset is too small. (MaxX - (GlobalX*ScaleX)) > (2^31)*ScaleX\n");
508             exit(1);
509         }
510         double check_max_y = LASHeader_GetMaxY(header) - (((double) global_offset_y) * scale_y);
511         if (check_max_y > (MAX_INT_31 * scale_y)) {
512             fprintf(stderr, "ERROR: Specied Y global offset is too small. (MaxY - (GlobalY*ScaleY)) > (2^31)*ScaleY\n");
513             exit(1);
514         }
515     }
516 
517 
518     /*Write Postgres header*/
519     struct postHeader pgHeader;
520     pgHeader.s = "PGCOPY\n\377\r\n\0";
521     int i1T = 0, i2T = 0;
522     pgHeader.i1 = htonl(i1T);
523     pgHeader.i2 = htonl(i2T);
524     fwrite(pgHeader.s, 11, 1, file_out);
525     fwrite(&pgHeader.i1, sizeof(uint32_t), 1, file_out);
526     fwrite(&pgHeader.i2, sizeof(uint32_t), 1, file_out);
527 
528     /* declaration for morton*/
529     uint32_t rawx = 0;
530     uint32_t rawy = 0;
531     uint64_t mortonkey = 0;
532 
533     /* scaled offsets to add for the morton encoding */
534     int64_t factorX =  ((int64_t) (LASHeader_GetOffsetX(header) / file_scale_x)) - global_offset_x;
535     int64_t factorY =  ((int64_t) (LASHeader_GetOffsetY(header) / file_scale_y)) - global_offset_y;
536 
537     p = LASReader_GetNextPoint(reader);
538     while (p)
539     {
540         if (skip_invalid && !LASPoint_IsValid(p)) {
541             if (verbose) {
542                 LASError_Print("Skipping writing invalid point...");
543             }
544             p = LASReader_GetNextPoint(reader);
545             index -=1;
546             continue;
547         }
548         struct postRow pgRow;
549         uint32_t size;
550         uint16_t hT = num_entries;
551         pgRow.h = htons(hT);
552         fwrite(& pgRow.h, 2, 1, file_out);
553         size = sizeof(double);
554         pgRow.vardSize = htonl(size);
555         size = sizeof(uint32_t);
556         pgRow.varSize = htonl(size);
557 
558         i = 0;
559         for (;;)
560         {
561             LASColorH color = LASPoint_GetColor(p);
562             double vard;
563             int var;
564             unsigned long long int vardL, varL;
565 
566             switch (parse_string[i])
567                {
568                     /* // the morton code on xy */
569                 case 'k':
570                     rawx = (uint32_t) (((int64_t) LASPoint_GetRawX(p)) + factorX);
571                     rawy = (uint32_t) (((int64_t) LASPoint_GetRawY(p)) + factorY);
572                     mortonkey = EncodeMorton2D(rawx,rawy);
573                     varL = htobe64(mortonkey);
574                     fwrite(&pgRow.vardSize, sizeof(uint32_t), 1, file_out);
575                     fwrite(&varL, sizeof(uint64_t), 1, file_out);
576                     break;
577                     /* // the x coordinate */
578                 case 'x':
579                     vard = LASPoint_GetX(p);
580                     fwrite(&pgRow.vardSize, sizeof(uint32_t), 1, file_out);
581                     vardL = bigEndian_double(vard);
582                     fwrite(&vardL, sizeof(double), 1, file_out);
583                     break;
584                     /* // the y coordinate */
585                 case 'y':
586                     vard = LASPoint_GetY(p);
587                     fwrite(&pgRow.vardSize, sizeof(uint32_t), 1, file_out);
588                     vardL = bigEndian_double(vard);
589                     fwrite(&vardL, sizeof(double), 1, file_out);
590                     break;
591                     /* // the z coordinate */
592                 case 'z':
593                     vard = LASPoint_GetZ(p);
594                     fwrite(&pgRow.vardSize, sizeof(uint32_t), 1, file_out);
595                     vardL = bigEndian_double(vard);
596                     fwrite(&vardL, sizeof(double), 1, file_out);
597                     break;
598                     /* // the gps-time */
599                 case 't':
600                     vard = LASPoint_GetTime(p);
601                     fwrite(&pgRow.vardSize, sizeof(uint32_t), 1, file_out);
602                     vardL = bigEndian_double(vard);
603                     fwrite(&vardL, sizeof(double), 1, file_out);
604                     break;
605                     /* // the intensity */
606                 case 'i':
607                     var = LASPoint_GetIntensity(p);
608                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
609                     varL = htonl(var);
610                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
611                     break;
612                     /* the scan angle */
613                 case 'a':
614                     var = LASPoint_GetScanAngleRank(p);
615                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
616                     varL = htonl(var);
617                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
618                     break;
619                     /* the number of the return */
620                 case 'r':
621                     var = LASPoint_GetReturnNumber(p);
622                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
623                     varL = htonl(var);
624                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
625                     break;
626                     /* the classification */
627                 case 'c':
628                     var = LASPoint_GetClassification(p);
629                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
630                     varL = htonl(var);
631                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
632                     break;
633                     /* the user data */
634                 case 'u':
635                     var = LASPoint_GetUserData(p);
636                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
637                     varL = htonl(var);
638                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
639                     break;
640                     /* the number of returns of given pulse */
641                 case 'n':
642                     var = LASPoint_GetNumberOfReturns(p);
643                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
644                     varL = htonl(var);
645                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
646                     break;
647                     /* the red channel color */
648                 case 'R':
649                     var = LASColor_GetRed(color);
650                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
651                     varL = htonl(var);
652                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
653                     break;
654                     /* the green channel color */
655                 case 'G':
656                     var = LASColor_GetGreen(color);
657                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
658                     varL = htonl(var);
659                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
660                     break;
661                     /* the blue channel color */
662                 case 'B':
663                     var = LASColor_GetBlue(color);
664                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
665                     varL = htonl(var);
666                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
667                     break;
668                 case 'M':
669                     var = index;
670                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
671                     varL = htonl(var);
672                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
673                     break;
674                 case 'p':
675                     var = LASPoint_GetPointSourceId(p);
676                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
677                     varL = htonl(var);
678                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
679                     break;
680                     /* the edge of flight line flag */
681                 case 'e':
682                     var = LASPoint_GetFlightLineEdge(p);
683                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
684                     varL = htonl(var);
685                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
686                     break;
687                     /* the direction of scan flag */
688                 case 'd':
689                     var = LASPoint_GetScanDirection(p);
690                     fwrite(&pgRow.varSize, sizeof(uint32_t), 1, file_out);
691                     varL = htonl(var);
692                     fwrite(&varL, sizeof(uint32_t), 1, file_out);
693                     break;
694             }
695             i++;
696             if (!parse_string[i])
697             {
698                 break;
699             }
700             LASColor_Destroy(color);
701         }
702         p = LASReader_GetNextPoint(reader);
703         index +=1;
704     }
705     short endT = -1;
706     short end = htons(endT);
707     fwrite(&end, sizeof(end), 1, file_out);
708 
709     fflush(file_out);
710     fclose(file_out);
711 
712     LASReader_Destroy(reader);
713     LASHeader_Destroy(header);
714     return 0;
715 }
716