BOOL LASwriterBIN::write_point(const LASpoint* point) { U16 echo; if (point->number_of_returns <= 1) echo = 0; else if (point->return_number == 1) echo = 1; else if (point->return_number >= point->number_of_returns) echo = 3; else echo = 2; if (version == 20020715) { TSpoint tspoint; tspoint.x = I32_QUANTIZE(point->get_x()*units+origin_x); tspoint.y = I32_QUANTIZE(point->get_y()*units+origin_y); tspoint.z = I32_QUANTIZE(point->get_z()*units+origin_z); tspoint.code = point->classification; tspoint.echo = (U8)echo; tspoint.flag = 0; tspoint.mark = 0; tspoint.line = point->point_source_ID; tspoint.intensity = point->intensity; if (!stream->putBytes((U8*)&tspoint, sizeof(TSpoint))) return FALSE; } else { TSrow tsrow; tsrow.code = point->classification; tsrow.line = (U8)(point->point_source_ID); tsrow.echo_intensity = (echo << 14) | (point->intensity & 0x3FFF); tsrow.x = I32_QUANTIZE(point->get_x()*units+origin_x); tsrow.y = I32_QUANTIZE(point->get_y()*units+origin_y); tsrow.z = I32_QUANTIZE(point->get_z()*units+origin_z); if (!stream->putBytes((U8*)&tsrow, sizeof(TSrow))) return FALSE; } if (point->have_gps_time) { U32 time = (U32)(point->gps_time/0.0002+0.5); if (!stream->putBytes((U8*)&time, sizeof(U32))) return FALSE; } if (point->have_rgb) { U8 rgba[4]; rgba[0] = point->rgb[0]/256; rgba[1] = point->rgb[1]/256; rgba[2] = point->rgb[2]/256; rgba[3] = 0; if (!stream->putBytes((U8*)&rgba, sizeof(U32))) return FALSE; } p_count++; return TRUE; }
void set_bounding_box(F64 min_x, F64 min_y, F64 min_z, F64 max_x, F64 max_y, F64 max_z, BOOL auto_scale=TRUE, BOOL auto_offset=TRUE) { if (auto_scale) { if (-360 < min_x && -360 < min_y && max_x < 360 && max_y < 360) { x_scale_factor = 0.0000001; y_scale_factor = 0.0000001; } else { x_scale_factor = 0.01; y_scale_factor = 0.01; } z_scale_factor = 0.01; } if (auto_offset) { if (-360 < min_x && -360 < min_y && max_x < 360 && max_y < 360) { x_offset = 0; y_offset = 0; z_offset = 0; } else { x_offset = ((I32)((min_x + max_x)/200000))*100000; y_offset = ((I32)((min_y + max_y)/200000))*100000; z_offset = ((I32)((min_z + max_z)/200000))*100000; } } this->min_x = x_offset + x_scale_factor*I32_QUANTIZE((min_x-x_offset)/x_scale_factor); this->min_y = y_offset + y_scale_factor*I32_QUANTIZE((min_y-y_offset)/y_scale_factor); this->min_z = z_offset + z_scale_factor*I32_QUANTIZE((min_z-z_offset)/z_scale_factor); this->max_x = x_offset + x_scale_factor*I32_QUANTIZE((max_x-x_offset)/x_scale_factor); this->max_y = y_offset + y_scale_factor*I32_QUANTIZE((max_y-y_offset)/y_scale_factor); this->max_z = z_offset + z_scale_factor*I32_QUANTIZE((max_z-z_offset)/z_scale_factor); };
BOOL PULSEreaderGCW::open() { F64 xyz; // clean the header header.clean(); // set some parameters memset(header.system_identifier, 0, PULSEWAVES_DESCRIPTION_SIZE); memset(header.generating_software, 0, PULSEWAVES_DESCRIPTION_SIZE); sprintf(header.system_identifier, "created by PULSEreaderGCW"); sprintf(header.generating_software, "PulseWaves %d.%d r%d (%d) by rapidlasso", PULSEWAVES_VERSION_MAJOR, PULSEWAVES_VERSION_MINOR, PULSEWAVES_REVISION, PULSEWAVES_BUILD_DATE); header.file_creation_day = 333; header.file_creation_year = 2012; // read the first pulse if (!read_pulse_lgc()) { fprintf(stderr,"ERROR: reading first pulse\n"); return FALSE; } // use first pulse to determine some settings BOOL long_lat_coordinates = valid_lonlat(pulselgc.e0, pulselgc.n0); if (long_lat_coordinates) { header.x_scale_factor = 1e-7; header.y_scale_factor = 1e-7; header.x_offset = I32_QUANTIZE(pulselgc.e0/10)*10; header.y_offset = I32_QUANTIZE(pulselgc.n0/10)*10; } else { header.x_scale_factor = 0.01; header.y_scale_factor = 0.01; header.x_offset = I32_QUANTIZE(pulselgc.e0/100000)*100000; header.y_offset = I32_QUANTIZE(pulselgc.n0/100000)*100000; } header.z_scale_factor = 0.01; header.z_offset = 0; pulse.last_returning_sample = pulselgc.wfoffset + pulselgc.wflen - 1; header.min_x = header.max_x = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset); header.min_y = header.max_y = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset); header.min_z = header.max_z = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset); xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; // seek to the last pulse in the file try { pulse_stream->seekEnd(56); } catch(...) { fprintf(stderr,"ERROR: cannot seek to end of file\n"); return FALSE; } I64 file_size = pulse_stream->tell(); if ( (file_size % 56) != 0 ) { fprintf(stderr,"WARNING: odd file size. with data records of %d bytes there is a remainder of %d bytes\n", 56, (I32)(file_size % 56)); } header.number_of_pulses = npulses = (file_size / 56) + 1; p_count = 0; // use last pulse to update bounding box approximation if (!read_pulse_lgc()) { fprintf(stderr,"ERROR: reading last pulse\n"); return FALSE; } xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; // load a few more pulses to update bounding box approximation try { pulse_stream->seek(npulses/4*1*56); } catch(...) { fprintf(stderr,"ERROR: seeking to intermediate pulse\n"); return FALSE; } if (!read_pulse_lgc()) { fprintf(stderr,"ERROR: reading intermediate pulse\n"); return FALSE; } xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; try { pulse_stream->seek(npulses/4*2*56); } catch(...) { fprintf(stderr,"ERROR: seeking to intermediate pulse\n"); return FALSE; } if (!read_pulse_lgc()) { fprintf(stderr,"ERROR: reading intermediate pulse\n"); return FALSE; } xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; try { pulse_stream->seek(npulses/4*3*56); } catch(...) { fprintf(stderr,"ERROR: seeking to intermediate pulse\n"); return FALSE; } if (!read_pulse_lgc()) { fprintf(stderr,"ERROR: reading intermediate pulse\n"); return FALSE; } xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; xyz = pulselgc.e0 + ((F64)pulselgc.de)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_x > xyz) header.min_x = xyz; else if (header.max_x < xyz) header.max_x = xyz; xyz = pulselgc.n0 + ((F64)pulselgc.dn)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_y > xyz) header.min_y = xyz; else if (header.max_y < xyz) header.max_y = xyz; xyz = pulselgc.h0 + ((F64)pulselgc.dh)*(pulselgc.wfoffset + pulselgc.wflen - 1); if (header.min_z > xyz) header.min_z = xyz; else if (header.max_z < xyz) header.max_z = xyz; // go back to the beginning of the stream try { pulse_stream->seek(0); } catch(...) { fprintf(stderr,"ERROR: cannot seek back to beginning\n"); return FALSE; } if (long_lat_coordinates) { // create Projection PULSEkeyentry key_entries[4]; // projected coordinates key_entries[0].key_id = 1024; // GTModelTypeGeoKey key_entries[0].tiff_tag_location = 0; key_entries[0].count = 1; key_entries[0].value_offset = 2; // ModelTypeGeographic // ellipsoid used with latitude/longitude coordinates key_entries[1].key_id = 2048; // GeographicTypeGeoKey key_entries[1].tiff_tag_location = 0; key_entries[1].count = 1; key_entries[1].value_offset = 4326; // WGS84 // vertical units key_entries[2].key_id = 4099; // VerticalUnitsGeoKey key_entries[2].tiff_tag_location = 0; key_entries[2].count = 1; key_entries[2].value_offset = 9001; // vertical datum key_entries[3].key_id = 4096; // VerticalCSTypeGeoKey key_entries[3].tiff_tag_location = 0; key_entries[3].count = 1; key_entries[3].value_offset = 5030; // WGS84 header.set_geokey_entries(4, key_entries); } // create scanner PULSEscanner scanner; scanner.wave_length = 1064; // [nanometer] scanner.outgoing_pulse_width = 10; // [nanoseconds] scanner.beam_diameter_at_exit_aperture = 0; // [millimeters] scanner.beam_divergence = 0; // [milliradians] header.add_scanner(&scanner, 1); // create pulse descriptors (composition + samplings) PULSEcomposition composition; PULSEsampling samplings[2]; composition.optical_center_to_anchor_point = PULSEWAVES_OPTICAL_CENTER_AND_ANCHOR_POINT_COINCIDE; // the duration from the optical center to the anchor point is zero composition.number_of_extra_waves_bytes = 0; composition.number_of_samplings = 2; // one outgoing, one returning composition.sample_units = 1.0f; // [nanoseconds] composition.scanner_index = 1; memset(composition.description, 0, PULSEWAVES_DESCRIPTION_SIZE); strncpy(composition.description, "GeoLas GCW pulse (8 bit)", PULSEWAVES_DESCRIPTION_SIZE); samplings[0].type = PULSEWAVES_OUTGOING; samplings[0].channel = 0; samplings[0].bits_for_duration_from_anchor = 0; // the outgoing waveform segment starts at time zero at the anchor samplings[0].bits_for_number_of_segments = 0; // the number of segments is fixed (i.e. there is just one) samplings[0].bits_for_number_of_samples = 16; // the number of samples per segment is specified per segment with 16 bits samplings[0].number_of_segments = 1; // the number of segments per sampling is always 1 samplings[0].number_of_samples = 0; // the number of samples per segment varies samplings[0].bits_per_sample = 8; // the number of bits per sample is always 8 samplings[0].lookup_table_index = PULSEWAVES_UNDEFINED; // the index to the optional lookup table translating sample values to physical measurements samplings[0].sample_units = 1.0f; // [nanoseconds] samplings[0].compression = PULSEWAVES_UNCOMPRESSED; // the samples are stored without compression strncpy(samplings[0].description, "outgoing at 8 bits", PULSEWAVES_DESCRIPTION_SIZE); samplings[1].type = PULSEWAVES_RETURNING; samplings[1].channel = 0; samplings[1].bits_for_duration_from_anchor = 16; // the start of each waveform segment is specified with 16 bits (in sampling units) samplings[1].scale_for_duration_from_anchor = 1.0f; // the duration is specified in sampling unit increments (without fractions) samplings[1].offset_for_duration_from_anchor = 0.0f; // the duration is specified with zero offset samplings[1].bits_for_number_of_segments = 0; // the number of segments is fixed (i.e. there is just one) samplings[1].bits_for_number_of_samples = 16; // the number of samples per segment is specified per segment with 16 bits samplings[1].number_of_segments = 1; // the number of segments per sampling is always 1 samplings[1].number_of_samples = 0; // the number of samples per segment varies samplings[1].bits_per_sample = 8; // the number of bits per sample is always 8 samplings[1].lookup_table_index = PULSEWAVES_UNDEFINED; // the index to the optional lookup table translating sample values to physical measurements samplings[1].sample_units = 1.0f; // [nanoseconds] samplings[1].compression = PULSEWAVES_UNCOMPRESSED; // the samples are stored without compression strncpy(samplings[1].description, "returning at 8 bits", PULSEWAVES_DESCRIPTION_SIZE); header.add_descriptor(&composition, samplings, GCW_PULSE_DESCRIPTOR_INDEX_8_BIT, TRUE); memset(composition.description, 0, PULSEWAVES_DESCRIPTION_SIZE); strncpy(composition.description, "GeoLas GCW pulse (16 bit)", PULSEWAVES_DESCRIPTION_SIZE); samplings[0].type = PULSEWAVES_OUTGOING; samplings[0].channel = 0; samplings[0].bits_for_duration_from_anchor = 0; // the outgoing waveform segment starts at time zero at the anchor samplings[0].bits_for_number_of_segments = 0; // the number of segments is fixed (i.e. there is just one) samplings[0].bits_for_number_of_samples = 16; // the number of samples per segment is specified per segment with 16 bits samplings[0].number_of_segments = 1; // the number of segments per sampling is always 1 samplings[0].number_of_samples = 0; // the number of samples per segment varies samplings[0].bits_per_sample = 16; // the number of bits per sample is always 16 samplings[0].lookup_table_index = PULSEWAVES_UNDEFINED; // the index to the optional lookup table translating sample values to physical measurements samplings[0].sample_units = 1.0f; // [nanoseconds] samplings[0].compression = PULSEWAVES_UNCOMPRESSED; // the samples are stored without compression strncpy(samplings[0].description, "outgoing at 16 bits", PULSEWAVES_DESCRIPTION_SIZE); samplings[1].type = PULSEWAVES_RETURNING; samplings[1].channel = 0; samplings[1].bits_for_duration_from_anchor = 16; // the start of each waveform segment is specified with 16 bits (in sampling units) samplings[1].scale_for_duration_from_anchor = 1.0f; // the duration is specified in sampling unit increments (without fractions) samplings[1].offset_for_duration_from_anchor = 0.0f; // the duration is specified with zero offset samplings[1].bits_for_number_of_segments = 0; // the number of segments is fixed (i.e. there is just one) samplings[1].bits_for_number_of_samples = 16; // the number of samples per segment is specified per segment with 16 bits samplings[1].number_of_segments = 1; // the number of segments per sampling is always 1 samplings[1].number_of_samples = 0; // the number of samples per segment varies samplings[1].bits_per_sample = 16; // the number of bits per sample is always 16 samplings[1].lookup_table_index = PULSEWAVES_UNDEFINED; // the index to the optional lookup table translating sample values to physical measurements samplings[1].sample_units = 1.0f; // [nanoseconds] samplings[1].compression = PULSEWAVES_UNCOMPRESSED; // the samples are stored without compression strncpy(samplings[1].description, "returning at 16 bits", PULSEWAVES_DESCRIPTION_SIZE); header.add_descriptor(&composition, samplings, GCW_PULSE_DESCRIPTOR_INDEX_16_BIT, TRUE); pulse.init(&header); header_is_populated = FALSE; return TRUE; }
int32_t laszip_check_for_integer_overflow( laszip_dll_struct *laszip_dll ) { if (laszip_dll == 0) return 1; 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; }