31#include <QElapsedTimer>
32#include <QTemporaryFile>
37#include "lazperf/las.hpp"
38#include <lazperf/lazperf.hpp>
55 const char val = char( value );
61 const unsigned char val = (
unsigned char )( value );
68 short val = short( value );
69 memcpy( s + position,
reinterpret_cast<char *
>( &val ),
sizeof(
short ) );
74 unsigned short val =
static_cast< unsigned short>( value );
75 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
unsigned short ) );
81 qint32 val = qint32( value );
82 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint32 ) );
87 quint32 val = quint32( value );
88 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint32 ) );
94 qint64 val = qint64( value );
95 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint64 ) );
100 quint64 val = quint64( value );
101 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint64 ) );
107 float val = float( value );
108 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
float ) );
113 double val = double( value );
114 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
double ) );
125 if ( outputType == inputType )
127 memcpy( data + outputPosition, input + inputPosition, inputSize );
135 const char val = *( input + inputPosition );
136 return _lazStoreToStream<char>( data, outputPosition, outputType, val );
140 const unsigned char val = *( input + inputPosition );
141 return _lazStoreToStream<unsigned char>( data, outputPosition, outputType, val );
145 const short val = *
reinterpret_cast< const short *
>( input + inputPosition );
146 return _lazStoreToStream<short>( data, outputPosition, outputType, val );
150 const unsigned short val = *
reinterpret_cast< const unsigned short *
>( input + inputPosition );
151 return _lazStoreToStream<unsigned short>( data, outputPosition, outputType, val );
155 const qint32 val = *
reinterpret_cast<const qint32 *
>( input + inputPosition );
156 return _lazStoreToStream<qint32>( data, outputPosition, outputType, val );
160 const quint32 val = *
reinterpret_cast<const quint32 *
>( input + inputPosition );
161 return _lazStoreToStream<quint32>( data, outputPosition, outputType, val );
165 const qint64 val = *
reinterpret_cast<const qint64 *
>( input + inputPosition );
166 return _lazStoreToStream<qint64>( data, outputPosition, outputType, val );
170 const quint64 val = *
reinterpret_cast<const quint64 *
>( input + inputPosition );
171 return _lazStoreToStream<quint64>( data, outputPosition, outputType, val );
175 const float val = *
reinterpret_cast< const float *
>( input + inputPosition );
176 return _lazStoreToStream<float>( data, outputPosition, outputType, val );
180 const double val = *
reinterpret_cast< const double *
>( input + inputPosition );
181 return _lazStoreToStream<double>( data, outputPosition, outputType, val );
189std::vector< QgsLazDecoder::RequestedAttributeDetails > __prepareRequestedAttributeDetails(
const QgsPointCloudAttributeCollection &requestedAttributes, QVector<QgsLazInfo::ExtraBytesAttributeDetails> &extrabytesAttr )
191 const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.
attributes();
193 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails;
194 requestedAttributeDetails.reserve( requestedAttributesVector.size() );
197 if ( requestedAttribute.name().compare( QLatin1String(
"X" ), Qt::CaseInsensitive ) == 0 )
199 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
201 else if ( requestedAttribute.name().compare( QLatin1String(
"Y" ), Qt::CaseInsensitive ) == 0 )
203 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
205 else if ( requestedAttribute.name().compare( QLatin1String(
"Z" ), Qt::CaseInsensitive ) == 0 )
207 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
209 else if ( requestedAttribute.name().compare( QLatin1String(
"Classification" ), Qt::CaseInsensitive ) == 0 )
211 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
213 else if ( requestedAttribute.name().compare( QLatin1String(
"Intensity" ), Qt::CaseInsensitive ) == 0 )
215 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
217 else if ( requestedAttribute.name().compare( QLatin1String(
"ReturnNumber" ), Qt::CaseInsensitive ) == 0 )
219 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
221 else if ( requestedAttribute.name().compare( QLatin1String(
"NumberOfReturns" ), Qt::CaseInsensitive ) == 0 )
223 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
225 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanDirectionFlag" ), Qt::CaseInsensitive ) == 0 )
227 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
229 else if ( requestedAttribute.name().compare( QLatin1String(
"EdgeOfFlightLine" ), Qt::CaseInsensitive ) == 0 )
231 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
233 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanAngleRank" ), Qt::CaseInsensitive ) == 0 )
235 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
237 else if ( requestedAttribute.name().compare( QLatin1String(
"UserData" ), Qt::CaseInsensitive ) == 0 )
239 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
241 else if ( requestedAttribute.name().compare( QLatin1String(
"PointSourceId" ), Qt::CaseInsensitive ) == 0 )
243 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
245 else if ( requestedAttribute.name().compare( QLatin1String(
"GpsTime" ), Qt::CaseInsensitive ) == 0 )
247 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::GpsTime, requestedAttribute.type(), requestedAttribute.size() ) );
249 else if ( requestedAttribute.name().compare( QLatin1String(
"Red" ), Qt::CaseInsensitive ) == 0 )
251 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
253 else if ( requestedAttribute.name().compare( QLatin1String(
"Green" ), Qt::CaseInsensitive ) == 0 )
255 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
257 else if ( requestedAttribute.name().compare( QLatin1String(
"Blue" ), Qt::CaseInsensitive ) == 0 )
259 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
261 else if ( requestedAttribute.name().compare( QLatin1String(
"ScannerChannel" ), Qt::CaseInsensitive ) == 0 )
263 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScannerChannel, requestedAttribute.type(), requestedAttribute.size() ) );
265 else if ( requestedAttribute.name().compare( QLatin1String(
"ClassificationFlags" ), Qt::CaseInsensitive ) == 0 )
267 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ClassificationFlags, requestedAttribute.type(), requestedAttribute.size() ) );
269 else if ( requestedAttribute.name().compare( QLatin1String(
"Infrared" ), Qt::CaseInsensitive ) == 0 )
271 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NIR, requestedAttribute.type(), requestedAttribute.size() ) );
275 bool foundAttr =
false;
278 if ( requestedAttribute.name().compare( eba.attribute.trimmed() ) == 0 )
280 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ExtraBytes, eba.type, eba.size, eba.offset ) );
288 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
292 return requestedAttributeDetails;
295void decodePoint(
char *buf,
int lasPointFormat,
char *dataBuffer, std::size_t &outputOffset, std::vector< QgsLazDecoder::RequestedAttributeDetails > &requestedAttributeDetails )
297 lazperf::las::point10 p10;
298 lazperf::las::gpstime gps;
299 lazperf::las::rgb rgb;
300 lazperf::las::nir14 nir;
301 lazperf::las::point14 p14;
303 bool isLas14 = ( lasPointFormat == 6 || lasPointFormat == 7 || lasPointFormat == 8 );
305 switch ( lasPointFormat )
313 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
317 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) );
321 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
322 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) +
sizeof( lazperf::las::gpstime ) );
331 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
335 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
336 nir.unpack( buf +
sizeof( lazperf::las::point14 ) +
sizeof( lazperf::las::rgb ) );
343 for (
const QgsLazDecoder::RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
345 switch ( requestedAttribute.attribute )
347 case QgsLazDecoder::LazAttribute::X:
348 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.x() : p10.x );
350 case QgsLazDecoder::LazAttribute::Y:
351 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.y() : p10.y );
353 case QgsLazDecoder::LazAttribute::Z:
354 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.z() : p10.z );
356 case QgsLazDecoder::LazAttribute::Classification:
357 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.classification() : p10.classification );
359 case QgsLazDecoder::LazAttribute::Intensity:
360 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.intensity() : p10.intensity );
362 case QgsLazDecoder::LazAttribute::ReturnNumber:
363 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.returnNum() : p10.return_number );
365 case QgsLazDecoder::LazAttribute::NumberOfReturns:
366 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.numReturns() : p10.number_of_returns_of_given_pulse );
368 case QgsLazDecoder::LazAttribute::ScanDirectionFlag:
369 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanDirFlag() : p10.scan_direction_flag );
371 case QgsLazDecoder::LazAttribute::EdgeOfFlightLine:
372 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.eofFlag() : p10.edge_of_flight_line );
374 case QgsLazDecoder::LazAttribute::ScanAngleRank:
375 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanAngle() : p10.scan_angle_rank );
377 case QgsLazDecoder::LazAttribute::UserData:
378 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
380 case QgsLazDecoder::LazAttribute::PointSourceId:
381 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
383 case QgsLazDecoder::LazAttribute::GpsTime:
385 _lazStoreToStream<double>( dataBuffer, outputOffset, requestedAttribute.type,
386 isLas14 ? p14.gpsTime() : *
reinterpret_cast<const double *
>(
reinterpret_cast<const void *
>( &gps.value ) ) );
388 case QgsLazDecoder::LazAttribute::Red:
389 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
391 case QgsLazDecoder::LazAttribute::Green:
392 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
394 case QgsLazDecoder::LazAttribute::Blue:
395 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
397 case QgsLazDecoder::LazAttribute::ScannerChannel:
398 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, p14.scannerChannel() );
400 case QgsLazDecoder::LazAttribute::ClassificationFlags:
401 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, p14.classFlags() );
403 case QgsLazDecoder::LazAttribute::NIR:
405 if ( lasPointFormat == 8 || lasPointFormat == 10 )
407 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
412 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
417 case QgsLazDecoder::LazAttribute::ExtraBytes:
419 switch ( requestedAttribute.type )
422 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
425 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
428 _lazStoreToStream<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
431 _lazStoreToStream<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
434 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
437 _lazStoreToStream<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
440 _lazStoreToStream<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
443 _lazStoreToStream<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
446 _lazStoreToStream<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
449 _lazStoreToStream<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
454 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
456 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
460 outputOffset += requestedAttribute.size;
464template<
typename FileType>
478 lazperf::reader::generic_file f( file );
485 int lasPointFormat = f.header().pointFormat();
486 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 &&
487 lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
489 QgsDebugMsg( QStringLiteral(
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported" ).arg( lasPointFormat ) );
493 const size_t count = f.header().point_count;
494 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
495 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
497 QByteArray bufArray( f.header().point_record_length, 0 );
498 char *buf = bufArray.data();
500 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
502 data.resize( requestedPointRecordSize * count );
503 char *dataBuffer = data.data();
505 std::size_t outputOffset = 0;
507 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
513 int skippedPoints = 0;
514 const bool filterIsValid = filterExpression.isValid();
515 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
518 block->setPointCount( 0 );
519 return block.release();
522 std::vector<char> rawExtrabytes = f.vlrData(
"LASF_Spec", 4 );
523 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails =
QgsLazInfo::parseExtrabytes( rawExtrabytes.data(), rawExtrabytes.size(), f.header().point_record_length );
524 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails = __prepareRequestedAttributeDetails( requestedAttributes, extrabyteAttributesDetails );
526 for (
size_t i = 0 ; i < count ; i ++ )
530 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
536 double eval = filterExpression.evaluate( i - skippedPoints );
537 if ( !eval || std::isnan( eval ) )
540 outputOffset -= requestedPointRecordSize;
547 QgsDebugMsgLevel( QStringLiteral(
"LAZ-PERF Read through the points in %1 seconds." ).arg( t.elapsed() / 1000. ), 2 );
549 block->setPointCount( count - skippedPoints );
550 return block.release();
552 catch ( std::exception &e )
554 QgsDebugMsg(
"Error decompressing laz file: " + QString::fromLatin1( e.what() ) );
561 QgsPointCloudExpression &filterExpression )
563 std::ifstream file( toNativePath( filename ), std::ios::binary );
565 return __decompressLaz<std::ifstream>( file, requestedAttributes, filterExpression );
570 QgsPointCloudExpression &filterExpression )
572 std::istringstream file( byteArrayData.toStdString() );
573 return __decompressLaz<std::istringstream>( file, requestedAttributes, filterExpression );
580 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
582 QgsDebugMsg( QStringLiteral(
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format" ).arg( lasPointFormat ) );
588 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
590 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
591 QByteArray blockData;
592 blockData.resize( requestedPointRecordSize * pointCount );
593 char *dataBuffer = blockData.data();
595 std::size_t outputOffset = 0;
597 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
598 std::vector< RequestedAttributeDetails > requestedAttributeDetails = __prepareRequestedAttributeDetails( requestedAttributes, extrabyteAttributesDetails );
599 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
600 pointCount, requestedAttributes,
604 int skippedPoints = 0;
605 const bool filterIsValid = filterExpression.isValid();
606 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
609 block->setPointCount( 0 );
610 return block.release();
613 for (
int i = 0 ; i < pointCount; ++i )
615 decompressor.decompress( decodedData.get() );
616 char *buf = decodedData.get();
618 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
624 double eval = filterExpression.evaluate( i - skippedPoints );
625 if ( !eval || std::isnan( eval ) )
628 outputOffset -= requestedPointRecordSize;
634 block->setPointCount( pointCount - skippedPoints );
635 return block.release();
639std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
641 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
642 return converter.from_bytes( filename.toStdString() );
645std::string QgsLazDecoder::toNativePath(
const QString &filename )
647 return filename.toStdString();
Class for extracting information contained in LAZ file such as the public header block and variable l...
int extrabytesCount() const
Returns the number of extrabytes contained in the LAZ dataset.
QgsVector3D scale() const
Returns the scale of the points coordinates.
int pointFormat() const
Returns the point format of the point records contained in the LAZ file.
QVector< ExtraBytesAttributeDetails > extrabytes() const
Returns the list of extrabytes contained in the LAZ file.
QgsVector3D offset() const
Returns the offset of the points coordinates.
int pointRecordLength() const
Returns the length of each point record in bytes.
static QVector< ExtraBytesAttributeDetails > parseExtrabytes(char *rawData, int length, int pointRecordLength)
Static function to parse the raw extrabytes VLR into a list of recognizable extrabyte attributes.
Collection of point cloud attributes.
int pointRecordSize() const
Returns total size of record.
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.
@ Short
Short int 2 bytes.
@ UChar
Unsigned char 1 byte.
@ UInt32
Unsigned int32 4 bytes.
@ UInt64
Unsigned int64 8 bytes.
Base class for storing raw data from point cloud nodes.
#define QgsDebugMsgLevel(str, level)