예제 #1
0
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;
}
예제 #2
0
 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);
 };
예제 #3
0
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;
}
예제 #4
0
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;
}