/* =============================================================================== FILE: laszip_dll.cpp CONTENTS: see corresponding header file PROGRAMMERS: martin.isenburg@rapidlasso.com - http://rapidlasso.com COPYRIGHT: (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality This is free software; you can redistribute and/or modify it under the terms of the GNU Lesser General Licence as published by the Free Software Foundation. See the COPYING file for more information. This software is distributed WITHOUT ANY WARRANTY and without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. CHANGE HISTORY: 15 October 2019 -- support reading from and writing to unicode file names under Windows 20 March 2019 -- check consistent legacy and extended classification in laszip_write_point() 7 November 2018 -- assure identical legacy and extended flags in laszip_write_point() 20 October 2018 -- changed (U8*) to (const U8*) for all out->put___() calls 5 October 2018 -- corrected 'is_empty' return value in laszip_inside_rectangle() 29 September 2018 -- laszip_prepare_point_for_write() sets extended_point_type 19 September 2018 -- removed tuples and triple support from attributes 7 September 2018 -- replaced calls to _strdup with calls to the LASCopyString macro 6 April 2018 -- added zero() function to laszip_dll struct to fix memory leak 30 August 2017 -- completing stream-based writing (with writing LAS header) 23 August 2017 -- turn on "native" by default 3 August 2017 -- new 'laszip_create_laszip_vlr()' gets VLR as C++ std::vector 29 July 2017 -- integrating minimal stream-based reading/writing into branch 20 July 2017 -- Andrew Bell adds support for stream-based reading/writing 28 May 2017 -- support for "LAS 1.4 selective decompression" added into DLL API 25 April 2017 -- adding initial support for new "native LAS 1.4 extension" 8 January 2017 -- changed from "laszip_dll.h" to "laszip_api.h" for hobu =============================================================================== */ #define LASZIP_DYN_LINK #define LASZIP_SOURCE #include "laszip_api.h" #include #include #include #include #include "laszip.hpp" #include "lasattributer.hpp" #include "bytestreamout_file.hpp" #include "bytestreamin_file.hpp" #include "bytestreamout_array.hpp" #include "bytestreamin_array.hpp" #include "bytestreamin_istream.hpp" #include "bytestreamout_ostream.hpp" #include "laswritepoint.hpp" #include "lasreadpoint.hpp" #include "lasquadtree.hpp" #include "lasindex.hpp" class laszip_dll_inventory { public: BOOL active() const { return (first == FALSE); }; U32 number_of_point_records; U32 number_of_points_by_return[16]; I32 max_X; I32 min_X; I32 max_Y; I32 min_Y; I32 max_Z; I32 min_Z; void add(const laszip_point_struct* point) { number_of_point_records++; if (point->extended_point_type) { number_of_points_by_return[point->extended_return_number]++; } else { number_of_points_by_return[point->return_number]++; } if (first) { min_X = max_X = point->X; min_Y = max_Y = point->Y; min_Z = max_Z = point->Z; first = FALSE; } else { if (point->X < min_X) min_X = point->X; else if (point->X > max_X) max_X = point->X; if (point->Y < min_Y) min_Y = point->Y; else if (point->Y > max_Y) max_Y = point->Y; if (point->Z < min_Z) min_Z = point->Z; else if (point->Z > max_Z) max_Z = point->Z; } } laszip_dll_inventory() { U32 i; number_of_point_records = 0; for (i = 0; i < 16; i++) number_of_points_by_return[i] = 0; max_X = min_X = 0; max_Y = min_Y = 0; max_Z = min_Z = 0; first = TRUE; } private: BOOL first; }; typedef struct laszip_dll { laszip_header_struct header; I64 p_count; I64 npoints; laszip_point_struct point; U8** point_items; FILE* file; ByteStreamIn* streamin; LASreadPoint* reader; ByteStreamOut* streamout; LASwritePoint* writer; LASattributer* attributer; CHAR error[1024]; CHAR warning[1024]; LASindex* lax_index; F64 lax_r_min_x; F64 lax_r_min_y; F64 lax_r_max_x; F64 lax_r_max_y; CHAR* lax_file_name; BOOL lax_create; BOOL lax_append; BOOL lax_exploit; U32 las14_decompress_selective; BOOL preserve_generating_software; BOOL request_native_extension; BOOL request_compatibility_mode; BOOL compatibility_mode; U32 set_chunk_size; I32 start_scan_angle; I32 start_extended_returns; I32 start_classification; I32 start_flags_and_channel; I32 start_NIR_band; laszip_dll_inventory* inventory; std::vector buffers; void zero() { memset(&header, 0, sizeof(laszip_header_struct)); p_count = 0; npoints = 0; memset(&point, 0, sizeof(laszip_point_struct)); point_items = NULL; file = NULL; streamin = NULL; reader = NULL; streamout = NULL; writer = NULL; attributer = NULL; memset(error, 0, 1024); memset(warning, 0, 1024); lax_index = NULL; lax_r_min_x = 0.0; lax_r_min_y = 0.0; lax_r_max_x = 0.0; lax_r_max_y = 0.0; lax_file_name = NULL; lax_create = FALSE; lax_append = FALSE; lax_exploit = FALSE; las14_decompress_selective = 0; preserve_generating_software = FALSE; request_native_extension = FALSE; request_compatibility_mode = FALSE; compatibility_mode = FALSE; set_chunk_size = 0; start_scan_angle = 0; start_extended_returns = 0; start_classification = 0; start_flags_and_channel = 0; start_NIR_band = 0; inventory = NULL; }; } laszip_dll_struct; /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_get_version( laszip_U8* version_major , laszip_U8* version_minor , laszip_U16* version_revision , laszip_U32* version_build ) { try { *version_major = LASZIP_VERSION_MAJOR; *version_minor = LASZIP_VERSION_MINOR; *version_revision = LASZIP_VERSION_REVISION; *version_build = LASZIP_VERSION_BUILD_DATE; } catch (...) { return 1; } return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_get_error( laszip_POINTER pointer , laszip_CHAR** error ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { *error = laszip_dll->error; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_get_error"); return 1; } return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_get_warning( laszip_POINTER pointer , laszip_CHAR** warning ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { *warning = laszip_dll->warning; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_get_warning"); return 1; } return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_create( laszip_POINTER* pointer ) { if (pointer == 0) return 1; try { laszip_dll_struct* laszip_dll = new laszip_dll_struct; if (laszip_dll == 0) { return 1; } // zero every field of the laszip_dll struct laszip_dll->zero(); // create the default laszip_clean(laszip_dll); *pointer = laszip_dll; } catch (...) { return 1; } return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_clean( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot clean while reader is open."); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot clean while writer is open."); return 1; } // dealloc everything alloc in the header if (laszip_dll->header.user_data_in_header) { delete [] laszip_dll->header.user_data_in_header; laszip_dll->header.user_data_in_header = 0; } if (laszip_dll->header.vlrs) { U32 i; for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if (laszip_dll->header.vlrs[i].data) { delete [] laszip_dll->header.vlrs[i].data; } } free(laszip_dll->header.vlrs); laszip_dll->header.vlrs = 0; } if (laszip_dll->header.user_data_after_header) { delete [] laszip_dll->header.user_data_after_header; laszip_dll->header.user_data_after_header = 0; } // dealloc everything alloc in the point if (laszip_dll->point.extra_bytes) { delete [] laszip_dll->point.extra_bytes; laszip_dll->point.extra_bytes = 0; } // dealloc point items although close_reader() / close_writer() call should have done this already if (laszip_dll->point_items) { delete [] laszip_dll->point_items; laszip_dll->point_items = 0; } // close file although close_reader() / close_writer() call should have done this already if (laszip_dll->file) { fclose(laszip_dll->file); laszip_dll->file = 0; } // dealloc streamin although close_reader() call should have done this already if (laszip_dll->streamin) { delete laszip_dll->streamin; laszip_dll->streamin = 0; } // dealloc streamout although close_writer() call should have done this already if (laszip_dll->streamout) { delete laszip_dll->streamout; laszip_dll->streamout = 0; } // dealloc the attributer if (laszip_dll->attributer) { delete laszip_dll->attributer; laszip_dll->attributer = 0; } // dealloc lax_index although close_reader() / close_writer() call should have done this already if (laszip_dll->lax_index) { delete laszip_dll->lax_index; laszip_dll->lax_index = 0; } // dealloc lax_file_name although close_writer() call should have done this already if (laszip_dll->lax_file_name) { free(laszip_dll->lax_file_name); laszip_dll->lax_file_name = 0; } // dealloc the inventory although close_writer() call should have done this already if (laszip_dll->inventory == 0) { delete laszip_dll->inventory; laszip_dll->inventory = 0; } // dealloc any data fields that were kept around in memory for others if (laszip_dll->buffers.size()) { for (size_t i = 0; i < laszip_dll->buffers.size(); i++) { free(laszip_dll->buffers[i]); } laszip_dll->buffers.clear(); } // zero every field of the laszip_dll struct laszip_dll->zero(); // create default header sprintf(laszip_dll->header.generating_software, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); laszip_dll->header.version_major = 1; laszip_dll->header.version_minor = 2; laszip_dll->header.header_size = 227; laszip_dll->header.offset_to_point_data = 227; laszip_dll->header.point_data_format = 1; laszip_dll->header.point_data_record_length = 28; laszip_dll->header.x_scale_factor = 0.01; laszip_dll->header.y_scale_factor = 0.01; laszip_dll->header.z_scale_factor = 0.01; laszip_dll->set_chunk_size = LASZIP_CHUNK_SIZE_DEFAULT; laszip_dll->request_native_extension = TRUE; laszip_dll->las14_decompress_selective = LASZIP_DECOMPRESS_SELECTIVE_ALL; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_clean"); return 1; } return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_destroy( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; int err = 0; try { err = laszip_clean(laszip_dll); delete laszip_dll; } catch (...) { return 1; } return err; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_get_header_pointer( laszip_POINTER pointer , laszip_header_struct** header_pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (header_pointer == 0) { sprintf(laszip_dll->error, "laszip_header_struct pointer 'header_pointer' is zero"); return 1; } *header_pointer = &laszip_dll->header; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_get_header_pointer"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_get_point_pointer( laszip_POINTER pointer , laszip_point_struct** point_pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (point_pointer == 0) { sprintf(laszip_dll->error, "laszip_point_struct pointer 'point_pointer' is zero"); return 1; } *point_pointer = &laszip_dll->point; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_get_point_pointer"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_get_point_count( laszip_POINTER pointer , laszip_I64* count ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (count == 0) { sprintf(laszip_dll->error, "laszip_I64 pointer 'count' is zero"); return 1; } if ((laszip_dll->reader == 0) && (laszip_dll->writer == 0)) { sprintf(laszip_dll->error, "getting count before reader or writer was opened"); return 1; } *count = laszip_dll->p_count; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_get_point_count"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_header( laszip_POINTER pointer , const laszip_header_struct* header ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (header == 0) { sprintf(laszip_dll->error, "laszip_header_struct pointer 'header' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot set header after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot set header after writer was opened"); return 1; } // dealloc the attributer (if needed) if (laszip_dll->attributer) { delete laszip_dll->attributer; laszip_dll->attributer = 0; } // populate the header U32 i; laszip_dll->header.file_source_ID = header->file_source_ID; laszip_dll->header.global_encoding = header->global_encoding; laszip_dll->header.project_ID_GUID_data_1 = header->project_ID_GUID_data_1; laszip_dll->header.project_ID_GUID_data_2 = header->project_ID_GUID_data_2; laszip_dll->header.project_ID_GUID_data_3 = header->project_ID_GUID_data_3; memcpy(laszip_dll->header.project_ID_GUID_data_4, header->project_ID_GUID_data_4, 8); laszip_dll->header.version_major = header->version_major; laszip_dll->header.version_minor = header->version_minor; memcpy(laszip_dll->header.system_identifier, header->system_identifier, 32); memcpy(laszip_dll->header.generating_software, header->generating_software, 32); laszip_dll->header.file_creation_day = header->file_creation_day; laszip_dll->header.file_creation_year = header->file_creation_year; laszip_dll->header.header_size = header->header_size; laszip_dll->header.offset_to_point_data = header->offset_to_point_data; laszip_dll->header.number_of_variable_length_records = header->number_of_variable_length_records; laszip_dll->header.point_data_format = header->point_data_format; laszip_dll->header.point_data_record_length = header->point_data_record_length; laszip_dll->header.number_of_point_records = header->number_of_point_records; for (i = 0; i < 5; i++) laszip_dll->header.number_of_points_by_return[i] = header->number_of_points_by_return[i]; laszip_dll->header.x_scale_factor = header->x_scale_factor; laszip_dll->header.y_scale_factor = header->y_scale_factor; laszip_dll->header.z_scale_factor = header->z_scale_factor; laszip_dll->header.x_offset = header->x_offset; laszip_dll->header.y_offset = header->y_offset; laszip_dll->header.z_offset = header->z_offset; laszip_dll->header.max_x = header->max_x; laszip_dll->header.min_x = header->min_x; laszip_dll->header.max_y = header->max_y; laszip_dll->header.min_y = header->min_y; laszip_dll->header.max_z = header->max_z; laszip_dll->header.min_z = header->min_z; if (laszip_dll->header.version_minor >= 3) { laszip_dll->header.start_of_waveform_data_packet_record = header->start_of_first_extended_variable_length_record; } if (laszip_dll->header.version_minor >= 4) { laszip_dll->header.start_of_first_extended_variable_length_record = header->start_of_first_extended_variable_length_record; laszip_dll->header.number_of_extended_variable_length_records = header->number_of_extended_variable_length_records; laszip_dll->header.extended_number_of_point_records = header->extended_number_of_point_records; for (i = 0; i < 15; i++) laszip_dll->header.extended_number_of_points_by_return[i] = header->extended_number_of_points_by_return[i]; } laszip_dll->header.user_data_in_header_size = header->user_data_in_header_size; if (laszip_dll->header.user_data_in_header) { delete [] laszip_dll->header.user_data_in_header; laszip_dll->header.user_data_in_header = 0; } if (header->user_data_in_header_size) { if (header->user_data_in_header == 0) { sprintf(laszip_dll->error, "header->user_data_in_header_size is %d but header->user_data_in_header is NULL", header->user_data_in_header_size); return 1; } laszip_dll->header.user_data_in_header = new U8[header->user_data_in_header_size]; memcpy(laszip_dll->header.user_data_in_header, header->user_data_in_header, header->user_data_in_header_size); } if (laszip_dll->header.vlrs) { for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if (laszip_dll->header.vlrs[i].data) { delete [] laszip_dll->header.vlrs[i].data; } } free(laszip_dll->header.vlrs); laszip_dll->header.vlrs = 0; } if (header->number_of_variable_length_records) { laszip_dll->header.vlrs = (laszip_vlr*)malloc(sizeof(laszip_vlr)*header->number_of_variable_length_records); for (i = 0; i < header->number_of_variable_length_records; i++) { laszip_dll->header.vlrs[i].reserved = header->vlrs[i].reserved; memcpy(laszip_dll->header.vlrs[i].user_id, header->vlrs[i].user_id, 16); laszip_dll->header.vlrs[i].record_id = header->vlrs[i].record_id; laszip_dll->header.vlrs[i].record_length_after_header = header->vlrs[i].record_length_after_header; memcpy(laszip_dll->header.vlrs[i].description, header->vlrs[i].description, 32); if (header->vlrs[i].record_length_after_header) { if (header->vlrs[i].data == 0) { sprintf(laszip_dll->error, "header->vlrs[%d].record_length_after_header is %d but header->vlrs[%d].data is NULL", i, header->vlrs[i].record_length_after_header, i); return 1; } laszip_dll->header.vlrs[i].data = new U8[header->vlrs[i].record_length_after_header]; memcpy(laszip_dll->header.vlrs[i].data, header->vlrs[i].data, header->vlrs[i].record_length_after_header); } else { laszip_dll->header.vlrs[i].data = 0; } // populate the attributer if needed if ((strcmp(laszip_dll->header.vlrs[i].user_id, "LASF_Spec") == 0) && (laszip_dll->header.vlrs[i].record_id == 4)) { if (laszip_dll->attributer == 0) { laszip_dll->attributer = new LASattributer; if (laszip_dll->attributer == 0) { sprintf(laszip_dll->error, "cannot allocate LASattributer"); return 1; } } laszip_dll->attributer->init_attributes(laszip_dll->header.vlrs[i].record_length_after_header/sizeof(LASattribute), (LASattribute*)laszip_dll->header.vlrs[i].data); } } } laszip_dll->header.user_data_after_header_size = header->user_data_after_header_size; if (laszip_dll->header.user_data_after_header) { delete [] laszip_dll->header.user_data_after_header; laszip_dll->header.user_data_after_header = 0; } if (header->user_data_after_header_size) { if (header->user_data_after_header == 0) { sprintf(laszip_dll->error, "header->user_data_after_header_size is %d but header->user_data_after_header is NULL", header->user_data_after_header_size); return 1; } laszip_dll->header.user_data_after_header = new U8[header->user_data_after_header_size]; memcpy(laszip_dll->header.user_data_after_header, header->user_data_after_header, header->user_data_after_header_size); } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_header"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_point_type_and_size( laszip_POINTER pointer , laszip_U8 point_type , laszip_U16 point_size ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot set point format and point size after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot set point format and point size after writer was opened"); return 1; } // check if point type and type are supported if (!LASzip().setup(point_type, point_size, LASZIP_COMPRESSOR_NONE)) { sprintf(laszip_dll->error, "invalid combination of point_type %d and point_size %d", (I32)point_type, (I32)point_size); return 1; } // set point type and point size laszip_dll->header.point_data_format = point_type; laszip_dll->header.point_data_record_length = point_size; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_point_type_and_size"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_check_for_integer_overflow( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { // get a pointer to the header laszip_header_struct* header = &(laszip_dll->header); // quantize and dequantize the bounding box with current scale_factor and offset I32 quant_min_x = I32_QUANTIZE((header->min_x-header->x_offset)/header->x_scale_factor); I32 quant_max_x = I32_QUANTIZE((header->max_x-header->x_offset)/header->x_scale_factor); I32 quant_min_y = I32_QUANTIZE((header->min_y-header->y_offset)/header->y_scale_factor); I32 quant_max_y = I32_QUANTIZE((header->max_y-header->y_offset)/header->y_scale_factor); I32 quant_min_z = I32_QUANTIZE((header->min_z-header->z_offset)/header->z_scale_factor); I32 quant_max_z = I32_QUANTIZE((header->max_z-header->z_offset)/header->z_scale_factor); F64 dequant_min_x = header->x_scale_factor*quant_min_x+header->x_offset; F64 dequant_max_x = header->x_scale_factor*quant_max_x+header->x_offset; F64 dequant_min_y = header->y_scale_factor*quant_min_y+header->y_offset; F64 dequant_max_y = header->y_scale_factor*quant_max_y+header->y_offset; F64 dequant_min_z = header->z_scale_factor*quant_min_z+header->z_offset; F64 dequant_max_z = header->z_scale_factor*quant_max_z+header->z_offset; // make sure that there is not sign flip (a 32-bit integer overflow) for the bounding box if ((header->min_x > 0) != (dequant_min_x > 0)) { sprintf(laszip_dll->error, "quantization sign flip for min_x from %g to %g. set scale factor for x coarser than %g\n", header->min_x, dequant_min_x, header->x_scale_factor); return 1; } if ((header->max_x > 0) != (dequant_max_x > 0)) { sprintf(laszip_dll->error, "quantization sign flip for max_x from %g to %g. set scale factor for x coarser than %g\n", header->max_x, dequant_max_x, header->x_scale_factor); return 1; } if ((header->min_y > 0) != (dequant_min_y > 0)) { sprintf(laszip_dll->error, "quantization sign flip for min_y from %g to %g. set scale factor for y coarser than %g\n", header->min_y, dequant_min_y, header->y_scale_factor); return 1; } if ((header->max_y > 0) != (dequant_max_y > 0)) { sprintf(laszip_dll->error, "quantization sign flip for max_y from %g to %g. set scale factor for y coarser than %g\n", header->max_y, dequant_max_y, header->y_scale_factor); return 1; } if ((header->min_z > 0) != (dequant_min_z > 0)) { sprintf(laszip_dll->error, "quantization sign flip for min_z from %g to %g. set scale factor for z coarser than %g\n", header->min_z, dequant_min_z, header->z_scale_factor); return 1; } if ((header->max_z > 0) != (dequant_max_z > 0)) { sprintf(laszip_dll->error, "quantization sign flip for max_z from %g to %g. set scale factor for z coarser than %g\n", header->max_z, dequant_max_z, header->z_scale_factor); return 1; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_auto_offset"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_auto_offset( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot auto offset after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot auto offset after writer was opened"); return 1; } // get a pointer to the header laszip_header_struct* header = &(laszip_dll->header); // check scale factor F64 x_scale_factor = header->x_scale_factor; F64 y_scale_factor = header->y_scale_factor; F64 z_scale_factor = header->z_scale_factor; if ((x_scale_factor <= 0) || !F64_IS_FINITE(x_scale_factor)) { sprintf(laszip_dll->error, "invalid x scale_factor %g in header", header->x_scale_factor); return 1; } if ((y_scale_factor <= 0) || !F64_IS_FINITE(y_scale_factor)) { sprintf(laszip_dll->error, "invalid y scale_factor %g in header", header->y_scale_factor); return 1; } if ((z_scale_factor <= 0) || !F64_IS_FINITE(z_scale_factor)) { sprintf(laszip_dll->error, "invalid z scale_factor %g in header", header->z_scale_factor); return 1; } F64 center_bb_x = (header->min_x + header->max_x) / 2; F64 center_bb_y = (header->min_y + header->max_y) / 2; F64 center_bb_z = (header->min_z + header->max_z) / 2; if (!F64_IS_FINITE(center_bb_x)) { sprintf(laszip_dll->error, "invalid x coordinate at center of bounding box (min: %g max: %g)", header->min_x, header->max_x); return 1; } if (!F64_IS_FINITE(center_bb_y)) { sprintf(laszip_dll->error, "invalid y coordinate at center of bounding box (min: %g max: %g)", header->min_y, header->max_y); return 1; } if (!F64_IS_FINITE(center_bb_z)) { sprintf(laszip_dll->error, "invalid z coordinate at center of bounding box (min: %g max: %g)", header->min_z, header->max_z); return 1; } F64 x_offset = header->x_offset; F64 y_offset = header->y_offset; F64 z_offset = header->z_offset; header->x_offset = (I64_FLOOR(center_bb_x/x_scale_factor/10000000))*10000000*x_scale_factor; header->y_offset = (I64_FLOOR(center_bb_y/y_scale_factor/10000000))*10000000*y_scale_factor; header->z_offset = (I64_FLOOR(center_bb_z/z_scale_factor/10000000))*10000000*z_scale_factor; if (laszip_check_for_integer_overflow(pointer)) { header->x_offset = x_offset; header->y_offset = y_offset; header->z_offset = z_offset; return 1; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_auto_offset"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_point( laszip_POINTER pointer , const laszip_point_struct* point ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (point == 0) { sprintf(laszip_dll->error, "laszip_point_struct pointer 'point' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot set point for reader"); return 1; } memcpy(&laszip_dll->point, point, ((U8*)&(laszip_dll->point.extra_bytes)) - ((U8*)&(laszip_dll->point.X))); if (laszip_dll->point.extra_bytes) { if (point->extra_bytes) { if (laszip_dll->point.num_extra_bytes == point->num_extra_bytes) { memcpy(laszip_dll->point.extra_bytes, point->extra_bytes, laszip_dll->point.num_extra_bytes); } else { sprintf(laszip_dll->error, "target point has %d extra bytes but source point has %d", laszip_dll->point.num_extra_bytes, point->num_extra_bytes); return 1; } } else if (!laszip_dll->compatibility_mode) { sprintf(laszip_dll->error, "target point has extra bytes but source point does not"); return 1; } } /* else { if (point->extra_bytes) { sprintf(laszip_dll->error, "source point has extra bytes but target point does not"); return 1; } } */ } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_point"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_coordinates( laszip_POINTER pointer , const laszip_F64* coordinates ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (coordinates == 0) { sprintf(laszip_dll->error, "laszip_F64 pointer 'coordinates' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot set coordinates for reader"); return 1; } // get a pointer to the header laszip_header_struct* header = &(laszip_dll->header); // get a pointer to the point laszip_point_struct* point = &(laszip_dll->point); // set the coordinates point->X = I32_QUANTIZE((coordinates[0]-header->x_offset)/header->x_scale_factor); point->Y = I32_QUANTIZE((coordinates[1]-header->y_offset)/header->y_scale_factor); point->Z = I32_QUANTIZE((coordinates[2]-header->z_offset)/header->z_scale_factor); } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_coordinates"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_get_coordinates( laszip_POINTER pointer , laszip_F64* coordinates ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (coordinates == 0) { sprintf(laszip_dll->error, "laszip_F64 pointer 'coordinates' is zero"); return 1; } // get a pointer to the header laszip_header_struct* header = &(laszip_dll->header); // get a pointer to the point laszip_point_struct* point = &(laszip_dll->point); // get the coordinates coordinates[0] = header->x_scale_factor*point->X+header->x_offset; coordinates[1] = header->y_scale_factor*point->Y+header->y_offset; coordinates[2] = header->z_scale_factor*point->Z+header->z_offset; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_get_coordinates"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_geokeys( laszip_POINTER pointer , laszip_U32 number , const laszip_geokey_struct* key_entries ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (number == 0) { sprintf(laszip_dll->error, "number of key_entries is zero"); return 1; } if (key_entries == 0) { sprintf(laszip_dll->error, "laszip_geokey_struct pointer 'key_entries' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot set geokeys after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot set geokeys after writer was opened"); return 1; } // create the geokey directory laszip_geokey_struct* key_entries_plus_one = new laszip_geokey_struct[number+1]; if (key_entries_plus_one == 0) { sprintf(laszip_dll->error, "allocating laszip_geokey_struct[%u] array", number+1); return 1; } key_entries_plus_one[0].key_id = 1; // aka key_directory_version key_entries_plus_one[0].tiff_tag_location = 1; // aka key_revision key_entries_plus_one[0].count = 0; // aka minor_revision key_entries_plus_one[0].value_offset = number; // aka number_of_keys memcpy(key_entries_plus_one + 1, key_entries, sizeof(laszip_geokey_struct)*number); // add the VLR if (laszip_add_vlr(laszip_dll, "LASF_Projection", 34735, (laszip_U16)(8 + number*8), 0, (laszip_U8*)key_entries_plus_one)) { sprintf(laszip_dll->error, "setting %u geodouble_params", number); return 1; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_geokey_entries"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_geodouble_params( laszip_POINTER pointer , laszip_U32 number , const laszip_F64* geodouble_params ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (number == 0) { sprintf(laszip_dll->error, "number of geodouble_params is zero"); return 1; } if (geodouble_params == 0) { sprintf(laszip_dll->error, "laszip_F64 pointer 'geodouble_params' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot set geodouble_params after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot set geodouble_params after writer was opened"); return 1; } // add the VLR if (laszip_add_vlr(laszip_dll, "LASF_Projection", 34736, (laszip_U16)(number*8), 0, (laszip_U8*)geodouble_params)) { sprintf(laszip_dll->error, "setting %u geodouble_params", number); return 1; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_geodouble_params"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_geoascii_params( laszip_POINTER pointer , laszip_U32 number , const laszip_CHAR* geoascii_params ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (number == 0) { sprintf(laszip_dll->error, "number of geoascii_params is zero"); return 1; } if (geoascii_params == 0) { sprintf(laszip_dll->error, "laszip_CHAR pointer 'geoascii_params' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot set geoascii_params after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot set geoascii_params after writer was opened"); return 1; } // add the VLR if (laszip_add_vlr(laszip_dll, "LASF_Projection", 34737, (laszip_U16)(number), 0, (laszip_U8*)geoascii_params)) { sprintf(laszip_dll->error, "setting %u geoascii_params", number); return 1; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_geoascii_params"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_add_attribute( laszip_POINTER pointer , laszip_U32 type , const laszip_CHAR* name , const laszip_CHAR* description , laszip_F64 scale , laszip_F64 offset ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (type > LAS_ATTRIBUTE_F64) { sprintf(laszip_dll->error, "laszip_U32 'type' is %u but needs to be between %d and %d", type, LAS_ATTRIBUTE_U8, LAS_ATTRIBUTE_F64); return 1; } if (name == 0) { sprintf(laszip_dll->error, "laszip_CHAR pointer 'name' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot add attribute after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot add attribute after writer was opened"); return 1; } LASattribute lasattribute(type, name, description); lasattribute.set_scale(scale); lasattribute.set_offset(offset); if (laszip_dll->attributer == 0) { laszip_dll->attributer = new LASattributer; if (laszip_dll->attributer == 0) { sprintf(laszip_dll->error, "cannot allocate LASattributer"); return 1; } } if (laszip_dll->attributer->add_attribute(lasattribute) == -1) { sprintf(laszip_dll->error, "cannot add attribute '%s' to attributer", name); return 1; } if (laszip_add_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4, (laszip_U16)(laszip_dll->attributer->number_attributes*sizeof(LASattribute)), 0, (laszip_U8*)laszip_dll->attributer->attributes)) { sprintf(laszip_dll->error, "adding the new extra bytes VLR with the additional attribute '%s'", name); return 1; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_add_attribute"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_add_vlr( laszip_POINTER pointer , const laszip_CHAR* user_id , laszip_U16 record_id , laszip_U16 record_length_after_header , const laszip_CHAR* description , const laszip_U8* data ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (user_id == 0) { sprintf(laszip_dll->error, "laszip_CHAR pointer 'user_id' is zero"); return 1; } if ((record_length_after_header > 0) && (data == 0)) { sprintf(laszip_dll->error, "record_length_after_header of VLR is %u but data pointer is zero", (U32)record_length_after_header); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot add vlr after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot add vlr after writer was opened"); return 1; } U32 i = 0; if (laszip_dll->header.vlrs) { // overwrite existing VLR ? for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if ((strncmp(laszip_dll->header.vlrs[i].user_id, user_id, 16) == 0) && (laszip_dll->header.vlrs[i].record_id == record_id)) { if (laszip_dll->header.vlrs[i].record_length_after_header) { laszip_dll->header.offset_to_point_data -= laszip_dll->header.vlrs[i].record_length_after_header; laszip_dll->header.vlrs[i].record_length_after_header = 0; delete [] laszip_dll->header.vlrs[i].data; laszip_dll->header.vlrs[i].data = 0; } break; } } // create new VLR if (i == laszip_dll->header.number_of_variable_length_records) { laszip_dll->header.number_of_variable_length_records++; laszip_dll->header.offset_to_point_data += 54; laszip_dll->header.vlrs = (laszip_vlr_struct*)realloc(laszip_dll->header.vlrs, sizeof(laszip_vlr_struct)*laszip_dll->header.number_of_variable_length_records); if (laszip_dll->header.vlrs == 0) { sprintf(laszip_dll->error, "reallocating vlrs[%u] array", laszip_dll->header.number_of_variable_length_records); return 1; } } } else { laszip_dll->header.number_of_variable_length_records = 1; laszip_dll->header.offset_to_point_data += 54; laszip_dll->header.vlrs = (laszip_vlr_struct*)malloc(sizeof(laszip_vlr_struct)); if (laszip_dll->header.vlrs == 0) { sprintf(laszip_dll->error, "allocating vlrs[1] array"); return 1; } } // zero the VLR memset(&(laszip_dll->header.vlrs[i]), 0, sizeof(laszip_vlr_struct)); // copy the VLR laszip_dll->header.vlrs[i].reserved = 0x0; strncpy(laszip_dll->header.vlrs[i].user_id, user_id, 16); laszip_dll->header.vlrs[i].record_id = record_id; laszip_dll->header.vlrs[i].record_length_after_header = record_length_after_header; if (description) { strncpy(laszip_dll->header.vlrs[i].description, description, 32); } else { sprintf(laszip_dll->header.vlrs[i].description, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); } if (record_length_after_header) { laszip_dll->header.offset_to_point_data += record_length_after_header; laszip_dll->header.vlrs[i].data = new U8[record_length_after_header]; memcpy(laszip_dll->header.vlrs[i].data, data, record_length_after_header); } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_add_vlr"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_remove_vlr( laszip_POINTER pointer , const laszip_CHAR* user_id , laszip_U16 record_id ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (user_id == 0) { sprintf(laszip_dll->error, "laszip_CHAR pointer 'user_id' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "cannot remove vlr after reader was opened"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "cannot remove vlr after writer was opened"); return 1; } U32 i = 0; if (laszip_dll->header.vlrs) { for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if ((strncmp(laszip_dll->header.vlrs[i].user_id, user_id, 16) == 0) && (laszip_dll->header.vlrs[i].record_id == record_id)) { if (laszip_dll->header.vlrs[i].record_length_after_header) { laszip_dll->header.offset_to_point_data -= (54 + laszip_dll->header.vlrs[i].record_length_after_header); delete [] laszip_dll->header.vlrs[i].data; laszip_dll->header.vlrs[i].data = 0; } laszip_dll->header.number_of_variable_length_records--; for (/*i = i*/; i < laszip_dll->header.number_of_variable_length_records; i++) { laszip_dll->header.vlrs[i] = laszip_dll->header.vlrs[i+1]; } if (laszip_dll->header.number_of_variable_length_records) { laszip_dll->header.vlrs = (laszip_vlr_struct*)realloc(laszip_dll->header.vlrs, sizeof(laszip_vlr_struct)*laszip_dll->header.number_of_variable_length_records); if (laszip_dll->header.vlrs == 0) { sprintf(laszip_dll->error, "reallocating vlrs[%u] array", laszip_dll->header.number_of_variable_length_records); return 1; } } else { free(laszip_dll->header.vlrs); laszip_dll->header.vlrs = 0; } i = U32_MAX; break; } } if (i != U32_MAX) { sprintf(laszip_dll->error, "cannot find VLR with user_id '%s' and record_id %d among the %u VLRs in the header", user_id, (I32)record_id, laszip_dll->header.number_of_variable_length_records); return 1; } } else { sprintf(laszip_dll->error, "cannot remove VLR with user_id '%s' and record_id %d because header has no VLRs", user_id, (I32)record_id); return 1; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_add_vlr"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_preserve_generating_software( laszip_POINTER pointer , const laszip_BOOL preserve ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } laszip_dll->preserve_generating_software = preserve; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_preserve_generating_software"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_request_native_extension( laszip_POINTER pointer , const laszip_BOOL request ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } laszip_dll->request_native_extension = request; if (request) // only one should be on { laszip_dll->request_compatibility_mode = FALSE; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_request_native_extension"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_request_compatibility_mode( laszip_POINTER pointer , const laszip_BOOL request ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } laszip_dll->request_compatibility_mode = request; if (request) // only one should be on { laszip_dll->request_native_extension = FALSE; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_request_compatibility_mode"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_set_chunk_size( laszip_POINTER pointer , const laszip_U32 chunk_size ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } laszip_dll->set_chunk_size = chunk_size; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_set_chunk_size"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_create_spatial_index( laszip_POINTER pointer , const laszip_BOOL create , const laszip_BOOL append ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } if (append) { sprintf(laszip_dll->error, "appending of spatial index not (yet) supported in this version"); return 1; } laszip_dll->lax_create = create; laszip_dll->lax_append = append; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_create_spatial_index"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ static I32 laszip_prepare_header_for_write( laszip_dll_struct* laszip_dll ) { if ((laszip_dll->header.version_major != 1) || (laszip_dll->header.version_minor > 4)) { sprintf(laszip_dll->error, "unknown LAS version %d.%d", (I32)laszip_dll->header.version_major, (I32)laszip_dll->header.version_minor); return 1; } // check counters U32 i; if (laszip_dll->header.point_data_format > 5) { // legacy counters are zero for new point types laszip_dll->header.number_of_point_records = 0; for (i = 0; i < 5; i++) { laszip_dll->header.number_of_points_by_return[i] = 0; } } else if (laszip_dll->header.version_minor > 3) { // legacy counters must be zero or consistent for old point types if (laszip_dll->header.number_of_point_records != laszip_dll->header.extended_number_of_point_records) { if (laszip_dll->header.number_of_point_records != 0) { #ifdef _WIN32 sprintf(laszip_dll->error, "inconsistent number_of_point_records %u and extended_number_of_point_records %I64d", laszip_dll->header.number_of_point_records, laszip_dll->header.extended_number_of_point_records); #else sprintf(laszip_dll->error, "inconsistent number_of_point_records %u and extended_number_of_point_records %llu", laszip_dll->header.number_of_point_records, laszip_dll->header.extended_number_of_point_records); #endif return 1; } else if (laszip_dll->header.extended_number_of_point_records <= U32_MAX) { laszip_dll->header.number_of_point_records = (U32)laszip_dll->header.extended_number_of_point_records; } } for (i = 0; i < 5; i++) { if (laszip_dll->header.number_of_points_by_return[i] != laszip_dll->header.extended_number_of_points_by_return[i]) { if (laszip_dll->header.number_of_points_by_return[i] != 0) { #ifdef _WIN32 sprintf(laszip_dll->error, "inconsistent number_of_points_by_return[%u] %u and extended_number_of_points_by_return[%u] %I64d", i, laszip_dll->header.number_of_points_by_return[i], i, laszip_dll->header.extended_number_of_points_by_return[i]); #else sprintf(laszip_dll->error, "inconsistent number_of_points_by_return[%u] %u and extended_number_of_points_by_return[%u] %llu", i, laszip_dll->header.number_of_points_by_return[i], i, laszip_dll->header.extended_number_of_points_by_return[i]); #endif return 1; } else if (laszip_dll->header.extended_number_of_points_by_return[i] <= U32_MAX) { laszip_dll->header.number_of_points_by_return[i] = (U32)laszip_dll->header.extended_number_of_points_by_return[i]; } } } } return 0; } /*---------------------------------------------------------------------------*/ static I32 laszip_prepare_point_for_write( laszip_dll_struct* laszip_dll , const laszip_BOOL compress ) { U32 i; if (laszip_dll->header.point_data_format > 5) { // must be set for the new point types 6 or higher ... laszip_dll->point.extended_point_type = 1; if (laszip_dll->request_native_extension) { // we are *not* operating in compatibility mode laszip_dll->compatibility_mode = FALSE; } else if (laszip_dll->request_compatibility_mode) { // we are *not* using the native extension laszip_dll->request_native_extension = FALSE; // make sure there are no more than U32_MAX points if (laszip_dll->header.extended_number_of_point_records > U32_MAX) { #ifdef _WIN32 sprintf(laszip_dll->error, "extended_number_of_point_records of %I64d is too much for 32-bit counters of compatibility mode", laszip_dll->header.extended_number_of_point_records); #else sprintf(laszip_dll->error, "extended_number_of_point_records of %llu is too much for 32-bit counters of compatibility mode", laszip_dll->header.extended_number_of_point_records); #endif return 1; } // copy 64-bit extended counters back into 32-bit legacy counters laszip_dll->header.number_of_point_records = (U32)(laszip_dll->header.extended_number_of_point_records); for (i = 0; i < 5; i++) { laszip_dll->header.number_of_points_by_return[i] = (U32)(laszip_dll->header.extended_number_of_points_by_return[i]); } // are there any "extra bytes" already ... ? I32 number_of_existing_extrabytes = 0; switch (laszip_dll->header.point_data_format) { case 6: number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 30; break; case 7: number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 36; break; case 8: number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 38; break; case 9: number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 59; break; case 10: number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 67; break; default: sprintf(laszip_dll->error, "unknown point_data_format %d", laszip_dll->header.point_data_format); return 1; } if (number_of_existing_extrabytes < 0) { sprintf(laszip_dll->error, "bad point_data_format %d point_data_record_length %d combination", laszip_dll->header.point_data_format, laszip_dll->header.point_data_record_length); return 1; } // downgrade to LAS 1.2 or LAS 1.3 if (laszip_dll->header.point_data_format <= 8) { laszip_dll->header.version_minor = 2; // LAS 1.2 header is 148 bytes less than LAS 1.4+ header laszip_dll->header.header_size -= 148; laszip_dll->header.offset_to_point_data -= 148; } else { laszip_dll->header.version_minor = 3; // LAS 1.3 header is 140 bytes less than LAS 1.4+ header laszip_dll->header.header_size -= 140; laszip_dll->header.offset_to_point_data -= 140; } // turn off the bit indicating the presence of the OGC WKT laszip_dll->header.global_encoding &= ~(1<<4); // old point type is two bytes shorter laszip_dll->header.point_data_record_length -= 2; // but we add 5 bytes of attributes laszip_dll->header.point_data_record_length += 5; // create 2+2+4+148 bytes payload for compatibility VLR ByteStreamOutArray* out; if (IS_LITTLE_ENDIAN()) out = new ByteStreamOutArrayLE(); else out = new ByteStreamOutArrayBE(); // write control info U16 laszip_version = (U16)LASZIP_VERSION_BUILD_DATE; out->put16bitsLE((U8*)&laszip_version); U16 compatible_version = 3; out->put16bitsLE((U8*)&compatible_version); U32 unused = 0; out->put32bitsLE((U8*)&unused); // write the 148 bytes of the extended LAS 1.4 header U64 start_of_waveform_data_packet_record = laszip_dll->header.start_of_waveform_data_packet_record; if (start_of_waveform_data_packet_record != 0) { #ifdef _WIN32 fprintf(stderr,"WARNING: header->start_of_waveform_data_packet_record is %I64d. writing 0 instead.\n", start_of_waveform_data_packet_record); #else fprintf(stderr,"WARNING: header->start_of_waveform_data_packet_record is %llu. writing 0 instead.\n", start_of_waveform_data_packet_record); #endif start_of_waveform_data_packet_record = 0; } out->put64bitsLE((U8*)&start_of_waveform_data_packet_record); U64 start_of_first_extended_variable_length_record = laszip_dll->header.start_of_first_extended_variable_length_record; if (start_of_first_extended_variable_length_record != 0) { #ifdef _WIN32 fprintf(stderr,"WARNING: EVLRs not supported. header->start_of_first_extended_variable_length_record is %I64d. writing 0 instead.\n", start_of_first_extended_variable_length_record); #else fprintf(stderr,"WARNING: EVLRs not supported. header->start_of_first_extended_variable_length_record is %llu. writing 0 instead.\n", start_of_first_extended_variable_length_record); #endif start_of_first_extended_variable_length_record = 0; } out->put64bitsLE((U8*)&start_of_first_extended_variable_length_record); U32 number_of_extended_variable_length_records = laszip_dll->header.number_of_extended_variable_length_records; if (number_of_extended_variable_length_records != 0) { fprintf(stderr,"WARNING: EVLRs not supported. header->number_of_extended_variable_length_records is %u. writing 0 instead.\n", number_of_extended_variable_length_records); number_of_extended_variable_length_records = 0; } out->put32bitsLE((U8*)&number_of_extended_variable_length_records); U64 extended_number_of_point_records; if (laszip_dll->header.number_of_point_records) extended_number_of_point_records = laszip_dll->header.number_of_point_records; else extended_number_of_point_records = laszip_dll->header.extended_number_of_point_records; out->put64bitsLE((U8*)&extended_number_of_point_records); U64 extended_number_of_points_by_return; for (i = 0; i < 15; i++) { if ((i < 5) && laszip_dll->header.number_of_points_by_return[i]) extended_number_of_points_by_return = laszip_dll->header.number_of_points_by_return[i]; else extended_number_of_points_by_return = laszip_dll->header.extended_number_of_points_by_return[i]; out->put64bitsLE((U8*)&extended_number_of_points_by_return); } // add the compatibility VLR if (laszip_add_vlr(laszip_dll, "lascompatible\0\0", 22204, (laszip_U16)(2+2+4+148), 0, (laszip_U8*)out->takeData())) { sprintf(laszip_dll->error, "adding the compatibility VLR"); return 1; } delete out; // if needed create an attributer to describe the "extra bytes" if (laszip_dll->attributer == 0) { laszip_dll->attributer = new LASattributer; if (laszip_dll->attributer == 0) { sprintf(laszip_dll->error, "cannot allocate LASattributer"); return 1; } } // were there any pre-existing extra bytes if (number_of_existing_extrabytes > 0) { // make sure the existing "extra bytes" are documented if (laszip_dll->attributer->get_attributes_size() > number_of_existing_extrabytes) { sprintf(laszip_dll->error, "bad \"extra bytes\" VLR describes %d bytes more than points actually have", laszip_dll->attributer->get_attributes_size() - number_of_existing_extrabytes); return 1; } else if (laszip_dll->attributer->get_attributes_size() < number_of_existing_extrabytes) { // maybe the existing "extra bytes" are documented in a VLR if (laszip_dll->header.vlrs) { for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if ((strcmp(laszip_dll->header.vlrs[i].user_id, "LASF_Spec") == 0) && (laszip_dll->header.vlrs[i].record_id == 4)) { laszip_dll->attributer->init_attributes(laszip_dll->header.vlrs[i].record_length_after_header/sizeof(LASattribute), (LASattribute*)laszip_dll->header.vlrs[i].data); } } } // describe any undocumented "extra bytes" as "unknown" U8 attributes for (I32 i = (I32)(laszip_dll->attributer->get_attributes_size()); i < number_of_existing_extrabytes; i++) { CHAR unknown_name[16]; memset(unknown_name, 0, 16); sprintf(unknown_name, "unknown %d", i); LASattribute lasattribute_unknown(LAS_ATTRIBUTE_U8, unknown_name, unknown_name); if (laszip_dll->attributer->add_attribute(lasattribute_unknown) == -1) { sprintf(laszip_dll->error, "cannot add unknown U8 attribute '%s' of %d to attributer", unknown_name, number_of_existing_extrabytes); return 1; } } } } // create the "extra bytes" that store the newer LAS 1.4 point attributes // scan_angle (difference or remainder) is stored as a I16 LASattribute lasattribute_scan_angle(LAS_ATTRIBUTE_I16, "LAS 1.4 scan angle", "additional attributes"); lasattribute_scan_angle.set_scale(0.006); I32 index_scan_angle = laszip_dll->attributer->add_attribute(lasattribute_scan_angle); laszip_dll->start_scan_angle = laszip_dll->attributer->get_attribute_start(index_scan_angle); // extended returns stored as a U8 LASattribute lasattribute_extended_returns(LAS_ATTRIBUTE_U8, "LAS 1.4 extended returns", "additional attributes"); I32 index_extended_returns = laszip_dll->attributer->add_attribute(lasattribute_extended_returns); laszip_dll->start_extended_returns = laszip_dll->attributer->get_attribute_start(index_extended_returns); // classification stored as a U8 LASattribute lasattribute_classification(LAS_ATTRIBUTE_U8, "LAS 1.4 classification", "additional attributes"); I32 index_classification = laszip_dll->attributer->add_attribute(lasattribute_classification); laszip_dll->start_classification = laszip_dll->attributer->get_attribute_start(index_classification); // flags and channel stored as a U8 LASattribute lasattribute_flags_and_channel(LAS_ATTRIBUTE_U8, "LAS 1.4 flags and channel", "additional attributes"); I32 index_flags_and_channel = laszip_dll->attributer->add_attribute(lasattribute_flags_and_channel); laszip_dll->start_flags_and_channel = laszip_dll->attributer->get_attribute_start(index_flags_and_channel); // maybe store the NIR band as a U16 if (laszip_dll->header.point_data_format == 8 || laszip_dll->header.point_data_format == 10) { // the NIR band is stored as a U16 LASattribute lasattribute_NIR_band(LAS_ATTRIBUTE_U16, "LAS 1.4 NIR band", "additional attributes"); I32 index_NIR_band = laszip_dll->attributer->add_attribute(lasattribute_NIR_band); laszip_dll->start_NIR_band = laszip_dll->attributer->get_attribute_start(index_NIR_band); } else { laszip_dll->start_NIR_band = -1; } // add the extra bytes VLR with the additional attributes if (laszip_add_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4, (laszip_U16)(laszip_dll->attributer->number_attributes*sizeof(LASattribute)), 0, (laszip_U8*)laszip_dll->attributer->attributes)) { sprintf(laszip_dll->error, "adding the extra bytes VLR with the additional attributes"); return 1; } // update point type if (laszip_dll->header.point_data_format == 6) { laszip_dll->header.point_data_format = 1; } else if (laszip_dll->header.point_data_format <= 8) { laszip_dll->header.point_data_format = 3; } else // 9->4 and 10->5 { laszip_dll->header.point_data_format -= 5; } // we are operating in compatibility mode laszip_dll->compatibility_mode = TRUE; } else if (compress) { sprintf(laszip_dll->error, "LASzip DLL %d.%d r%d (%d) cannot compress point data format %d without requesting 'compatibility mode'", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE, (I32)laszip_dll->header.point_data_format); return 1; } } else { // must *not* be set for the old point type 5 or lower laszip_dll->point.extended_point_type = 0; // we are *not* operating in compatibility mode laszip_dll->compatibility_mode = FALSE; } return 0; } /*---------------------------------------------------------------------------*/ static I32 laszip_prepare_vlrs_for_write( laszip_dll_struct* laszip_dll ) { U32 i, vlrs_size = 0; if (laszip_dll->header.number_of_variable_length_records) { if (laszip_dll->header.vlrs == 0) { sprintf(laszip_dll->error, "number_of_variable_length_records is %u but vlrs pointer is zero", laszip_dll->header.number_of_variable_length_records); return 1; } for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { vlrs_size += 54; if (laszip_dll->header.vlrs[i].record_length_after_header) { if (laszip_dll->header.vlrs == 0) { sprintf(laszip_dll->error, "vlrs[%u].record_length_after_header is %u but vlrs[%u].data pointer is zero", i, laszip_dll->header.vlrs[i].record_length_after_header, i); return 1; } vlrs_size += laszip_dll->header.vlrs[i].record_length_after_header; } } } if ((vlrs_size + laszip_dll->header.header_size + laszip_dll->header.user_data_after_header_size) != laszip_dll->header.offset_to_point_data) { sprintf(laszip_dll->error,"header_size (%u) plus vlrs_size (%u) plus user_data_after_header_size (%u) does not equal offset_to_point_data (%u)", (U32)laszip_dll->header.header_size, vlrs_size, laszip_dll->header.user_data_after_header_size, laszip_dll->header.offset_to_point_data); return 1; } return 0; } /*---------------------------------------------------------------------------*/ static U32 laszip_vrl_payload_size( const LASzip* laszip ) { return 34 + (6 * laszip->num_items); } /*---------------------------------------------------------------------------*/ static I32 write_laszip_vlr_header( laszip_dll_struct* laszip_dll , const LASzip* laszip , ByteStreamOut* out ) { // write the LASzip VLR header U16 reserved = 0x0; try { out->put16bitsLE((U8*)&reserved); } catch(...) { sprintf(laszip_dll->error, "writing LASzip VLR header.reserved"); return 1; } U8 user_id[16] = "laszip encoded\0"; try { out->putBytes((U8*)user_id, 16); } catch(...) { sprintf(laszip_dll->error, "writing LASzip VLR header.user_id"); return 1; } U16 record_id = 22204; try { out->put16bitsLE((U8*)&record_id); } catch(...) { sprintf(laszip_dll->error, "writing LASzip VLR header.record_id"); return 1; } U16 record_length_after_header = (U16)laszip_vrl_payload_size(laszip); try { out->put16bitsLE((U8*)&record_length_after_header); } catch(...) { sprintf(laszip_dll->error, "writing LASzip VLR header.record_length_after_header"); return 1; } CHAR description[32]; memset(description, 0, 32); sprintf(description, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); try { out->putBytes((U8*)description, 32); } catch(...) { sprintf(laszip_dll->error, "writing LASzip VLR header.description"); return 1; } return 0; } /*---------------------------------------------------------------------------*/ static I32 write_laszip_vlr_payload( laszip_dll_struct* laszip_dll , const LASzip* laszip , ByteStreamOut* out ) { // write the LASzip VLR payload // U16 compressor 2 bytes // U32 coder 2 bytes // U8 version_major 1 byte // U8 version_minor 1 byte // U16 version_revision 2 bytes // U32 options 4 bytes // I32 chunk_size 4 bytes // I64 number_of_special_evlrs 8 bytes // I64 offset_to_special_evlrs 8 bytes // U16 num_items 2 bytes // U16 type 2 bytes * num_items // U16 size 2 bytes * num_items // U16 version 2 bytes * num_items // which totals 34+6*num_items try { out->put16bitsLE((const U8*)&(laszip->compressor)); } catch(...) { sprintf(laszip_dll->error, "writing compressor %d", (I32)laszip->compressor); return 1; } try { out->put16bitsLE((const U8*)&(laszip->coder)); } catch(...) { sprintf(laszip_dll->error, "writing coder %d", (I32)laszip->coder); return 1; } try { out->putBytes((const U8*)&(laszip->version_major), 1); } catch(...) { sprintf(laszip_dll->error, "writing version_major %d", (I32)laszip->version_major); return 1; } try { out->putBytes((const U8*)&(laszip->version_minor), 1); } catch(...) { sprintf(laszip_dll->error, "writing version_minor %d", (I32)laszip->version_minor); return 1; } try { out->put16bitsLE((const U8*)&(laszip->version_revision)); } catch(...) { sprintf(laszip_dll->error, "writing version_revision %d", (I32)laszip->version_revision); return 1; } try { out->put32bitsLE((const U8*)&(laszip->options)); } catch(...) { sprintf(laszip_dll->error, "writing options %u", laszip->options); return 1; } try { out->put32bitsLE((const U8*)&(laszip->chunk_size)); } catch(...) { sprintf(laszip_dll->error, "writing chunk_size %u", laszip->chunk_size); return 1; } try { out->put64bitsLE((const U8*)&(laszip->number_of_special_evlrs)); } catch(...) { sprintf(laszip_dll->error, "writing number_of_special_evlrs %d", (I32)laszip->number_of_special_evlrs); return 1; } try { out->put64bitsLE((const U8*)&(laszip->offset_to_special_evlrs)); } catch(...) { sprintf(laszip_dll->error, "writing offset_to_special_evlrs %d", (I32)laszip->offset_to_special_evlrs); return 1; } try { out->put16bitsLE((const U8*)&(laszip->num_items)); } catch(...) { sprintf(laszip_dll->error, "writing num_items %d", (I32)laszip->num_items); return 1; } U32 j; for (j = 0; j < laszip->num_items; j++) { U16 type = (U16)(laszip->items[j].type); try { out->put16bitsLE((const U8*)&type); } catch(...) { sprintf(laszip_dll->error, "writing type %d of item %d", (I32)laszip->items[j].type, j); return 1; } try { out->put16bitsLE((const U8*)&(laszip->items[j].size)); } catch(...) { sprintf(laszip_dll->error, "writing size %d of item %d", (I32)laszip->items[j].size, j); return 1; } try { out->put16bitsLE((const U8*)&(laszip->items[j].version)); } catch(...) { sprintf(laszip_dll->error, "writing version %d of item %d", (I32)laszip->items[j].version, j); return 1; } } return 0; } /*---------------------------------------------------------------------------*/ static I32 laszip_write_header( laszip_dll_struct* laszip_dll , const LASzip* laszip , const laszip_BOOL compress ) { U32 i; try { laszip_dll->streamout->putBytes((const U8*)"LASF", 4); } catch(...) { sprintf(laszip_dll->error, "writing header.file_signature"); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.file_source_ID)); } catch(...) { sprintf(laszip_dll->error, "writing header.file_source_ID"); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.global_encoding)); } catch(...) { sprintf(laszip_dll->error, "writing header.global_encoding"); return 1; } try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.project_ID_GUID_data_1)); } catch(...) { sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_1"); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.project_ID_GUID_data_2)); } catch(...) { sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_2"); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.project_ID_GUID_data_3)); } catch(...) { sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_3"); return 1; } try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.project_ID_GUID_data_4, 8); } catch(...) { sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_4"); return 1; } try { laszip_dll->streamout->putBytes((const U8*)&(laszip_dll->header.version_major), 1); } catch(...) { sprintf(laszip_dll->error, "writing header.version_major"); return 1; } try { laszip_dll->streamout->putBytes((const U8*)&(laszip_dll->header.version_minor), 1); } catch(...) { sprintf(laszip_dll->error, "writing header.version_minor"); return 1; } try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.system_identifier, 32); } catch(...) { sprintf(laszip_dll->error, "writing header.system_identifier"); return 1; } if (!laszip_dll->preserve_generating_software) { memset(laszip_dll->header.generating_software, 0, 32); sprintf(laszip_dll->header.generating_software, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); } try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.generating_software, 32); } catch(...) { sprintf(laszip_dll->error, "writing header.generating_software"); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.file_creation_day)); } catch(...) { sprintf(laszip_dll->error, "writing header.file_creation_day"); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.file_creation_year)); } catch(...) { sprintf(laszip_dll->error, "writing header.file_creation_year"); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.header_size)); } catch(...) { sprintf(laszip_dll->error, "writing header.header_size"); return 1; } if (compress) { laszip_dll->header.offset_to_point_data += (54 + laszip_vrl_payload_size(laszip)); } try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.offset_to_point_data)); } catch(...) { sprintf(laszip_dll->error, "writing header.offset_to_point_data"); return 1; } if (compress) { laszip_dll->header.offset_to_point_data -= (54 + laszip_vrl_payload_size(laszip)); laszip_dll->header.number_of_variable_length_records += 1; } try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_variable_length_records)); } catch(...) { sprintf(laszip_dll->error, "writing header.number_of_variable_length_records"); return 1; } if (compress) { laszip_dll->header.number_of_variable_length_records -= 1; laszip_dll->header.point_data_format |= 128; } try { laszip_dll->streamout->putBytes((const U8*)&(laszip_dll->header.point_data_format), 1); } catch(...) { sprintf(laszip_dll->error, "writing header.point_data_format"); return 1; } if (compress) { laszip_dll->header.point_data_format &= 127; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.point_data_record_length)); } catch(...) { sprintf(laszip_dll->error, "writing header.point_data_record_length"); return 1; } try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_point_records)); } catch(...) { sprintf(laszip_dll->error, "writing header.number_of_point_records"); return 1; } for (i = 0; i < 5; i++) { try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_points_by_return[i])); } catch(...) { sprintf(laszip_dll->error, "writing header.number_of_points_by_return %d", i); return 1; } } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.x_scale_factor)); } catch(...) { sprintf(laszip_dll->error, "writing header.x_scale_factor"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.y_scale_factor)); } catch(...) { sprintf(laszip_dll->error, "writing header.y_scale_factor"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.z_scale_factor)); } catch(...) { sprintf(laszip_dll->error, "writing header.z_scale_factor"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.x_offset)); } catch(...) { sprintf(laszip_dll->error, "writing header.x_offset"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.y_offset)); } catch(...) { sprintf(laszip_dll->error, "writing header.y_offset"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.z_offset)); } catch(...) { sprintf(laszip_dll->error, "writing header.z_offset"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.max_x)); } catch(...) { sprintf(laszip_dll->error, "writing header.max_x"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.min_x)); } catch(...) { sprintf(laszip_dll->error, "writing header.min_x"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.max_y)); } catch(...) { sprintf(laszip_dll->error, "writing header.max_y"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.min_y)); } catch(...) { sprintf(laszip_dll->error, "writing header.min_y"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.max_z)); } catch(...) { sprintf(laszip_dll->error, "writing header.max_z"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.min_z)); } catch(...) { sprintf(laszip_dll->error, "writing header.min_z"); return 1; } // special handling for LAS 1.3 if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 3)) { if (laszip_dll->header.header_size < 235) { sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 235 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); return 1; } else { if (laszip_dll->header.start_of_waveform_data_packet_record != 0) { #ifdef _WIN32 sprintf(laszip_dll->warning, "header.start_of_waveform_data_packet_record is %I64d. writing 0 instead.", laszip_dll->header.start_of_waveform_data_packet_record); #else sprintf(laszip_dll->warning, "header.start_of_waveform_data_packet_record is %llu. writing 0 instead.", laszip_dll->header.start_of_waveform_data_packet_record); #endif laszip_dll->header.start_of_waveform_data_packet_record = 0; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.start_of_waveform_data_packet_record)); } catch(...) { sprintf(laszip_dll->error, "writing header.start_of_waveform_data_packet_record"); return 1; } laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 235; } } else { laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 227; } // special handling for LAS 1.4 if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 4)) { if (laszip_dll->header.header_size < 375) { sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 375 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); return 1; } else { try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.start_of_first_extended_variable_length_record)); } catch(...) { sprintf(laszip_dll->error, "writing header.start_of_first_extended_variable_length_record"); return 1; } try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_extended_variable_length_records)); } catch(...) { sprintf(laszip_dll->error, "writing header.number_of_extended_variable_length_records"); return 1; } try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.extended_number_of_point_records)); } catch(...) { sprintf(laszip_dll->error, "writing header.extended_number_of_point_records"); return 1; } for (i = 0; i < 15; i++) { try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.extended_number_of_points_by_return[i])); } catch(...) { sprintf(laszip_dll->error, "writing header.extended_number_of_points_by_return[%d]", i); return 1; } } laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 375; } } // write any number of user-defined bytes that might have been added to the header if (laszip_dll->header.user_data_in_header_size) { try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.user_data_in_header, laszip_dll->header.user_data_in_header_size); } catch(...) { sprintf(laszip_dll->error, "writing %d bytes of data into header.user_data_in_header", laszip_dll->header.user_data_in_header_size); return 1; } } // write variable length records into the header if (laszip_dll->header.number_of_variable_length_records) { U32 i; for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { // write variable length records variable after variable (to avoid alignment issues) try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.vlrs[i].reserved)); } catch(...) { sprintf(laszip_dll->error, "writing header.vlrs[%d].reserved", i); return 1; } try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.vlrs[i].user_id, 16); } catch(...) { sprintf(laszip_dll->error, "writing header.vlrs[%d].user_id", i); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.vlrs[i].record_id)); } catch(...) { sprintf(laszip_dll->error, "writing header.vlrs[%d].record_id", i); return 1; } try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.vlrs[i].record_length_after_header)); } catch(...) { sprintf(laszip_dll->error, "writing header.vlrs[%d].record_length_after_header", i); return 1; } try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.vlrs[i].description, 32); } catch(...) { sprintf(laszip_dll->error, "writing header.vlrs[%d].description", i); return 1; } // write data following the header of the variable length record if (laszip_dll->header.vlrs[i].record_length_after_header) { try { laszip_dll->streamout->putBytes(laszip_dll->header.vlrs[i].data, laszip_dll->header.vlrs[i].record_length_after_header); } catch(...) { sprintf(laszip_dll->error, "writing %d bytes of data into header.vlrs[%d].data", laszip_dll->header.vlrs[i].record_length_after_header, i); return 1; } } } } if (compress) { // write the LASzip VLR header if (write_laszip_vlr_header(laszip_dll, laszip, laszip_dll->streamout)) { return 1; } // write the LASzip VLR payload if (write_laszip_vlr_payload(laszip_dll, laszip, laszip_dll->streamout)) { return 1; } } // write any number of user-defined bytes that might have been added after the header if (laszip_dll->header.user_data_after_header_size) { try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.user_data_after_header, laszip_dll->header.user_data_after_header_size); } catch(...) { sprintf(laszip_dll->error, "writing %u bytes of data into header.user_data_after_header", laszip_dll->header.user_data_after_header_size); return 1; } } return 0; } /*----------------------------------------------------------------------------*/ laszip_I32 create_point_writer ( laszip_dll_struct *laszip_dll , const LASzip *laszip ) { // create the point writer laszip_dll->writer = new LASwritePoint(); if (laszip_dll->writer == 0) { sprintf(laszip_dll->error, "could not alloc LASwritePoint"); return 1; } if (!laszip_dll->writer->setup(laszip->num_items, laszip->items, laszip)) { sprintf(laszip_dll->error, "setup of LASwritePoint failed"); return 1; } if (!laszip_dll->writer->init(laszip_dll->streamout)) { sprintf(laszip_dll->error, "init of LASwritePoint failed"); return 1; } return 0; } /*---------------------------------------------------------------------------*/ static I32 setup_laszip_items( laszip_dll_struct* laszip_dll , LASzip* laszip , laszip_BOOL compress ) { laszip_U8 point_type = laszip_dll->header.point_data_format; laszip_U16 point_size = laszip_dll->header.point_data_record_length; if ((point_type > 5) && laszip_dll->request_compatibility_mode) { if (!laszip->request_compatibility_mode(1)) { sprintf(laszip_dll->error, "requesting 'compatibility mode' has failed"); return 1; } } // create point items in the LASzip structure from point format and size if (!laszip->setup(point_type, point_size, LASZIP_COMPRESSOR_NONE)) { sprintf(laszip_dll->error, "invalid combination of point_type %d and point_size %d", (I32)point_type, (I32)point_size); return 1; } // compute offsets (or points item pointers) for data transfer from the point items if (laszip_dll->point_items) { delete [] laszip_dll->point_items; } laszip_dll->point_items = new U8*[laszip->num_items]; if (laszip_dll->point_items == 0) { sprintf(laszip_dll->error, "could not alloc point_items"); return 1; } for (size_t i = 0; i < laszip->num_items; i++) { switch (laszip->items[i].type) { case LASitem::POINT10: case LASitem::POINT14: laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.X); break; case LASitem::GPSTIME11: laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.gps_time); break; case LASitem::RGB12: case LASitem::RGB14: case LASitem::RGBNIR14: laszip_dll->point_items[i] = (U8*)laszip_dll->point.rgb; break; case LASitem::BYTE: case LASitem::BYTE14: laszip_dll->point.num_extra_bytes = laszip->items[i].size; if (laszip_dll->point.extra_bytes) delete [] laszip_dll->point.extra_bytes; laszip_dll->point.extra_bytes = new U8[laszip_dll->point.num_extra_bytes]; laszip_dll->point_items[i] = laszip_dll->point.extra_bytes; break; case LASitem::WAVEPACKET13: case LASitem::WAVEPACKET14: laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.wave_packet); break; default: sprintf(laszip_dll->error, "unknown LASitem type %d", (I32)laszip->items[i].type); return 1; } } if (compress) { if ((point_type > 5) && laszip_dll->request_native_extension) { if (!laszip->setup(point_type, point_size, LASZIP_COMPRESSOR_LAYERED_CHUNKED)) { sprintf(laszip_dll->error, "cannot compress point_type %d with point_size %d using native", (I32)point_type, (I32)point_size); return 1; } } else { if (!laszip->setup(point_type, point_size, LASZIP_COMPRESSOR_DEFAULT)) { sprintf(laszip_dll->error, "cannot compress point_type %d with point_size %d", (I32)point_type, (I32)point_size); return 1; } } // request version (old point types only, new point types always use version 3) laszip->request_version(2); // maybe we should change the chunk size if (laszip_dll->set_chunk_size != LASZIP_CHUNK_SIZE_DEFAULT) { if (!laszip->set_chunk_size(laszip_dll->set_chunk_size)) { sprintf(laszip_dll->error, "setting chunk size %d has failed", laszip_dll->set_chunk_size); return 1; } } } else { laszip->request_version(0); } return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_open_writer( laszip_POINTER pointer , const laszip_CHAR* file_name , laszip_BOOL compress ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (file_name == 0) { sprintf(laszip_dll->error, "laszip_CHAR pointer 'file_name' is zero"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } // open the file #ifdef _MSC_VER wchar_t* utf16_file_name = UTF8toUTF16(file_name); laszip_dll->file = _wfopen(utf16_file_name, L"wb"); delete [] utf16_file_name; #else laszip_dll->file = fopen(file_name, "wb"); #endif if (laszip_dll->file == 0) { sprintf(laszip_dll->error, "cannot open file '%s'", file_name); return 1; } if (setvbuf(laszip_dll->file, NULL, _IOFBF, 262144) != 0) { sprintf(laszip_dll->warning, "setvbuf() failed with buffer size 262144\n"); } // create the outstream if (IS_LITTLE_ENDIAN()) laszip_dll->streamout = new ByteStreamOutFileLE(laszip_dll->file); else laszip_dll->streamout = new ByteStreamOutFileBE(laszip_dll->file); if (laszip_dll->streamout == 0) { sprintf(laszip_dll->error, "could not alloc ByteStreamOutFile"); return 1; } // setup the items that make up the point LASzip laszip; if (setup_laszip_items(laszip_dll, &laszip, compress)) { return 1; } // prepare header if (laszip_prepare_header_for_write(laszip_dll)) { return 1; } // prepare point if (laszip_prepare_point_for_write(laszip_dll, compress)) { return 1; } // prepare VLRs if (laszip_prepare_vlrs_for_write(laszip_dll)) { return 1; } // write header variable after variable if (laszip_write_header(laszip_dll, &laszip, compress)) { return 1; } // create the point writer if (create_point_writer(laszip_dll, &laszip)) { return 1; } if (laszip_dll->lax_create) { // create spatial indexing information using cell_size = 100.0f and threshold = 1000 LASquadtree* lasquadtree = new LASquadtree; lasquadtree->setup(laszip_dll->header.min_x, laszip_dll->header.max_x, laszip_dll->header.min_y, laszip_dll->header.max_y, 100.0f); laszip_dll->lax_index = new LASindex; laszip_dll->lax_index->prepare(lasquadtree, 1000); // copy the file name for later laszip_dll->lax_file_name = LASCopyString(file_name); } // set the point number and point count laszip_dll->npoints = (laszip_dll->header.number_of_point_records ? laszip_dll->header.number_of_point_records : laszip_dll->header.extended_number_of_point_records); laszip_dll->p_count = 0; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_open_writer '%s'", file_name); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_write_point( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { // temporary fix to avoid corrupt LAZ files if (laszip_dll->point.extended_point_type) { // make sure legacy flags and extended flags are identical if ((laszip_dll->point.extended_classification_flags & 0x7) != ((((U8*)&(laszip_dll->point.intensity))[3]) >> 5)) { sprintf(laszip_dll->error, "legacy flags and extended flags are not identical"); return 1; } // make sure legacy classification is zero or identical to extended classification if (laszip_dll->point.classification != 0) { if (laszip_dll->point.classification != laszip_dll->point.extended_classification) { sprintf(laszip_dll->error, "legacy classification %d and extended classification %d are not consistent", laszip_dll->point.classification, laszip_dll->point.extended_classification); return 1; } } } // special recoding of points (in compatibility mode only) if (laszip_dll->compatibility_mode) { I32 scan_angle_remainder; I32 number_of_returns_increment; I32 return_number_increment; I32 return_count_difference; I32 overlap_bit; I32 scanner_channel; // distill extended attributes struct laszip_point* point = &laszip_dll->point; point->scan_angle_rank = I8_CLAMP(I16_QUANTIZE(0.006f*point->extended_scan_angle)); scan_angle_remainder = point->extended_scan_angle - I16_QUANTIZE(((F32)point->scan_angle_rank)/0.006f); if (point->extended_number_of_returns <= 7) { point->number_of_returns = point->extended_number_of_returns; if (point->extended_return_number <= 7) { point->return_number = point->extended_return_number; } else { point->return_number = 7; } } else { point->number_of_returns = 7; if (point->extended_return_number <= 4) { point->return_number = point->extended_return_number; } else { return_count_difference = point->extended_number_of_returns - point->extended_return_number; if (return_count_difference <= 0) { point->return_number = 7; } else if (return_count_difference >= 3) { point->return_number = 4; } else { point->return_number = 7 - return_count_difference; } } } return_number_increment = point->extended_return_number - point->return_number; number_of_returns_increment = point->extended_number_of_returns - point->number_of_returns; if (point->extended_classification > 31) { point->classification = 0; } else { point->extended_classification = 0; } scanner_channel = point->extended_scanner_channel; overlap_bit = (point->extended_classification_flags >> 3); // write distilled extended attributes into extra bytes *((I16*)(point->extra_bytes + laszip_dll->start_scan_angle)) = ((I16)scan_angle_remainder); point->extra_bytes[laszip_dll->start_extended_returns] = (U8)((return_number_increment << 4) | number_of_returns_increment); point->extra_bytes[laszip_dll->start_classification] = (U8)(point->extended_classification); point->extra_bytes[laszip_dll->start_flags_and_channel] = (U8)((scanner_channel << 1) | overlap_bit); if (laszip_dll->start_NIR_band != -1) { *((U16*)(point->extra_bytes + laszip_dll->start_NIR_band)) = point->rgb[3]; } } // write the point if (!laszip_dll->writer->write(laszip_dll->point_items)) { #ifdef _WIN32 sprintf(laszip_dll->error, "writing point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); #else sprintf(laszip_dll->error, "writing point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); #endif return 1; } laszip_dll->p_count++; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_write_point"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_write_indexed_point( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { // write the point if (!laszip_dll->writer->write(laszip_dll->point_items)) { #ifdef _WIN32 sprintf(laszip_dll->error, "writing point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); #else sprintf(laszip_dll->error, "writing point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); #endif return 1; } // index the point F64 x = laszip_dll->header.x_scale_factor*laszip_dll->point.X+laszip_dll->header.x_offset; F64 y = laszip_dll->header.y_scale_factor*laszip_dll->point.Y+laszip_dll->header.y_offset; laszip_dll->lax_index->add(x, y, (U32)laszip_dll->p_count); laszip_dll->p_count++; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_write_indexed_point"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_update_inventory( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->inventory == 0) { laszip_dll->inventory = new laszip_dll_inventory; } laszip_dll->inventory->add(&laszip_dll->point); } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_update_inventory"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_close_writer( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->writer == 0) { sprintf(laszip_dll->error, "closing writer before it was opened"); return 1; } if (!laszip_dll->writer->done()) { sprintf(laszip_dll->error, "done of LASwritePoint failed"); return 1; } delete laszip_dll->writer; laszip_dll->writer = 0; delete [] laszip_dll->point_items; laszip_dll->point_items = 0; // maybe update the header if (laszip_dll->inventory) { if (laszip_dll->header.point_data_format <= 5) // only update legacy counters for old point types { laszip_dll->streamout->seek(107); if (!laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->inventory->number_of_point_records))) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->number_of_point_records"); return 1; } for (I32 i = 0; i < 5; i++) { if (!laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->inventory->number_of_points_by_return[i+1]))) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->number_of_points_by_return[%d]\n", i); return 1; } } } laszip_dll->streamout->seek(179); F64 value; value = laszip_dll->header.x_scale_factor*laszip_dll->inventory->max_X+laszip_dll->header.x_offset; if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->max_X"); return 1; } value = laszip_dll->header.x_scale_factor*laszip_dll->inventory->min_X+laszip_dll->header.x_offset; if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->min_X"); return 1; } value = laszip_dll->header.y_scale_factor*laszip_dll->inventory->max_Y+laszip_dll->header.y_offset; if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->max_Y"); return 1; } value = laszip_dll->header.y_scale_factor*laszip_dll->inventory->min_Y+laszip_dll->header.y_offset; if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->min_Y"); return 1; } value = laszip_dll->header.z_scale_factor*laszip_dll->inventory->max_Z+laszip_dll->header.z_offset; if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->max_Z"); return 1; } value = laszip_dll->header.z_scale_factor*laszip_dll->inventory->min_Z+laszip_dll->header.z_offset; if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->min_Z"); return 1; } if (laszip_dll->header.version_minor >= 4) // only update extended counters for LAS 1.4 { laszip_dll->streamout->seek(247); I64 number = laszip_dll->inventory->number_of_point_records; if (!laszip_dll->streamout->put64bitsLE((const U8*)&number)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->extended_number_of_point_records"); return 1; } for (I32 i = 0; i < 15; i++) { number = laszip_dll->inventory->number_of_points_by_return[i+1]; if (!laszip_dll->streamout->put64bitsLE((const U8*)&number)) { sprintf(laszip_dll->error, "updating laszip_dll->inventory->extended_number_of_points_by_return[%d]\n", i); return 1; } } } laszip_dll->streamout->seekEnd(); delete laszip_dll->inventory; laszip_dll->inventory = 0; } if (laszip_dll->lax_index) { laszip_dll->lax_index->complete(100000, -20, FALSE); if (!laszip_dll->lax_index->write(laszip_dll->lax_file_name)) { sprintf(laszip_dll->error, "writing LAX file to '%s'", laszip_dll->lax_file_name); return 1; } free(laszip_dll->lax_file_name); laszip_dll->lax_file_name = 0; delete laszip_dll->lax_index; laszip_dll->lax_index = 0; } delete laszip_dll->streamout; laszip_dll->streamout = 0; if (laszip_dll->file) { fclose(laszip_dll->file); laszip_dll->file = 0; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_writer_close"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_exploit_spatial_index( laszip_POINTER pointer , const laszip_BOOL exploit ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } laszip_dll->lax_exploit = exploit; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_exploit_spatial_index"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_decompress_selective( laszip_POINTER pointer , const laszip_U32 decompress_selective ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } laszip_dll->las14_decompress_selective = decompress_selective; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_decompress_selective"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ static I32 laszip_read_header( laszip_dll_struct* laszip_dll , laszip_BOOL* is_compressed ) { U32 i; // read the header variable after variable CHAR file_signature[5]; try { laszip_dll->streamin->getBytes((U8*)file_signature, 4); } catch(...) { sprintf(laszip_dll->error, "reading header.file_signature"); return 1; } if (strncmp(file_signature, "LASF", 4) != 0) { sprintf(laszip_dll->error, "wrong file_signature. not a LAS/LAZ file."); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.file_source_ID)); } catch(...) { sprintf(laszip_dll->error, "reading header.file_source_ID"); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.global_encoding)); } catch(...) { sprintf(laszip_dll->error, "reading header.global_encoding"); return 1; } try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.project_ID_GUID_data_1)); } catch(...) { sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_1"); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.project_ID_GUID_data_2)); } catch(...) { sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_2"); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.project_ID_GUID_data_3)); } catch(...) { sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_3"); return 1; } try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.project_ID_GUID_data_4, 8); } catch(...) { sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_4"); return 1; } try { laszip_dll->streamin->getBytes((U8*)&(laszip_dll->header.version_major), 1); } catch(...) { sprintf(laszip_dll->error, "reading header.version_major"); return 1; } try { laszip_dll->streamin->getBytes((U8*)&(laszip_dll->header.version_minor), 1); } catch(...) { sprintf(laszip_dll->error, "reading header.version_minor"); return 1; } try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.system_identifier, 32); } catch(...) { sprintf(laszip_dll->error, "reading header.system_identifier"); return 1; } try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.generating_software, 32); } catch(...) { sprintf(laszip_dll->error, "reading header.generating_software"); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.file_creation_day)); } catch(...) { sprintf(laszip_dll->error, "reading header.file_creation_day"); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.file_creation_year)); } catch(...) { sprintf(laszip_dll->error, "reading header.file_creation_year"); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.header_size)); } catch(...) { sprintf(laszip_dll->error, "reading header.header_size"); return 1; } try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.offset_to_point_data)); } catch(...) { sprintf(laszip_dll->error, "reading header.offset_to_point_data"); return 1; } try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_variable_length_records)); } catch(...) { sprintf(laszip_dll->error, "reading header.number_of_variable_length_records"); return 1; } try { laszip_dll->streamin->getBytes((U8*)&(laszip_dll->header.point_data_format), 1); } catch(...) { sprintf(laszip_dll->error, "reading header.point_data_format"); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.point_data_record_length)); } catch(...) { sprintf(laszip_dll->error, "reading header.point_data_record_length"); return 1; } try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_point_records)); } catch(...) { sprintf(laszip_dll->error, "reading header.number_of_point_records"); return 1; } for (i = 0; i < 5; i++) { try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_points_by_return[i])); } catch(...) { sprintf(laszip_dll->error, "reading header.number_of_points_by_return %d", i); return 1; } } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.x_scale_factor)); } catch(...) { sprintf(laszip_dll->error, "reading header.x_scale_factor"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.y_scale_factor)); } catch(...) { sprintf(laszip_dll->error, "reading header.y_scale_factor"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.z_scale_factor)); } catch(...) { sprintf(laszip_dll->error, "reading header.z_scale_factor"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.x_offset)); } catch(...) { sprintf(laszip_dll->error, "reading header.x_offset"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.y_offset)); } catch(...) { sprintf(laszip_dll->error, "reading header.y_offset"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.z_offset)); } catch(...) { sprintf(laszip_dll->error, "reading header.z_offset"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.max_x)); } catch(...) { sprintf(laszip_dll->error, "reading header.max_x"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.min_x)); } catch(...) { sprintf(laszip_dll->error, "reading header.min_x"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.max_y)); } catch(...) { sprintf(laszip_dll->error, "reading header.max_y"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.min_y)); } catch(...) { sprintf(laszip_dll->error, "reading header.min_y"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.max_z)); } catch(...) { sprintf(laszip_dll->error, "reading header.max_z"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.min_z)); } catch(...) { sprintf(laszip_dll->error, "reading header.min_z"); return 1; } // special handling for LAS 1.3 if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 3)) { if (laszip_dll->header.header_size < 235) { sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 235 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); return 1; } else { try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.start_of_waveform_data_packet_record)); } catch(...) { sprintf(laszip_dll->error, "reading header.start_of_waveform_data_packet_record"); return 1; } laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 235; } } else { laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 227; } // special handling for LAS 1.4 if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 4)) { if (laszip_dll->header.header_size < 375) { sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 375 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); return 1; } else { try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.start_of_first_extended_variable_length_record)); } catch(...) { sprintf(laszip_dll->error, "reading header.start_of_first_extended_variable_length_record"); return 1; } try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_extended_variable_length_records)); } catch(...) { sprintf(laszip_dll->error, "reading header.number_of_extended_variable_length_records"); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.extended_number_of_point_records)); } catch(...) { sprintf(laszip_dll->error, "reading header.extended_number_of_point_records"); return 1; } for (i = 0; i < 15; i++) { try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.extended_number_of_points_by_return[i])); } catch(...) { sprintf(laszip_dll->error, "reading header.extended_number_of_points_by_return[%d]", i); return 1; } } laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 375; } } // load any number of user-defined bytes that might have been added to the header if (laszip_dll->header.user_data_in_header_size) { if (laszip_dll->header.user_data_in_header) { delete [] laszip_dll->header.user_data_in_header; } laszip_dll->header.user_data_in_header = new U8[laszip_dll->header.user_data_in_header_size]; try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.user_data_in_header, laszip_dll->header.user_data_in_header_size); } catch(...) { sprintf(laszip_dll->error, "reading %u bytes of data into header.user_data_in_header", laszip_dll->header.user_data_in_header_size); return 1; } } // read variable length records into the header U32 vlrs_size = 0; LASzip* laszip = 0; if (laszip_dll->header.number_of_variable_length_records) { U32 i; laszip_dll->header.vlrs = (laszip_vlr*)malloc(sizeof(laszip_vlr)*laszip_dll->header.number_of_variable_length_records); if (laszip_dll->header.vlrs == 0) { sprintf(laszip_dll->error, "allocating %u VLRs", laszip_dll->header.number_of_variable_length_records); return 1; } for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { // make sure there are enough bytes left to read a variable length record before the point block starts if (((int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size) < 54) { sprintf(laszip_dll->warning, "only %d bytes until point block after reading %d of %d vlrs. skipping remaining vlrs ...", (int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size, i, laszip_dll->header.number_of_variable_length_records); laszip_dll->header.number_of_variable_length_records = i; break; } // read variable length records variable after variable (to avoid alignment issues) try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.vlrs[i].reserved)); } catch(...) { sprintf(laszip_dll->error, "reading header.vlrs[%u].reserved", i); return 1; } try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.vlrs[i].user_id, 16); } catch(...) { sprintf(laszip_dll->error, "reading header.vlrs[%u].user_id", i); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.vlrs[i].record_id)); } catch(...) { sprintf(laszip_dll->error, "reading header.vlrs[%u].record_id", i); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.vlrs[i].record_length_after_header)); } catch(...) { sprintf(laszip_dll->error, "reading header.vlrs[%u].record_length_after_header", i); return 1; } try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.vlrs[i].description, 32); } catch(...) { sprintf(laszip_dll->error, "reading header.vlrs[%u].description", i); return 1; } // keep track on the number of bytes we have read so far vlrs_size += 54; // check variable length record contents if ((laszip_dll->header.vlrs[i].reserved != 0xAABB) && (laszip_dll->header.vlrs[i].reserved != 0x0)) { sprintf(laszip_dll->warning,"wrong header.vlrs[%d].reserved: %d != 0xAABB and %d != 0x0", i, laszip_dll->header.vlrs[i].reserved, laszip_dll->header.vlrs[i].reserved); } // make sure there are enough bytes left to read the data of the variable length record before the point block starts if (((int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size) < laszip_dll->header.vlrs[i].record_length_after_header) { sprintf(laszip_dll->warning, "only %d bytes until point block when trying to read %d bytes into header.vlrs[%d].data", (int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size, laszip_dll->header.vlrs[i].record_length_after_header, i); laszip_dll->header.vlrs[i].record_length_after_header = (int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size; } // load data following the header of the variable length record if (laszip_dll->header.vlrs[i].record_length_after_header) { if ((strcmp(laszip_dll->header.vlrs[i].user_id, "laszip encoded") == 0) && (laszip_dll->header.vlrs[i].record_id == 22204)) { if (laszip) { delete laszip; } laszip = new LASzip(); if (laszip == 0) { sprintf(laszip_dll->error, "could not alloc LASzip"); return 1; } // read the LASzip VLR payload // U16 compressor 2 bytes // U32 coder 2 bytes // U8 version_major 1 byte // U8 version_minor 1 byte // U16 version_revision 2 bytes // U32 options 4 bytes // I32 chunk_size 4 bytes // I64 number_of_special_evlrs 8 bytes // I64 offset_to_special_evlrs 8 bytes // U16 num_items 2 bytes // U16 type 2 bytes * num_items // U16 size 2 bytes * num_items // U16 version 2 bytes * num_items // which totals 34+6*num_items try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->compressor)); } catch(...) { sprintf(laszip_dll->error, "reading compressor %d", (I32)laszip->compressor); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->coder)); } catch(...) { sprintf(laszip_dll->error, "reading coder %d", (I32)laszip->coder); return 1; } try { laszip_dll->streamin->getBytes((U8*)&(laszip->version_major), 1); } catch(...) { sprintf(laszip_dll->error, "reading version_major %d", (I32)laszip->version_major); return 1; } try { laszip_dll->streamin->getBytes((U8*)&(laszip->version_minor), 1); } catch(...) { sprintf(laszip_dll->error, "reading version_minor %d", (I32)laszip->version_minor); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->version_revision)); } catch(...) { sprintf(laszip_dll->error, "reading version_revision %d", (I32)laszip->version_revision); return 1; } try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip->options)); } catch(...) { sprintf(laszip_dll->error, "reading options %u", laszip->options); return 1; } try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip->chunk_size)); } catch(...) { sprintf(laszip_dll->error, "reading chunk_size %u", laszip->chunk_size); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip->number_of_special_evlrs)); } catch(...) { sprintf(laszip_dll->error, "reading number_of_special_evlrs %d", (I32)laszip->number_of_special_evlrs); return 1; } try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip->offset_to_special_evlrs)); } catch(...) { sprintf(laszip_dll->error, "reading offset_to_special_evlrs %d", (I32)laszip->offset_to_special_evlrs); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->num_items)); } catch(...) { sprintf(laszip_dll->error, "reading num_items %d", (I32)laszip->num_items); return 1; } laszip->items = new LASitem[laszip->num_items]; U32 j; for (j = 0; j < laszip->num_items; j++) { U16 type; try { laszip_dll->streamin->get16bitsLE((U8*)&type); } catch(...) { sprintf(laszip_dll->error, "reading type of item %u", j); return 1; } laszip->items[j].type = (LASitem::Type)type; try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->items[j].size)); } catch(...) { sprintf(laszip_dll->error, "reading size of item %u", j); return 1; } try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->items[j].version)); } catch(...) { sprintf(laszip_dll->error, "reading version of item %u", j); return 1; } } } else { laszip_dll->header.vlrs[i].data = new U8[laszip_dll->header.vlrs[i].record_length_after_header]; try { laszip_dll->streamin->getBytes(laszip_dll->header.vlrs[i].data, laszip_dll->header.vlrs[i].record_length_after_header); } catch(...) { sprintf(laszip_dll->error, "reading %d bytes of data into header.vlrs[%u].data", (I32)laszip_dll->header.vlrs[i].record_length_after_header, i); return 1; } } } else { laszip_dll->header.vlrs[i].data = 0; } // keep track on the number of bytes we have read so far vlrs_size += laszip_dll->header.vlrs[i].record_length_after_header; // special handling for LASzip VLR if ((strcmp(laszip_dll->header.vlrs[i].user_id, "laszip encoded") == 0) && (laszip_dll->header.vlrs[i].record_id == 22204)) { // we take our the VLR for LASzip away laszip_dll->header.offset_to_point_data -= (54+laszip_dll->header.vlrs[i].record_length_after_header); vlrs_size -= (54+laszip_dll->header.vlrs[i].record_length_after_header); i--; laszip_dll->header.number_of_variable_length_records--; // free or resize the VLR array if (laszip_dll->header.number_of_variable_length_records == 0) { free(laszip_dll->header.vlrs); laszip_dll->header.vlrs = 0; } else { laszip_dll->header.vlrs = (laszip_vlr*)realloc(laszip_dll->header.vlrs, sizeof(laszip_vlr)*laszip_dll->header.number_of_variable_length_records); } } } } // load any number of user-defined bytes that might have been added after the header laszip_dll->header.user_data_after_header_size = (I32)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size; if (laszip_dll->header.user_data_after_header_size) { if (laszip_dll->header.user_data_after_header) { delete [] laszip_dll->header.user_data_after_header; } laszip_dll->header.user_data_after_header = new U8[laszip_dll->header.user_data_after_header_size]; try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.user_data_after_header, laszip_dll->header.user_data_after_header_size); } catch(...) { sprintf(laszip_dll->error, "reading %u bytes of data into header.user_data_after_header", laszip_dll->header.user_data_after_header_size); return 1; } } // remove extra bits in point data type if ((laszip_dll->header.point_data_format & 128) || (laszip_dll->header.point_data_format & 64)) { if (!laszip) { sprintf(laszip_dll->error, "this file was compressed with an experimental version of LASzip. contact 'martin.isenburg@rapidlasso.com' for assistance"); return 1; } laszip_dll->header.point_data_format &= 127; } // check if file is compressed if (laszip) { // yes. check the compressor state *is_compressed = 1; if (!laszip->check(laszip_dll->header.point_data_record_length)) { sprintf(laszip_dll->error, "%s upgrade to the latest release of LASzip or contact 'martin.isenburg@rapidlasso.com' for assistance", laszip->get_error()); return 1; } } else { // no. setup an un-compressed read *is_compressed = 0; laszip = new LASzip; if (laszip == 0) { sprintf(laszip_dll->error, "could not alloc LASzip"); return 1; } if (!laszip->setup(laszip_dll->header.point_data_format, laszip_dll->header.point_data_record_length, LASZIP_COMPRESSOR_NONE)) { sprintf(laszip_dll->error, "invalid combination of point_data_format %d and point_data_record_length %d", (I32)laszip_dll->header.point_data_format, (I32)laszip_dll->header.point_data_record_length); return 1; } } // create point's item pointers if (laszip_dll->point_items) { delete [] laszip_dll->point_items; } laszip_dll->point_items = new U8*[laszip->num_items]; if (laszip_dll->point_items == 0) { sprintf(laszip_dll->error, "could not alloc point_items"); return 1; } for (i = 0; i < laszip->num_items; i++) { switch (laszip->items[i].type) { case LASitem::POINT10: case LASitem::POINT14: laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.X); break; case LASitem::GPSTIME11: laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.gps_time); break; case LASitem::RGB12: case LASitem::RGB14: case LASitem::RGBNIR14: laszip_dll->point_items[i] = (U8*)laszip_dll->point.rgb; break; case LASitem::BYTE: case LASitem::BYTE14: laszip_dll->point.num_extra_bytes = laszip->items[i].size; if (laszip_dll->point.extra_bytes) delete [] laszip_dll->point.extra_bytes; laszip_dll->point.extra_bytes = new U8[laszip_dll->point.num_extra_bytes]; laszip_dll->point_items[i] = laszip_dll->point.extra_bytes; break; case LASitem::WAVEPACKET13: case LASitem::WAVEPACKET14: laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.wave_packet); break; default: sprintf(laszip_dll->error, "unknown LASitem type %d", (I32)laszip->items[i].type); return 1; } } // did the user request to recode the compatibility mode points? laszip_dll->compatibility_mode = FALSE; if (laszip_dll->request_compatibility_mode && (laszip_dll->header.version_minor < 4)) { // does this file contain compatibility mode recoded LAS 1.4 content struct laszip_vlr* compatibility_VLR = 0; if (laszip_dll->header.point_data_format == 1 || laszip_dll->header.point_data_format == 3 || laszip_dll->header.point_data_format == 4 || laszip_dll->header.point_data_format == 5) { // if we find the compatibility VLR for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if ((strncmp(laszip_dll->header.vlrs[i].user_id, "lascompatible\0\0", 16) == 0) && (laszip_dll->header.vlrs[i].record_id == 22204)) { if (laszip_dll->header.vlrs[i].record_length_after_header == 2+2+4+148) { compatibility_VLR = &(laszip_dll->header.vlrs[i]); break; } } } if (compatibility_VLR) { // and we also find the extra bytes VLR with the right attributes LASattributer attributer; for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if ((strncmp(laszip_dll->header.vlrs[i].user_id, "LASF_Spec\0\0\0\0\0\0", 16) == 0) && (laszip_dll->header.vlrs[i].record_id == 4)) { attributer.init_attributes(laszip_dll->header.vlrs[i].record_length_after_header/192, (LASattribute*)laszip_dll->header.vlrs[i].data); laszip_dll->start_scan_angle = attributer.get_attribute_start("LAS 1.4 scan angle"); laszip_dll->start_extended_returns = attributer.get_attribute_start("LAS 1.4 extended returns"); laszip_dll->start_classification = attributer.get_attribute_start("LAS 1.4 classification"); laszip_dll->start_flags_and_channel = attributer.get_attribute_start("LAS 1.4 flags and channel"); laszip_dll->start_NIR_band = attributer.get_attribute_start("LAS 1.4 NIR band"); break; } } // can we do it ... ? if ((laszip_dll->start_scan_angle != -1) && (laszip_dll->start_extended_returns != -1) && (laszip_dll->start_classification != -1) && (laszip_dll->start_flags_and_channel != -1)) { // yes ... so let's fix the header (using the content from the compatibility VLR) ByteStreamInArray* in; if (IS_LITTLE_ENDIAN()) in = new ByteStreamInArrayLE(compatibility_VLR->data, compatibility_VLR->record_length_after_header); else in = new ByteStreamInArrayBE(compatibility_VLR->data, compatibility_VLR->record_length_after_header); // read control info U16 laszip_version; in->get16bitsLE((U8*)&laszip_version); U16 compatible_version; in->get16bitsLE((U8*)&compatible_version); U32 unused; in->get32bitsLE((U8*)&unused); // read the 148 bytes of the extended LAS 1.4 header U64 start_of_waveform_data_packet_record; in->get64bitsLE((U8*)&start_of_waveform_data_packet_record); if (start_of_waveform_data_packet_record != 0) { #ifdef _WIN32 fprintf(stderr,"WARNING: start_of_waveform_data_packet_record is %I64d. reading 0 instead.\n", start_of_waveform_data_packet_record); #else fprintf(stderr,"WARNING: start_of_waveform_data_packet_record is %llu. reading 0 instead.\n", start_of_waveform_data_packet_record); #endif } laszip_dll->header.start_of_waveform_data_packet_record = 0; U64 start_of_first_extended_variable_length_record; in->get64bitsLE((U8*)&start_of_first_extended_variable_length_record); if (start_of_first_extended_variable_length_record != 0) { #ifdef _WIN32 fprintf(stderr,"WARNING: EVLRs not supported. start_of_first_extended_variable_length_record is %I64d. reading 0 instead.\n", start_of_first_extended_variable_length_record); #else fprintf(stderr,"WARNING: EVLRs not supported. start_of_first_extended_variable_length_record is %llu. reading 0 instead.\n", start_of_first_extended_variable_length_record); #endif } laszip_dll->header.start_of_first_extended_variable_length_record = 0; U32 number_of_extended_variable_length_records ; in->get32bitsLE((U8*)&number_of_extended_variable_length_records); if (number_of_extended_variable_length_records != 0) { fprintf(stderr,"WARNING: EVLRs not supported. number_of_extended_variable_length_records is %u. reading 0 instead.\n", number_of_extended_variable_length_records); } laszip_dll->header.number_of_extended_variable_length_records = 0; U64 extended_number_of_point_records = 0; in->get64bitsLE((U8*)&extended_number_of_point_records); if (laszip_dll->header.number_of_point_records != 0 && ((U64)(laszip_dll->header.number_of_point_records)) != extended_number_of_point_records) { #ifdef _WIN32 fprintf(stderr,"WARNING: number_of_point_records is %u. but extended_number_of_point_records is %I64u.\n", laszip_dll->header.number_of_point_records, extended_number_of_point_records); #else fprintf(stderr,"WARNING: number_of_point_records is %u. but extended_number_of_point_records is %llu.\n", laszip_dll->header.number_of_point_records, extended_number_of_point_records); #endif } laszip_dll->header.extended_number_of_point_records = extended_number_of_point_records; U64 extended_number_of_points_by_return; for (U32 r = 0; r < 15; r++) { in->get64bitsLE((U8*)&extended_number_of_points_by_return); if ((r < 5) && laszip_dll->header.number_of_points_by_return[r] != 0 && ((U64)(laszip_dll->header.number_of_points_by_return[r])) != extended_number_of_points_by_return) { #ifdef _WIN32 fprintf(stderr,"WARNING: number_of_points_by_return[%u] is %u. but extended_number_of_points_by_return[%u] is %I64u.\n", r, laszip_dll->header.number_of_points_by_return[r], r, extended_number_of_points_by_return); #else fprintf(stderr,"WARNING: number_of_points_by_return[%u] is %u. but extended_number_of_points_by_return[%u] is %llu.\n", r, laszip_dll->header.number_of_points_by_return[r], r, extended_number_of_points_by_return); #endif } laszip_dll->header.extended_number_of_points_by_return[r] = extended_number_of_points_by_return; } delete in; // remove the compatibility VLR if (laszip_remove_vlr(laszip_dll, "lascompatible\0\0", 22204)) { sprintf(laszip_dll->error, "removing the compatibility VLR"); return 1; } // remove the LAS 1.4 attributes from the "extra bytes" description if (laszip_dll->start_NIR_band != -1) attributer.remove_attribute("LAS 1.4 NIR band"); attributer.remove_attribute("LAS 1.4 flags and channel"); attributer.remove_attribute("LAS 1.4 classification"); attributer.remove_attribute("LAS 1.4 extended returns"); attributer.remove_attribute("LAS 1.4 scan angle"); // either rewrite or remove the "extra bytes" VLR if (attributer.number_attributes) { if (laszip_add_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4, (laszip_U16)(attributer.number_attributes*sizeof(LASattribute)), 0, (laszip_U8*)attributer.attributes)) { sprintf(laszip_dll->error, "rewriting the extra bytes VLR without 'LAS 1.4 compatibility mode' attributes"); return 1; } } else { if (laszip_remove_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4)) { sprintf(laszip_dll->error, "removing the LAS 1.4 attribute VLR"); return 1; } } // upgrade to LAS 1.4 if (laszip_dll->header.version_minor < 3) { // LAS 1.2 header is 148 bytes less than LAS 1.4+ header laszip_dll->header.header_size += 148; laszip_dll->header.offset_to_point_data += 148; } else { // LAS 1.3 header is 140 bytes less than LAS 1.4+ header laszip_dll->header.header_size += 140; laszip_dll->header.offset_to_point_data += 140; } laszip_dll->header.version_minor = 4; // maybe turn on the bit indicating the presence of the OGC WKT for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) { if ((strncmp(laszip_dll->header.vlrs[i].user_id, "LASF_Projection", 16) == 0) && (laszip_dll->header.vlrs[i].record_id == 2112)) { laszip_dll->header.global_encoding |= (1<<4); break; } } // update point type and size laszip_dll->point.extended_point_type = 1; if (laszip_dll->header.point_data_format == 1) { laszip_dll->header.point_data_format = 6; laszip_dll->header.point_data_record_length += (2 - 5); // record is 2 bytes larger but minus 5 extra bytes } else if (laszip_dll->header.point_data_format == 3) { if (laszip_dll->start_NIR_band == -1) { laszip_dll->header.point_data_format = 7; laszip_dll->header.point_data_record_length += (2 - 5); // record is 2 bytes larger but minus 5 extra bytes } else { laszip_dll->header.point_data_format = 8; laszip_dll->header.point_data_record_length += (4 - 7); // record is 4 bytes larger but minus 7 extra bytes } } else { if (laszip_dll->start_NIR_band == -1) { laszip_dll->header.point_data_format = 9; laszip_dll->header.point_data_record_length += (2 - 5); } else { laszip_dll->header.point_data_format = 10; laszip_dll->header.point_data_record_length += (4 - 7); } } // we are operating in compatibility mode laszip_dll->compatibility_mode = TRUE; } } } } else if (laszip_dll->header.point_data_format > 5) { laszip_dll->point.extended_point_type = 1; } // create the point reader laszip_dll->reader = new LASreadPoint(laszip_dll->las14_decompress_selective); if (laszip_dll->reader == 0) { sprintf(laszip_dll->error, "could not alloc LASreadPoint"); return 1; } if (!laszip_dll->reader->setup(laszip->num_items, laszip->items, laszip)) { sprintf(laszip_dll->error, "setup of LASreadPoint failed"); return 1; } if (!laszip_dll->reader->init(laszip_dll->streamin)) { sprintf(laszip_dll->error, "init of LASreadPoint failed"); return 1; } delete laszip; // set the point number and point count laszip_dll->npoints = (laszip_dll->header.number_of_point_records ? laszip_dll->header.number_of_point_records : laszip_dll->header.extended_number_of_point_records); laszip_dll->p_count = 0; laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_open_reader( laszip_POINTER pointer , const laszip_CHAR* file_name , laszip_BOOL* is_compressed ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (file_name == 0) { sprintf(laszip_dll->error, "laszip_CHAR pointer 'file_name' is zero"); return 1; } if (is_compressed == 0) { sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_compressed' is zero"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } // open the file #ifdef _MSC_VER wchar_t* utf16_file_name = UTF8toUTF16(file_name); laszip_dll->file = _wfopen(utf16_file_name, L"rb"); delete [] utf16_file_name; #else laszip_dll->file = fopen(file_name, "rb"); #endif if (laszip_dll->file == 0) { sprintf(laszip_dll->error, "cannot open file '%s'", file_name); return 1; } if (setvbuf(laszip_dll->file, NULL, _IOFBF, 262144) != 0) { sprintf(laszip_dll->warning, "setvbuf() failed with buffer size 262144\n"); } if (IS_LITTLE_ENDIAN()) laszip_dll->streamin = new ByteStreamInFileLE(laszip_dll->file); else laszip_dll->streamin = new ByteStreamInFileBE(laszip_dll->file); if (laszip_dll->streamin == 0) { sprintf(laszip_dll->error, "could not alloc ByteStreamInFile"); return 1; } // read the header variable after variable if (laszip_read_header(laszip_dll, is_compressed)) { return 1; } // should we try to exploit existing spatial indexing information if (laszip_dll->lax_exploit) { laszip_dll->lax_index = new LASindex(); if (!laszip_dll->lax_index->read(file_name)) { delete laszip_dll->lax_index; laszip_dll->lax_index = 0; } } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_open_reader"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_has_spatial_index( laszip_POINTER pointer , laszip_BOOL* is_indexed , laszip_BOOL* is_appended ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (is_indexed == 0) { sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_indexed' is zero"); return 1; } if (laszip_dll->reader == 0) { sprintf(laszip_dll->error, "reader is not open"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } if (laszip_dll->lax_exploit == 0) { sprintf(laszip_dll->error, "exploiting of spatial indexing not enabled before opening reader"); return 1; } // check if reader found spatial indexing information when opening file if (laszip_dll->lax_index) { *is_indexed = 1; } else { *is_indexed = 0; } // optional: inform whether spatial index is appended to LAZ file or in separate LAX file if (is_appended) { *is_appended = 0; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_have_spatial_index"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_inside_rectangle( laszip_POINTER pointer , const laszip_F64 r_min_x , const laszip_F64 r_min_y , const laszip_F64 r_max_x , const laszip_F64 r_max_y , laszip_BOOL* is_empty ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader == 0) { sprintf(laszip_dll->error, "reader is not open"); return 1; } if (is_empty == 0) { sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_empty' is zero"); return 1; } if (laszip_dll->lax_exploit == FALSE) { sprintf(laszip_dll->error, "exploiting of spatial indexing not enabled before opening reader"); return 1; } laszip_dll->lax_r_min_x = r_min_x; laszip_dll->lax_r_min_y = r_min_y; laszip_dll->lax_r_max_x = r_max_x; laszip_dll->lax_r_max_y = r_max_y; if (laszip_dll->lax_index) { if (laszip_dll->lax_index->intersect_rectangle(r_min_x, r_min_y, r_max_x, r_max_y)) { *is_empty = 0; } else { // no overlap between spatial indexing cells and query reactangle *is_empty = 1; } } else { if ((laszip_dll->header.min_x > r_max_x) || (laszip_dll->header.min_y > r_max_y) || (laszip_dll->header.max_x < r_min_x) || (laszip_dll->header.max_y < r_min_y)) { // no overlap between header bouding box and query reactangle *is_empty = 1; } else { *is_empty = 0; } } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_inside_rectangle"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_seek_point( laszip_POINTER pointer , laszip_I64 index ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { // seek to the point if (!laszip_dll->reader->seek((U32)laszip_dll->p_count, (U32)index)) { #ifdef _WIN32 sprintf(laszip_dll->error, "seeking from index %I64d to index %I64d for file with %I64d points", laszip_dll->p_count, index, laszip_dll->npoints); #else sprintf(laszip_dll->error, "seeking from index %lld to index %lld for file with %lld points", laszip_dll->p_count, index, laszip_dll->npoints); #endif return 1; } laszip_dll->p_count = index; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_seek_point"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_read_point( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { // read the point if (!laszip_dll->reader->read(laszip_dll->point_items)) { #ifdef _WIN32 sprintf(laszip_dll->error, "reading point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); #else sprintf(laszip_dll->error, "reading point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); #endif return 1; } // special recoding of points (in compatibility mode only) if (laszip_dll->compatibility_mode) { I16 scan_angle_remainder; U8 extended_returns; U8 classification; U8 flags_and_channel; I32 return_number_increment; I32 number_of_returns_increment; I32 overlap_bit; I32 scanner_channel; // instill extended attributes struct laszip_point* point = &laszip_dll->point; // get extended attributes from extra bytes scan_angle_remainder = *((I16*)(point->extra_bytes + laszip_dll->start_scan_angle)); extended_returns = point->extra_bytes[laszip_dll->start_extended_returns]; classification = point->extra_bytes[laszip_dll->start_classification]; flags_and_channel = point->extra_bytes[laszip_dll->start_flags_and_channel]; if (laszip_dll->start_NIR_band != -1) { point->rgb[3] = *((U16*)(point->extra_bytes + laszip_dll->start_NIR_band)); } // decompose into individual attributes return_number_increment = (extended_returns >> 4) & 0x0F; number_of_returns_increment = extended_returns & 0x0F; scanner_channel = (flags_and_channel >> 1) & 0x03; overlap_bit = flags_and_channel & 0x01; // instill into point point->extended_scan_angle = scan_angle_remainder + I16_QUANTIZE(((F32)point->scan_angle_rank) / 0.006f); point->extended_return_number = return_number_increment + point->return_number; point->extended_number_of_returns = number_of_returns_increment + point->number_of_returns; point->extended_classification = classification + point->classification; point->extended_scanner_channel = scanner_channel; point->extended_classification_flags = (overlap_bit << 3) | ((point->withheld_flag) << 2) | ((point->keypoint_flag) << 1) | (point->synthetic_flag); } laszip_dll->p_count++; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_read_point"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_read_inside_point( laszip_POINTER pointer , laszip_BOOL* is_done ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { laszip_F64 xy; *is_done = 1; if (laszip_dll->lax_index) { while (laszip_dll->lax_index->seek_next(laszip_dll->reader, laszip_dll->p_count)) { if (laszip_dll->reader->read(laszip_dll->point_items)) { laszip_dll->p_count++; xy = laszip_dll->header.x_scale_factor*laszip_dll->point.X+laszip_dll->header.x_offset; if (xy < laszip_dll->lax_r_min_x || xy >= laszip_dll->lax_r_max_x) continue; xy = laszip_dll->header.y_scale_factor*laszip_dll->point.Y+laszip_dll->header.y_offset; if (xy < laszip_dll->lax_r_min_y || xy >= laszip_dll->lax_r_max_y) continue; *is_done = 0; break; } } } else { while (laszip_dll->reader->read(laszip_dll->point_items)) { laszip_dll->p_count++; xy = laszip_dll->header.x_scale_factor*laszip_dll->point.X+laszip_dll->header.x_offset; if (xy < laszip_dll->lax_r_min_x || xy >= laszip_dll->lax_r_max_x) continue; xy = laszip_dll->header.y_scale_factor*laszip_dll->point.Y+laszip_dll->header.y_offset; if (xy < laszip_dll->lax_r_min_y || xy >= laszip_dll->lax_r_max_y) continue; *is_done = 0; break; } if (*is_done) { if (laszip_dll->p_count < laszip_dll->npoints) { #ifdef _WIN32 sprintf(laszip_dll->error, "reading point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); #else sprintf(laszip_dll->error, "reading point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); #endif return 1; } } } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_read_inside_point"); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_close_reader( laszip_POINTER pointer ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->reader == 0) { sprintf(laszip_dll->error, "closing reader before it was opened"); return 1; } if (!laszip_dll->reader->done()) { sprintf(laszip_dll->error, "done of LASreadPoint failed"); return 1; } delete laszip_dll->reader; laszip_dll->reader = 0; delete [] laszip_dll->point_items; laszip_dll->point_items = 0; delete laszip_dll->streamin; laszip_dll->streamin = 0; if (laszip_dll->lax_index) { delete laszip_dll->lax_index; laszip_dll->lax_index = 0; } if (laszip_dll->file) { fclose(laszip_dll->file); laszip_dll->file = 0; } } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_close_reader"); return 1; } laszip_dll->error[0] = '\0'; return 0; } #ifdef __cplusplus /*---------------------------------------------------------------------------*/ LASZIP_API laszip_I32 laszip_open_reader_stream( laszip_POINTER pointer , istream& stream , laszip_BOOL* is_compressed ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (is_compressed == 0) { sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_compressed' is zero"); return 1; } if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } // open the file if (IS_LITTLE_ENDIAN()) laszip_dll->streamin = new ByteStreamInIstreamLE(stream); else laszip_dll->streamin = new ByteStreamInIstreamBE(stream); if (laszip_dll->streamin == 0) { sprintf(laszip_dll->error, "could not alloc ByteStreamInIstream"); return 1; } return laszip_read_header(laszip_dll, is_compressed); } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_open_reader"); return 1; } } /*---------------------------------------------------------------------------*/ // The stream writer also supports software that writes the LAS header on its // own simply by setting the BOOL 'do_not_write_header' to TRUE. This function // should then be called just prior to writing points as data is then written // to the current stream position LASZIP_API laszip_I32 laszip_open_writer_stream( laszip_POINTER pointer , ostream& stream , laszip_BOOL compress , laszip_BOOL do_not_write_header ) { if (pointer == 0) return 1; laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; try { if (laszip_dll->writer) { sprintf(laszip_dll->error, "writer is already open"); return 1; } if (laszip_dll->reader) { sprintf(laszip_dll->error, "reader is already open"); return 1; } // create the outstream if (IS_LITTLE_ENDIAN()) laszip_dll->streamout = new ByteStreamOutOstreamLE(stream); else laszip_dll->streamout = new ByteStreamOutOstreamBE(stream); if (laszip_dll->streamout == 0) { sprintf(laszip_dll->error, "could not alloc ByteStreamOutOstream"); return 1; } // setup the items that make up the point LASzip laszip; if (setup_laszip_items(laszip_dll, &laszip, compress)) { return 1; } // this supports software that writes the LAS header on its own if (do_not_write_header == FALSE) { // prepare header if (laszip_prepare_header_for_write(laszip_dll)) { return 1; } // prepare point if (laszip_prepare_point_for_write(laszip_dll, compress)) { return 1; } // prepare VLRs if (laszip_prepare_vlrs_for_write(laszip_dll)) { return 1; } // write header variable after variable if (laszip_write_header(laszip_dll, &laszip, compress)) { return 1; } } // create the point writer if (create_point_writer(laszip_dll, &laszip)) { return 1; } // set the point number and point count laszip_dll->npoints = (laszip_dll->header.number_of_point_records ? laszip_dll->header.number_of_point_records : laszip_dll->header.extended_number_of_point_records); laszip_dll->p_count = 0; } catch (...) { sprintf(laszip_dll->error, "internal error in laszip_open_writer_stream."); return 1; } laszip_dll->error[0] = '\0'; return 0; } /*---------------------------------------------------------------------------*/ // creates complete LASzip VLR for currently selected point type and compression // The VLR data is valid until the laszip_dll pointer is destroyed. LASZIP_API laszip_I32 laszip_create_laszip_vlr( laszip_POINTER pointer , laszip_U8** vlr , laszip_U32* vlr_size ) { if (pointer == 0) return 1; laszip_dll_struct *laszip_dll = (laszip_dll_struct *)pointer; LASzip laszip; if (setup_laszip_items(laszip_dll, &laszip, TRUE)) { return 1; } ByteStreamOutArray* out = 0; if (IS_LITTLE_ENDIAN()) out = new ByteStreamOutArrayLE(); else out = new ByteStreamOutArrayBE(); if (out == 0) { sprintf(laszip_dll->error, "could not alloc ByteStreamOutArray"); return 1; } if (write_laszip_vlr_header(laszip_dll, &laszip, out)) { return 1; } if (write_laszip_vlr_payload(laszip_dll, &laszip, out)) { return 1; } *vlr = (laszip_U8*)malloc(out->getSize()); *vlr_size = (U32)out->getSize(); laszip_dll->buffers.push_back(*vlr); memcpy(*vlr, out->getData(), out->getSize()); delete out; laszip_dll->error[0] = '\0'; return 0; } #endif // __cplusplus