27#include "lazperf/las.hpp"
28#include "lazperf/readers.hpp"
32#include "qgspointcloudexpression.h"
36#include <QElapsedTimer>
39#include <QTemporaryFile>
41using namespace Qt::StringLiterals;
60 const char val = char( value );
66 const unsigned char val = (
unsigned char )( value );
73 short val = short( value );
74 memcpy( s + position,
reinterpret_cast<char *
>( &val ),
sizeof(
short ) );
79 unsigned short val =
static_cast< unsigned short>( value );
80 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
unsigned short ) );
86 qint32 val = qint32( value );
87 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint32 ) );
92 quint32 val = quint32( value );
93 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint32 ) );
99 qint64 val = qint64( value );
100 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint64 ) );
105 quint64 val = quint64( value );
106 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint64 ) );
112 float val = float( value );
113 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
float ) );
118 double val = double( value );
119 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
double ) );
130 if ( outputType == inputType )
132 memcpy( data + outputPosition, input + inputPosition, inputSize );
140 const char val = *( input + inputPosition );
141 return lazStoreToStream_<char>( data, outputPosition, outputType, val );
145 const unsigned char val = *( input + inputPosition );
146 return lazStoreToStream_<unsigned char>( data, outputPosition, outputType, val );
150 const short val = *
reinterpret_cast< const short *
>( input + inputPosition );
151 return lazStoreToStream_<short>( data, outputPosition, outputType, val );
155 const unsigned short val = *
reinterpret_cast< const unsigned short *
>( input + inputPosition );
156 return lazStoreToStream_<unsigned short>( data, outputPosition, outputType, val );
160 const qint32 val = *
reinterpret_cast<const qint32 *
>( input + inputPosition );
161 return lazStoreToStream_<qint32>( data, outputPosition, outputType, val );
165 const quint32 val = *
reinterpret_cast<const quint32 *
>( input + inputPosition );
166 return lazStoreToStream_<quint32>( data, outputPosition, outputType, val );
170 const qint64 val = *
reinterpret_cast<const qint64 *
>( input + inputPosition );
171 return lazStoreToStream_<qint64>( data, outputPosition, outputType, val );
175 const quint64 val = *
reinterpret_cast<const quint64 *
>( input + inputPosition );
176 return lazStoreToStream_<quint64>( data, outputPosition, outputType, val );
180 const float val = *
reinterpret_cast< const float *
>( input + inputPosition );
181 return lazStoreToStream_<float>( data, outputPosition, outputType, val );
185 const double val = *
reinterpret_cast< const double *
>( input + inputPosition );
186 return lazStoreToStream_<double>( data, outputPosition, outputType, val );
194std::vector< QgsLazDecoder::RequestedAttributeDetails > prepareRequestedAttributeDetails_(
const QgsPointCloudAttributeCollection &requestedAttributes, QVector<QgsLazInfo::ExtraBytesAttributeDetails> &extrabytesAttr )
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>( dataBuffer, outputOffset, requestedAttribute.type,
409 ? p14.scanAngle() * 0.006f
411 : p10.scan_angle_rank );
413 case QgsLazDecoder::LazAttribute::UserData:
414 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
416 case QgsLazDecoder::LazAttribute::PointSourceId:
417 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
419 case QgsLazDecoder::LazAttribute::GpsTime:
421 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type,
422 isLas14 ? p14.gpsTime() : *
reinterpret_cast<const double *
>(
reinterpret_cast<const void *
>( &gps.value ) ) );
424 case QgsLazDecoder::LazAttribute::Red:
425 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
427 case QgsLazDecoder::LazAttribute::Green:
428 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
430 case QgsLazDecoder::LazAttribute::Blue:
431 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
433 case QgsLazDecoder::LazAttribute::ScannerChannel:
434 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( p14.scannerChannel() ) : 0 );
436 case QgsLazDecoder::LazAttribute::Synthetic:
437 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 0 ) & 0x01 ) :
char( ( p10.classification >> 5 ) & 0x01 ) );
439 case QgsLazDecoder::LazAttribute::KeyPoint:
440 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 1 ) & 0x01 ) :
char( ( p10.classification >> 6 ) & 0x01 ) );
442 case QgsLazDecoder::LazAttribute::Withheld:
443 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 2 ) & 0x01 ) :
char( ( p10.classification >> 7 ) & 0x01 ) );
445 case QgsLazDecoder::LazAttribute::Overlap:
449 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( ( p14.classFlags() >> 3 ) & 0x01 ) );
454 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 1 : 0 );
458 case QgsLazDecoder::LazAttribute::NIR:
460 if ( lasPointFormat == 8 || lasPointFormat == 10 )
462 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
467 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
472 case QgsLazDecoder::LazAttribute::ExtraBytes:
474 switch ( requestedAttribute.type )
477 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
480 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
483 lazStoreToStream_<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
486 lazStoreToStream_<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
489 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
492 lazStoreToStream_<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
495 lazStoreToStream_<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
498 lazStoreToStream_<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
501 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
504 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
509 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
511 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
515 outputOffset += requestedAttribute.size;
520template<
typename FileType>
534 lazperf::reader::generic_file f( file );
541 int lasPointFormat = f.header().pointFormat();
542 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 &&
543 lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
545 QgsDebugError( u
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported"_s.arg( lasPointFormat ) );
549 const size_t count = f.header().point_count;
550 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
551 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
553 QByteArray bufArray( f.header().point_record_length, 0 );
554 char *buf = bufArray.data();
556 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
558 data.resize( requestedPointRecordSize * count );
559 char *dataBuffer = data.data();
561 std::size_t outputOffset = 0;
563 auto block = std::make_unique< QgsPointCloudBlock >(
569 int skippedPoints = 0;
570 const bool filterIsValid = filterExpression.isValid();
571 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
574 block->setPointCount( 0 );
578 int xAttributeOffset, yAttributeOffset;
581 const bool hasFilterRect = !filterRect.
isEmpty();
584 attributeX = requestedAttributes.
find(
"X"_L1, xAttributeOffset );
585 attributeY = requestedAttributes.
find(
"Y"_L1, yAttributeOffset );
592 std::vector<char> rawExtrabytes = f.vlrData(
"LASF_Spec", 4 );
593 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails =
QgsLazInfo::parseExtrabytes( rawExtrabytes.data(), rawExtrabytes.size(), f.header().point_record_length );
594 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
596 for (
size_t i = 0 ; i < count ; i ++ )
600 bool skipThisPoint = !decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
603 if ( !skipThisPoint && hasFilterRect && attributeX && attributeY )
605 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
606 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
608 skipThisPoint =
true;
610 if ( !skipThisPoint && filterIsValid )
613 double eval = filterExpression.evaluate( i - skippedPoints );
614 if ( !eval || std::isnan( eval ) )
615 skipThisPoint =
true;
620 outputOffset -= requestedPointRecordSize;
626 QgsDebugMsgLevel( u
"LAZ-PERF Read through the points in %1 seconds."_s.arg( t.elapsed() / 1000. ), 2 );
628 block->setPointCount( count - skippedPoints );
631 catch ( std::exception &e )
633 QgsDebugError(
"Error decompressing laz file: " + QString::fromLatin1( e.what() ) );
638std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QString &filename,
640 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
642 std::ifstream file( toNativePath( filename ), std::ios::binary );
644 return decompressLaz_<std::ifstream>( file, requestedAttributes, filterExpression, filterRect );
647std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QByteArray &byteArrayData,
649 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
651 std::istringstream file( byteArrayData.toStdString() );
652 return decompressLaz_<std::istringstream>( file, requestedAttributes, filterExpression, filterRect );
659 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
661 QgsDebugError( u
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format"_s.arg( lasPointFormat ) );
667 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
669 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
670 QByteArray blockData;
671 blockData.resize( requestedPointRecordSize * pointCount );
672 char *dataBuffer = blockData.data();
674 std::size_t outputOffset = 0;
676 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
677 std::vector< RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
678 auto block = std::make_unique< QgsPointCloudBlock >(
679 pointCount, requestedAttributes,
683 int skippedPoints = 0;
684 const bool filterIsValid = filterExpression.isValid();
685 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
688 block->setPointCount( 0 );
692 int xAttributeOffset, yAttributeOffset;
695 const bool hasFilterRect = !filterRect.
isEmpty();
698 attributeX = requestedAttributes.
find(
"X"_L1, xAttributeOffset );
699 attributeY = requestedAttributes.
find(
"Y"_L1, yAttributeOffset );
705 for (
int i = 0 ; i < pointCount; ++i )
707 decompressor.decompress( decodedData.get() );
708 char *buf = decodedData.get();
710 bool skipThisPoint = !decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
713 if ( !skipThisPoint && hasFilterRect && attributeX && attributeY )
715 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
716 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
718 skipThisPoint =
true;
720 if ( !skipThisPoint && filterIsValid )
723 double eval = filterExpression.evaluate( i - skippedPoints );
724 if ( !eval || std::isnan( eval ) )
725 skipThisPoint =
true;
730 outputOffset -= requestedPointRecordSize;
735 block->setPointCount( pointCount - skippedPoints );
740std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
742 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
743 return converter.from_bytes( filename.toStdString() );
746std::string QgsLazDecoder::toNativePath(
const QString &filename )
748 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)