QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgseptdecoder.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspointcloudrenderer.cpp
3  --------------------
4  begin : October 2020
5  copyright : (C) 2020 by Peter Petrik
6  email : zilolv at gmail dot com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgseptdecoder.h"
19 #include "qgseptpointcloudindex.h"
20 #include "qgspointcloudattribute.h"
21 #include "qgsvector3d.h"
22 #include "qgsconfig.h"
23 #include "qgslogger.h"
24 
25 #include <QFile>
26 #include <QDir>
27 #include <iostream>
28 #include <memory>
29 #include <cstring>
30 #include <QTemporaryFile>
31 #include <string>
32 
33 #include <zstd.h>
34 
35 
37 
38 template <typename T>
39 bool _storeToStream( char *s, size_t position, QgsPointCloudAttribute::DataType type, T value )
40 {
41  switch ( type )
42  {
44  {
45  const char val = char( value );
46  s[position] = val;
47  break;
48  }
50  {
51  const unsigned char val = ( unsigned char )( value );
52  s[position] = val;
53  break;
54  }
55 
57  {
58  short val = short( value );
59  memcpy( s + position, reinterpret_cast<char * >( &val ), sizeof( short ) );
60  break;
61  }
63  {
64  unsigned short val = static_cast< unsigned short>( value );
65  memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( unsigned short ) );
66  break;
67  }
68 
70  {
71  qint32 val = qint32( value );
72  memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( qint32 ) );
73  break;
74  }
76  {
77  quint32 val = quint32( value );
78  memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( quint32 ) );
79  break;
80  }
81 
83  {
84  qint64 val = qint64( value );
85  memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( qint64 ) );
86  break;
87  }
89  {
90  quint64 val = quint64( value );
91  memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( quint64 ) );
92  break;
93  }
94 
96  {
97  float val = float( value );
98  memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( float ) );
99  break;
100  }
102  {
103  double val = double( value );
104  memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( double ) );
105  break;
106  }
107  }
108 
109  return true;
110 }
111 
112 bool __serialize( char *data, size_t outputPosition, QgsPointCloudAttribute::DataType outputType,
113  const char *input, QgsPointCloudAttribute::DataType inputType, int inputSize, size_t inputPosition )
114 {
115  if ( outputType == inputType )
116  {
117  memcpy( data + outputPosition, input + inputPosition, inputSize );
118  return true;
119  }
120 
121  switch ( inputType )
122  {
124  {
125  const char val = *( input + inputPosition );
126  return _storeToStream<char>( data, outputPosition, outputType, val );
127  }
129  {
130  const unsigned char val = *( input + inputPosition );
131  return _storeToStream<unsigned char>( data, outputPosition, outputType, val );
132  }
134  {
135  const short val = *reinterpret_cast< const short * >( input + inputPosition );
136  return _storeToStream<short>( data, outputPosition, outputType, val );
137  }
139  {
140  const unsigned short val = *reinterpret_cast< const unsigned short * >( input + inputPosition );
141  return _storeToStream<unsigned short>( data, outputPosition, outputType, val );
142  }
144  {
145  const qint32 val = *reinterpret_cast<const qint32 * >( input + inputPosition );
146  return _storeToStream<qint32>( data, outputPosition, outputType, val );
147  }
149  {
150  const quint32 val = *reinterpret_cast<const quint32 * >( input + inputPosition );
151  return _storeToStream<quint32>( data, outputPosition, outputType, val );
152  }
154  {
155  const qint64 val = *reinterpret_cast<const qint64 * >( input + inputPosition );
156  return _storeToStream<qint64>( data, outputPosition, outputType, val );
157  }
159  {
160  const quint64 val = *reinterpret_cast<const quint64 * >( input + inputPosition );
161  return _storeToStream<quint64>( data, outputPosition, outputType, val );
162  }
164  {
165  const float val = *reinterpret_cast< const float * >( input + inputPosition );
166  return _storeToStream<float>( data, outputPosition, outputType, val );
167  }
169  {
170  const double val = *reinterpret_cast< const double * >( input + inputPosition );
171  return _storeToStream<double>( data, outputPosition, outputType, val );
172  }
173  }
174  return true;
175 }
176 
177 // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
178 
179 QgsPointCloudBlock *_decompressBinary( const QByteArray &dataUncompressed, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset )
180 {
181  const std::size_t pointRecordSize = attributes.pointRecordSize( );
182  const std::size_t requestedPointRecordSize = requestedAttributes.pointRecordSize();
183  const int count = dataUncompressed.size() / pointRecordSize;
184  QByteArray data;
185  data.resize( requestedPointRecordSize * count );
186  char *destinationBuffer = data.data();
187  const char *s = dataUncompressed.data();
188 
189  const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.attributes();
190 
191  // calculate input attributes and offsets once in advance
192 
193  struct AttributeData
194  {
195  AttributeData( int inputOffset, int inputSize, QgsPointCloudAttribute::DataType inputType, int requestedSize, QgsPointCloudAttribute::DataType requestedType )
196  : inputOffset( inputOffset )
197  , inputSize( inputSize )
198  , inputType( inputType )
199  , requestedSize( requestedSize )
200  , requestedType( requestedType )
201  {}
202 
203  int inputOffset;
204  int inputSize;
206  int requestedSize;
207  QgsPointCloudAttribute::DataType requestedType;
208  };
209 
210  std::vector< AttributeData > attributeData;
211  attributeData.reserve( requestedAttributesVector.size() );
212  for ( const QgsPointCloudAttribute &requestedAttribute : requestedAttributesVector )
213  {
214  int inputAttributeOffset;
215  const QgsPointCloudAttribute *inputAttribute = attributes.find( requestedAttribute.name(), inputAttributeOffset );
216  if ( !inputAttribute )
217  {
218  return nullptr;
219  }
220  attributeData.emplace_back( AttributeData( inputAttributeOffset, inputAttribute->size(), inputAttribute->type(),
221  requestedAttribute.size(), requestedAttribute.type() ) );
222  }
223 
224  // now loop through points
225  size_t outputOffset = 0;
226  for ( int i = 0; i < count; ++i )
227  {
228  for ( const AttributeData &attribute : attributeData )
229  {
230  __serialize( destinationBuffer, outputOffset,
231  attribute.requestedType, s,
232  attribute.inputType, attribute.inputSize, i * pointRecordSize + attribute.inputOffset );
233 
234  outputOffset += attribute.requestedSize;
235  }
236  }
237  return new QgsPointCloudBlock(
238  count,
239  requestedAttributes,
240  data, scale, offset
241  );
242 }
243 
244 QgsPointCloudBlock *QgsEptDecoder::decompressBinary( const QString &filename, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset )
245 {
246  if ( ! QFile::exists( filename ) )
247  return nullptr;
248 
249  QFile f( filename );
250  const bool r = f.open( QIODevice::ReadOnly );
251  if ( !r )
252  return nullptr;
253 
254  const QByteArray dataUncompressed = f.read( f.size() );
255  return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset );
256 }
257 
258 QgsPointCloudBlock *QgsEptDecoder::decompressBinary( const QByteArray &data, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset )
259 {
260  return _decompressBinary( data, attributes, requestedAttributes, scale, offset );
261 }
262 
263 /* *************************************************************************************** */
264 
265 QByteArray decompressZtdStream( const QByteArray &dataCompressed )
266 {
267  // NOTE: this is very primitive implementation because we expect the uncompressed
268  // data will be always less than 10 MB
269 
270  const int MAXSIZE = 10000000;
271  QByteArray dataUncompressed;
272  dataUncompressed.resize( MAXSIZE );
273 
274  ZSTD_DStream *strm = ZSTD_createDStream();
275  ZSTD_initDStream( strm );
276 
277  ZSTD_inBuffer m_inBuf;
278  m_inBuf.src = reinterpret_cast<const void *>( dataCompressed.constData() );
279  m_inBuf.size = dataCompressed.size();
280  m_inBuf.pos = 0;
281 
282  ZSTD_outBuffer outBuf { reinterpret_cast<void *>( dataUncompressed.data() ), MAXSIZE, 0 };
283  const size_t ret = ZSTD_decompressStream( strm, &outBuf, &m_inBuf );
284  Q_ASSERT( !ZSTD_isError( ret ) );
285  Q_ASSERT( outBuf.pos );
286  Q_ASSERT( outBuf.pos < outBuf.size );
287 
288  ZSTD_freeDStream( strm );
289  dataUncompressed.resize( outBuf.pos );
290  return dataUncompressed;
291 }
292 
293 QgsPointCloudBlock *QgsEptDecoder::decompressZStandard( const QString &filename, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset )
294 {
295  if ( ! QFile::exists( filename ) )
296  return nullptr;
297 
298  QFile f( filename );
299  const bool r = f.open( QIODevice::ReadOnly );
300  if ( !r )
301  return nullptr;
302 
303  const QByteArray dataCompressed = f.readAll();
304  const QByteArray dataUncompressed = decompressZtdStream( dataCompressed );
305  return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset );
306 }
307 
308 QgsPointCloudBlock *QgsEptDecoder::decompressZStandard( const QByteArray &data, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset )
309 {
310  const QByteArray dataUncompressed = decompressZtdStream( data );
311  return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset );
312 }
313 
314 /* *************************************************************************************** */
315 
316 
317 template<typename FileType>
318 QgsPointCloudBlock *__decompressLaz( FileType &file, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &_scale, const QgsVector3D &_offset )
319 {
320  Q_UNUSED( attributes );
321  Q_UNUSED( _scale );
322  Q_UNUSED( _offset );
323 
324  if ( ! file.good() )
325  return nullptr;
326 
327 #ifdef QGISDEBUG
328  const auto start = common::tick();
329 #endif
330 
331  laszip::io::reader::basic_file<FileType> f( file );
332 
333  const size_t count = f.get_header().point_count;
334  const QgsVector3D scale( f.get_header().scale.x, f.get_header().scale.y, f.get_header().scale.z );
335  const QgsVector3D offset( f.get_header().offset.x, f.get_header().offset.y, f.get_header().offset.z );
336 
337  QByteArray bufArray( f.get_header().point_record_length, 0 );
338  char *buf = bufArray.data();
339 
340  const size_t requestedPointRecordSize = requestedAttributes.pointRecordSize();
341  QByteArray data;
342  data.resize( requestedPointRecordSize * count );
343  char *dataBuffer = data.data();
344 
345  const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.attributes();
346 
347  std::size_t outputOffset = 0;
348 
349  enum class LazAttribute
350  {
351  X,
352  Y,
353  Z,
354  Classification,
355  Intensity,
356  ReturnNumber,
357  NumberOfReturns,
358  ScanDirectionFlag,
359  EdgeOfFlightLine,
360  ScanAngleRank,
361  UserData,
362  PointSourceId,
363  GpsTime,
364  Red,
365  Green,
366  Blue,
367  ExtraBytes,
368  MissingOrUnknown
369  };
370 
371  struct RequestedAttributeDetails
372  {
373  RequestedAttributeDetails( LazAttribute attribute, QgsPointCloudAttribute::DataType type, int size, int offset = -1 )
374  : attribute( attribute )
375  , type( type )
376  , size( size )
377  , offset( offset )
378  {}
379 
380  LazAttribute attribute;
382  int size;
383  int offset; // Used in case the attribute is an extra byte attribute
384  };
385 
386  QVector<QgsEptDecoder::ExtraBytesAttributeDetails> extrabytesAttr = QgsEptDecoder::readExtraByteAttributes<FileType>( file );
387 
388  std::vector< RequestedAttributeDetails > requestedAttributeDetails;
389  requestedAttributeDetails.reserve( requestedAttributesVector.size() );
390  for ( const QgsPointCloudAttribute &requestedAttribute : requestedAttributesVector )
391  {
392  if ( requestedAttribute.name().compare( QLatin1String( "X" ), Qt::CaseInsensitive ) == 0 )
393  {
394  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
395  }
396  else if ( requestedAttribute.name().compare( QLatin1String( "Y" ), Qt::CaseInsensitive ) == 0 )
397  {
398  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
399  }
400  else if ( requestedAttribute.name().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) == 0 )
401  {
402  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
403  }
404  else if ( requestedAttribute.name().compare( QLatin1String( "Classification" ), Qt::CaseInsensitive ) == 0 )
405  {
406  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
407  }
408  else if ( requestedAttribute.name().compare( QLatin1String( "Intensity" ), Qt::CaseInsensitive ) == 0 )
409  {
410  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
411  }
412  else if ( requestedAttribute.name().compare( QLatin1String( "ReturnNumber" ), Qt::CaseInsensitive ) == 0 )
413  {
414  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
415  }
416  else if ( requestedAttribute.name().compare( QLatin1String( "NumberOfReturns" ), Qt::CaseInsensitive ) == 0 )
417  {
418  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
419  }
420  else if ( requestedAttribute.name().compare( QLatin1String( "ScanDirectionFlag" ), Qt::CaseInsensitive ) == 0 )
421  {
422  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
423  }
424  else if ( requestedAttribute.name().compare( QLatin1String( "EdgeOfFlightLine" ), Qt::CaseInsensitive ) == 0 )
425  {
426  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
427  }
428  else if ( requestedAttribute.name().compare( QLatin1String( "ScanAngleRank" ), Qt::CaseInsensitive ) == 0 )
429  {
430  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
431  }
432  else if ( requestedAttribute.name().compare( QLatin1String( "UserData" ), Qt::CaseInsensitive ) == 0 )
433  {
434  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
435  }
436  else if ( requestedAttribute.name().compare( QLatin1String( "PointSourceId" ), Qt::CaseInsensitive ) == 0 )
437  {
438  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
439  }
440  else if ( requestedAttribute.name().compare( QLatin1String( "GpsTime" ), Qt::CaseInsensitive ) == 0 )
441  {
442  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::GpsTime, requestedAttribute.type(), requestedAttribute.size() ) );
443  }
444  else if ( requestedAttribute.name().compare( QLatin1String( "Red" ), Qt::CaseInsensitive ) == 0 )
445  {
446  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
447  }
448  else if ( requestedAttribute.name().compare( QLatin1String( "Green" ), Qt::CaseInsensitive ) == 0 )
449  {
450  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
451  }
452  else if ( requestedAttribute.name().compare( QLatin1String( "Blue" ), Qt::CaseInsensitive ) == 0 )
453  {
454  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
455  }
456  else
457  {
458  bool foundAttr = false;
459  for ( QgsEptDecoder::ExtraBytesAttributeDetails &eba : extrabytesAttr )
460  {
461  if ( requestedAttribute.name().compare( eba.attribute.trimmed() ) == 0 )
462  {
463  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ExtraBytes, eba.type, eba.size, eba.offset ) );
464  foundAttr = true;
465  break;
466  }
467  }
468  if ( !foundAttr )
469  {
470  // this can possibly happen -- e.g. if a style built using a different point cloud format references an attribute which isn't available from the laz file
471  requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
472  }
473  }
474  }
475 
476  for ( size_t i = 0 ; i < count ; i ++ )
477  {
478  f.readPoint( buf ); // read the point out
479  const laszip::formats::las::point10 p = laszip::formats::packers<laszip::formats::las::point10>::unpack( buf );
480  const laszip::formats::las::gpstime gps = laszip::formats::packers<laszip::formats::las::gpstime>::unpack( buf + sizeof( laszip::formats::las::point10 ) );
481  const laszip::formats::las::rgb rgb = laszip::formats::packers<laszip::formats::las::rgb>::unpack( buf + sizeof( laszip::formats::las::point10 ) + sizeof( laszip::formats::las::gpstime ) );
482 
483  for ( const RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
484  {
485  switch ( requestedAttribute.attribute )
486  {
487  case LazAttribute::X:
488  _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.x );
489  break;
490  case LazAttribute::Y:
491  _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.y );
492  break;
493  case LazAttribute::Z:
494  _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.z );
495  break;
496  case LazAttribute::Classification:
497  _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.classification );
498  break;
499  case LazAttribute::Intensity:
500  _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, p.intensity );
501  break;
502  case LazAttribute::ReturnNumber:
503  _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.return_number );
504  break;
505  case LazAttribute::NumberOfReturns:
506  _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.number_of_returns_of_given_pulse );
507  break;
508  case LazAttribute::ScanDirectionFlag:
509  _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.scan_direction_flag );
510  break;
511  case LazAttribute::EdgeOfFlightLine:
512  _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.edge_of_flight_line );
513  break;
514  case LazAttribute::ScanAngleRank:
515  _storeToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, p.scan_angle_rank );
516  break;
517  case LazAttribute::UserData:
518  _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.user_data );
519  break;
520  case LazAttribute::PointSourceId:
521  _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, p.point_source_ID );
522  break;
523  case LazAttribute::GpsTime:
524  // lazperf internally stores gps value as int64 field, but in fact it is a double value
525  _storeToStream<double>( dataBuffer, outputOffset, requestedAttribute.type,
526  *reinterpret_cast<const double *>( reinterpret_cast<const void *>( &gps.value ) ) );
527  break;
528  case LazAttribute::Red:
529  _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
530  break;
531  case LazAttribute::Green:
532  _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
533  break;
534  case LazAttribute::Blue:
535  _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
536  break;
537  case LazAttribute::ExtraBytes:
538  {
539  switch ( requestedAttribute.type )
540  {
542  _storeToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<char * >( &buf[requestedAttribute.offset] ) );
543  break;
545  _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<unsigned char * >( &buf[requestedAttribute.offset] ) );
546  break;
548  _storeToStream<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<qint16 * >( &buf[requestedAttribute.offset] ) );
549  break;
551  _storeToStream<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<quint16 * >( &buf[requestedAttribute.offset] ) );
552  break;
554  _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<qint32 * >( &buf[requestedAttribute.offset] ) );
555  break;
557  _storeToStream<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<quint32 * >( &buf[requestedAttribute.offset] ) );
558  break;
560  _storeToStream<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<qint64 * >( &buf[requestedAttribute.offset] ) );
561  break;
563  _storeToStream<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<quint64 * >( &buf[requestedAttribute.offset] ) );
564  break;
566  _storeToStream<float>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<float * >( &buf[requestedAttribute.offset] ) );
567  break;
569  _storeToStream<double>( dataBuffer, outputOffset, requestedAttribute.type, *reinterpret_cast<double * >( &buf[requestedAttribute.offset] ) );
570  break;
571  }
572  }
573  break;
574  case LazAttribute::MissingOrUnknown:
575  // just store 0 for unknown/missing attributes
576  _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
577  break;
578  }
579 
580  outputOffset += requestedAttribute.size;
581  }
582  }
583 
584 #ifdef QGISDEBUG
585  const float t = common::since( start );
586  QgsDebugMsgLevel( QStringLiteral( "LAZ-PERF Read through the points in %1 seconds." ).arg( t ), 2 );
587 #endif
589  count,
590  requestedAttributes,
591  data, scale, offset
592  );
593  return block;
594 }
595 
596 QgsPointCloudBlock *QgsEptDecoder::decompressLaz( const QString &filename,
597  const QgsPointCloudAttributeCollection &attributes,
598  const QgsPointCloudAttributeCollection &requestedAttributes,
599  const QgsVector3D &scale, const QgsVector3D &offset )
600 {
601  const QByteArray arr = filename.toUtf8();
602  std::ifstream file( arr.constData(), std::ios::binary );
603 
604  return __decompressLaz<std::ifstream>( file, attributes, requestedAttributes, scale, offset );
605 }
606 
607 QgsPointCloudBlock *QgsEptDecoder::decompressLaz( const QByteArray &byteArrayData,
608  const QgsPointCloudAttributeCollection &attributes,
609  const QgsPointCloudAttributeCollection &requestedAttributes,
610  const QgsVector3D &scale, const QgsVector3D &offset )
611 {
612  std::istringstream file( byteArrayData.toStdString() );
613  return __decompressLaz<std::istringstream>( file, attributes, requestedAttributes, scale, offset );
614 }
615 
Collection of point cloud attributes.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
Attribute for point cloud data pair of name and size in bytes.
DataType
Systems of unit measurement.
@ UShort
Unsigned short int 2 bytes.
@ UChar
Unsigned char 1 byte.
@ UInt32
Unsigned int32 4 bytes.
@ UInt64
Unsigned int64 8 bytes.
int size() const
Returns size of the attribute in bytes.
DataType type() const
Returns the data type.
Base class for storing raw data from point cloud nodes.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39