27#include "lazperf/las.hpp"
28#include "lazperf/readers.hpp"
32#include "qgspointcloudexpression.h"
36#include <QElapsedTimer>
39#include <QTemporaryFile>
41using namespace Qt::StringLiterals;
43#if defined( _MSC_VER )
59 const char val = char( value );
65 const unsigned char val = (
unsigned char ) ( value );
72 short val = short( value );
73 memcpy( s + position,
reinterpret_cast<char *
>( &val ),
sizeof(
short ) );
78 unsigned short val =
static_cast< unsigned short>( value );
79 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
unsigned short ) );
85 qint32 val = qint32( value );
86 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint32 ) );
91 quint32 val = quint32( value );
92 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint32 ) );
98 qint64 val = qint64( value );
99 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint64 ) );
104 quint64 val = quint64( value );
105 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint64 ) );
111 float val = float( value );
112 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
float ) );
117 double val = double( value );
118 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
double ) );
128 if ( outputType == inputType )
130 memcpy( data + outputPosition, input + inputPosition, inputSize );
138 const char val = *( input + inputPosition );
139 return lazStoreToStream_<char>( data, outputPosition, outputType, val );
143 const unsigned char val = *( input + inputPosition );
144 return lazStoreToStream_<unsigned char>( data, outputPosition, outputType, val );
148 const short val = *
reinterpret_cast< const short *
>( input + inputPosition );
149 return lazStoreToStream_<short>( data, outputPosition, outputType, val );
153 const unsigned short val = *
reinterpret_cast< const unsigned short *
>( input + inputPosition );
154 return lazStoreToStream_<unsigned short>( data, outputPosition, outputType, val );
158 const qint32 val = *
reinterpret_cast<const qint32 *
>( input + inputPosition );
159 return lazStoreToStream_<qint32>( data, outputPosition, outputType, val );
163 const quint32 val = *
reinterpret_cast<const quint32 *
>( input + inputPosition );
164 return lazStoreToStream_<quint32>( data, outputPosition, outputType, val );
168 const qint64 val = *
reinterpret_cast<const qint64 *
>( input + inputPosition );
169 return lazStoreToStream_<qint64>( data, outputPosition, outputType, val );
173 const quint64 val = *
reinterpret_cast<const quint64 *
>( input + inputPosition );
174 return lazStoreToStream_<quint64>( data, outputPosition, outputType, val );
178 const float val = *
reinterpret_cast< const float *
>( input + inputPosition );
179 return lazStoreToStream_<float>( data, outputPosition, outputType, val );
183 const double val = *
reinterpret_cast< const double *
>( input + inputPosition );
184 return lazStoreToStream_<double>( data, outputPosition, outputType, val );
192std::vector< QgsLazDecoder::RequestedAttributeDetails > prepareRequestedAttributeDetails_(
196 const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.
attributes();
198 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails;
199 requestedAttributeDetails.reserve( requestedAttributesVector.size() );
203 if ( requestedAttribute.name().compare(
'X'_L1, Qt::CaseInsensitive ) == 0 )
205 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
207 else if ( requestedAttribute.name().compare(
'Y'_L1, Qt::CaseInsensitive ) == 0 )
209 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
211 else if ( requestedAttribute.name().compare(
'Z'_L1, Qt::CaseInsensitive ) == 0 )
213 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
215 else if ( requestedAttribute.name().compare(
"Classification"_L1, Qt::CaseInsensitive ) == 0 )
217 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
219 else if ( requestedAttribute.name().compare(
"Intensity"_L1, Qt::CaseInsensitive ) == 0 )
221 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
223 else if ( requestedAttribute.name().compare(
"ReturnNumber"_L1, Qt::CaseInsensitive ) == 0 )
225 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
227 else if ( requestedAttribute.name().compare(
"NumberOfReturns"_L1, Qt::CaseInsensitive ) == 0 )
229 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
231 else if ( requestedAttribute.name().compare(
"ScanDirectionFlag"_L1, Qt::CaseInsensitive ) == 0 )
233 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
235 else if ( requestedAttribute.name().compare(
"EdgeOfFlightLine"_L1, Qt::CaseInsensitive ) == 0 )
237 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
239 else if ( requestedAttribute.name().compare(
"ScanAngleRank"_L1, Qt::CaseInsensitive ) == 0 )
241 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
243 else if ( requestedAttribute.name().compare(
"UserData"_L1, Qt::CaseInsensitive ) == 0 )
245 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
247 else if ( requestedAttribute.name().compare(
"PointSourceId"_L1, Qt::CaseInsensitive ) == 0 )
249 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
251 else if ( requestedAttribute.name().compare(
"GpsTime"_L1, Qt::CaseInsensitive ) == 0 )
253 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::GpsTime, requestedAttribute.type(), requestedAttribute.size() ) );
255 else if ( requestedAttribute.name().compare(
"Red"_L1, Qt::CaseInsensitive ) == 0 )
257 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
259 else if ( requestedAttribute.name().compare(
"Green"_L1, Qt::CaseInsensitive ) == 0 )
261 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
263 else if ( requestedAttribute.name().compare(
"Blue"_L1, Qt::CaseInsensitive ) == 0 )
265 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
267 else if ( requestedAttribute.name().compare(
"ScannerChannel"_L1, Qt::CaseInsensitive ) == 0 )
269 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScannerChannel, requestedAttribute.type(), requestedAttribute.size() ) );
271 else if ( requestedAttribute.name().compare(
"Synthetic"_L1, Qt::CaseInsensitive ) == 0 )
273 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Synthetic, requestedAttribute.type(), requestedAttribute.size() ) );
275 else if ( requestedAttribute.name().compare(
"KeyPoint"_L1, Qt::CaseInsensitive ) == 0 )
277 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::KeyPoint, requestedAttribute.type(), requestedAttribute.size() ) );
279 else if ( requestedAttribute.name().compare(
"Withheld"_L1, Qt::CaseInsensitive ) == 0 )
281 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Withheld, requestedAttribute.type(), requestedAttribute.size() ) );
283 else if ( requestedAttribute.name().compare(
"Overlap"_L1, Qt::CaseInsensitive ) == 0 )
285 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Overlap, requestedAttribute.type(), requestedAttribute.size() ) );
287 else if ( requestedAttribute.name().compare(
"Infrared"_L1, Qt::CaseInsensitive ) == 0 )
289 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NIR, requestedAttribute.type(), requestedAttribute.size() ) );
293 bool foundAttr =
false;
296 if ( requestedAttribute.name().compare( eba.attribute.trimmed() ) == 0 )
298 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ExtraBytes, eba.type, eba.size, eba.offset ) );
306 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
310 return requestedAttributeDetails;
313bool decodePoint(
char *buf,
int lasPointFormat,
char *dataBuffer, std::size_t &outputOffset, std::vector< QgsLazDecoder::RequestedAttributeDetails > &requestedAttributeDetails )
315 lazperf::las::point10 p10;
316 lazperf::las::gpstime gps;
317 lazperf::las::rgb rgb;
318 lazperf::las::nir14 nir;
319 lazperf::las::point14 p14;
323 const bool isLas14 = ( lasPointFormat == 6 || lasPointFormat == 7 || lasPointFormat == 8 || lasPointFormat == 9 || lasPointFormat == 10 );
325 switch ( lasPointFormat )
333 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
337 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) );
341 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
342 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) +
sizeof( lazperf::las::gpstime ) );
351 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
355 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
356 nir.unpack( buf +
sizeof( lazperf::las::point14 ) +
sizeof( lazperf::las::rgb ) );
364 for (
const QgsLazDecoder::RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
366 switch ( requestedAttribute.attribute )
368 case QgsLazDecoder::LazAttribute::X:
369 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.x() : p10.x );
371 case QgsLazDecoder::LazAttribute::Y:
372 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.y() : p10.y );
374 case QgsLazDecoder::LazAttribute::Z:
375 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.z() : p10.z );
377 case QgsLazDecoder::LazAttribute::Classification:
381 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p14.classification() );
386 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 0 : p10.classification & 0x1F );
390 case QgsLazDecoder::LazAttribute::Intensity:
391 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.intensity() : p10.intensity );
393 case QgsLazDecoder::LazAttribute::ReturnNumber:
394 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.returnNum() : p10.return_number );
396 case QgsLazDecoder::LazAttribute::NumberOfReturns:
397 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.numReturns() : p10.number_of_returns_of_given_pulse );
399 case QgsLazDecoder::LazAttribute::ScanDirectionFlag:
400 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanDirFlag() : p10.scan_direction_flag );
402 case QgsLazDecoder::LazAttribute::EdgeOfFlightLine:
403 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.eofFlag() : p10.edge_of_flight_line );
405 case QgsLazDecoder::LazAttribute::ScanAngleRank:
406 lazStoreToStream_<float>(
409 requestedAttribute.type,
412 ? p14.scanAngle() * 0.006f
414 : p10.scan_angle_rank
417 case QgsLazDecoder::LazAttribute::UserData:
418 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
420 case QgsLazDecoder::LazAttribute::PointSourceId:
421 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
423 case QgsLazDecoder::LazAttribute::GpsTime:
425 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.gpsTime() : *
reinterpret_cast<const double *
>(
reinterpret_cast<const void *
>( &gps.value ) ) );
427 case QgsLazDecoder::LazAttribute::Red:
428 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
430 case QgsLazDecoder::LazAttribute::Green:
431 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
433 case QgsLazDecoder::LazAttribute::Blue:
434 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
436 case QgsLazDecoder::LazAttribute::ScannerChannel:
437 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( p14.scannerChannel() ) : 0 );
439 case QgsLazDecoder::LazAttribute::Synthetic:
440 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 0 ) & 0x01 ) :
char( ( p10.classification >> 5 ) & 0x01 ) );
442 case QgsLazDecoder::LazAttribute::KeyPoint:
443 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 1 ) & 0x01 ) :
char( ( p10.classification >> 6 ) & 0x01 ) );
445 case QgsLazDecoder::LazAttribute::Withheld:
446 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 2 ) & 0x01 ) :
char( ( p10.classification >> 7 ) & 0x01 ) );
448 case QgsLazDecoder::LazAttribute::Overlap:
452 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( ( p14.classFlags() >> 3 ) & 0x01 ) );
457 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 1 : 0 );
461 case QgsLazDecoder::LazAttribute::NIR:
463 if ( lasPointFormat == 8 || lasPointFormat == 10 )
465 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
470 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
474 case QgsLazDecoder::LazAttribute::ExtraBytes:
476 switch ( requestedAttribute.type )
479 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
482 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
485 lazStoreToStream_<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
488 lazStoreToStream_<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
491 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
494 lazStoreToStream_<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
497 lazStoreToStream_<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
500 lazStoreToStream_<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
503 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
506 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
511 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
513 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
517 outputOffset += requestedAttribute.size;
522template<
typename FileType>
536 lazperf::reader::generic_file f( file );
543 int lasPointFormat = f.header().pointFormat();
544 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 && lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
546 QgsDebugError( u
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported"_s.arg( lasPointFormat ) );
550 const size_t count = f.header().point_count;
551 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
552 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
554 QByteArray bufArray( f.header().point_record_length, 0 );
555 char *buf = bufArray.data();
557 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
559 data.resize( requestedPointRecordSize * count );
560 char *dataBuffer = data.data();
562 std::size_t outputOffset = 0;
564 auto block = std::make_unique< QgsPointCloudBlock >( count, requestedAttributes, data, scale, offset );
566 int skippedPoints = 0;
567 const bool filterIsValid = filterExpression.isValid();
568 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
571 block->setPointCount( 0 );
575 int xAttributeOffset, yAttributeOffset;
578 const bool hasFilterRect = !filterRect.
isEmpty();
581 attributeX = requestedAttributes.
find(
"X"_L1, xAttributeOffset );
582 attributeY = requestedAttributes.
find(
"Y"_L1, yAttributeOffset );
589 std::vector<char> rawExtrabytes = f.vlrData(
"LASF_Spec", 4 );
590 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails =
QgsLazInfo::parseExtrabytes( rawExtrabytes.data(), rawExtrabytes.size(), f.header().point_record_length );
591 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
593 for (
size_t i = 0; i < count; i++ )
597 bool skipThisPoint = !decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
600 if ( !skipThisPoint && hasFilterRect && attributeX && attributeY )
602 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
603 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
605 skipThisPoint =
true;
607 if ( !skipThisPoint && filterIsValid )
610 double eval = filterExpression.evaluate( i - skippedPoints );
611 if ( !eval || std::isnan( eval ) )
612 skipThisPoint =
true;
617 outputOffset -= requestedPointRecordSize;
623 QgsDebugMsgLevel( u
"LAZ-PERF Read through the points in %1 seconds."_s.arg( t.elapsed() / 1000. ), 2 );
625 block->setPointCount( count - skippedPoints );
628 catch ( std::exception &e )
630 QgsDebugError(
"Error decompressing laz file: " + QString::fromLatin1( e.what() ) );
635std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
639 std::ifstream file( toNativePath( filename ), std::ios::binary );
641 return decompressLaz_<std::ifstream>( file, requestedAttributes, filterExpression, filterRect );
644std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
648 std::istringstream file( byteArrayData.toStdString() );
649 return decompressLaz_<std::istringstream>( file, requestedAttributes, filterExpression, filterRect );
652std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressCopc(
658 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
660 QgsDebugError( u
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format"_s.arg( lasPointFormat ) );
666 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
668 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
669 QByteArray blockData;
670 blockData.resize( requestedPointRecordSize * pointCount );
671 char *dataBuffer = blockData.data();
673 std::size_t outputOffset = 0;
675 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
676 std::vector< RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
677 auto block = std::make_unique< QgsPointCloudBlock >( pointCount, requestedAttributes, blockData, lazInfo.
scale(), lazInfo.
offset() );
679 int skippedPoints = 0;
680 const bool filterIsValid = filterExpression.isValid();
681 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
684 block->setPointCount( 0 );
688 int xAttributeOffset, yAttributeOffset;
691 const bool hasFilterRect = !filterRect.
isEmpty();
694 attributeX = requestedAttributes.
find(
"X"_L1, xAttributeOffset );
695 attributeY = requestedAttributes.
find(
"Y"_L1, yAttributeOffset );
701 for (
int i = 0; i < pointCount; ++i )
703 decompressor.decompress( decodedData.get() );
704 char *buf = decodedData.get();
706 bool skipThisPoint = !decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
709 if ( !skipThisPoint && hasFilterRect && attributeX && attributeY )
711 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
712 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
714 skipThisPoint =
true;
716 if ( !skipThisPoint && filterIsValid )
719 double eval = filterExpression.evaluate( i - skippedPoints );
720 if ( !eval || std::isnan( eval ) )
721 skipThisPoint =
true;
726 outputOffset -= requestedPointRecordSize;
731 block->setPointCount( pointCount - skippedPoints );
735#if defined( _MSC_VER )
736std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
738 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
739 return converter.from_bytes( filename.toStdString() );
742std::string QgsLazDecoder::toNativePath(
const QString &filename )
744 return filename.toStdString();
Extracts information contained in a LAZ file, such as the public header block and variable length rec...
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.
A 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.
@ Short
Short int 2 bytes.
@ UChar
Unsigned char 1 byte.
@ UInt32
Unsigned int32 4 bytes.
@ UInt64
Unsigned int64 8 bytes.
double convertValueToDouble(const char *ptr) const
Returns the attribute's value as a double for data pointed to by ptr.
A rectangle specified with double values.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
double y() const
Returns Y coordinate.
double x() const
Returns X coordinate.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)