24#include "qgspointcloudexpression.h"
28#include <QElapsedTimer>
29#include <QTemporaryFile>
37#include "lazperf/las.hpp"
38#include "lazperf/readers.hpp"
57 const char val = char( value );
63 const unsigned char val = (
unsigned char )( value );
70 short val = short( value );
71 memcpy( s + position,
reinterpret_cast<char *
>( &val ),
sizeof(
short ) );
76 unsigned short val =
static_cast< unsigned short>( value );
77 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
unsigned short ) );
83 qint32 val = qint32( value );
84 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint32 ) );
89 quint32 val = quint32( value );
90 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint32 ) );
96 qint64 val = qint64( value );
97 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint64 ) );
102 quint64 val = quint64( value );
103 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( quint64 ) );
109 float val = float( value );
110 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
float ) );
115 double val = double( value );
116 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
double ) );
127 if ( outputType == inputType )
129 memcpy( data + outputPosition, input + inputPosition, inputSize );
137 const char val = *( input + inputPosition );
138 return lazStoreToStream_<char>( data, outputPosition, outputType, val );
142 const unsigned char val = *( input + inputPosition );
143 return lazStoreToStream_<unsigned char>( data, outputPosition, outputType, val );
147 const short val = *
reinterpret_cast< const short *
>( input + inputPosition );
148 return lazStoreToStream_<short>( data, outputPosition, outputType, val );
152 const unsigned short val = *
reinterpret_cast< const unsigned short *
>( input + inputPosition );
153 return lazStoreToStream_<unsigned short>( data, outputPosition, outputType, val );
157 const qint32 val = *
reinterpret_cast<const qint32 *
>( input + inputPosition );
158 return lazStoreToStream_<qint32>( data, outputPosition, outputType, val );
162 const quint32 val = *
reinterpret_cast<const quint32 *
>( input + inputPosition );
163 return lazStoreToStream_<quint32>( data, outputPosition, outputType, val );
167 const qint64 val = *
reinterpret_cast<const qint64 *
>( input + inputPosition );
168 return lazStoreToStream_<qint64>( data, outputPosition, outputType, val );
172 const quint64 val = *
reinterpret_cast<const quint64 *
>( input + inputPosition );
173 return lazStoreToStream_<quint64>( data, outputPosition, outputType, val );
177 const float val = *
reinterpret_cast< const float *
>( input + inputPosition );
178 return lazStoreToStream_<float>( data, outputPosition, outputType, val );
182 const double val = *
reinterpret_cast< const double *
>( input + inputPosition );
183 return lazStoreToStream_<double>( data, outputPosition, outputType, val );
191std::vector< QgsLazDecoder::RequestedAttributeDetails > prepareRequestedAttributeDetails_(
const QgsPointCloudAttributeCollection &requestedAttributes, QVector<QgsLazInfo::ExtraBytesAttributeDetails> &extrabytesAttr )
193 const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.
attributes();
195 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails;
196 requestedAttributeDetails.reserve( requestedAttributesVector.size() );
200 if ( requestedAttribute.name().compare( QLatin1String(
"X" ), Qt::CaseInsensitive ) == 0 )
202 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
204 else if ( requestedAttribute.name().compare( QLatin1String(
"Y" ), Qt::CaseInsensitive ) == 0 )
206 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
208 else if ( requestedAttribute.name().compare( QLatin1String(
"Z" ), Qt::CaseInsensitive ) == 0 )
210 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
212 else if ( requestedAttribute.name().compare( QLatin1String(
"Classification" ), Qt::CaseInsensitive ) == 0 )
214 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
216 else if ( requestedAttribute.name().compare( QLatin1String(
"Intensity" ), Qt::CaseInsensitive ) == 0 )
218 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
220 else if ( requestedAttribute.name().compare( QLatin1String(
"ReturnNumber" ), Qt::CaseInsensitive ) == 0 )
222 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
224 else if ( requestedAttribute.name().compare( QLatin1String(
"NumberOfReturns" ), Qt::CaseInsensitive ) == 0 )
226 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
228 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanDirectionFlag" ), Qt::CaseInsensitive ) == 0 )
230 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
232 else if ( requestedAttribute.name().compare( QLatin1String(
"EdgeOfFlightLine" ), Qt::CaseInsensitive ) == 0 )
234 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
236 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanAngleRank" ), Qt::CaseInsensitive ) == 0 )
238 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
240 else if ( requestedAttribute.name().compare( QLatin1String(
"UserData" ), Qt::CaseInsensitive ) == 0 )
242 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
244 else if ( requestedAttribute.name().compare( QLatin1String(
"PointSourceId" ), Qt::CaseInsensitive ) == 0 )
246 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
248 else if ( requestedAttribute.name().compare( QLatin1String(
"GpsTime" ), Qt::CaseInsensitive ) == 0 )
250 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::GpsTime, requestedAttribute.type(), requestedAttribute.size() ) );
252 else if ( requestedAttribute.name().compare( QLatin1String(
"Red" ), Qt::CaseInsensitive ) == 0 )
254 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
256 else if ( requestedAttribute.name().compare( QLatin1String(
"Green" ), Qt::CaseInsensitive ) == 0 )
258 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
260 else if ( requestedAttribute.name().compare( QLatin1String(
"Blue" ), Qt::CaseInsensitive ) == 0 )
262 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
264 else if ( requestedAttribute.name().compare( QLatin1String(
"ScannerChannel" ), Qt::CaseInsensitive ) == 0 )
266 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScannerChannel, requestedAttribute.type(), requestedAttribute.size() ) );
268 else if ( requestedAttribute.name().compare( QLatin1String(
"Synthetic" ), Qt::CaseInsensitive ) == 0 )
270 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Synthetic, requestedAttribute.type(), requestedAttribute.size() ) );
272 else if ( requestedAttribute.name().compare( QLatin1String(
"KeyPoint" ), Qt::CaseInsensitive ) == 0 )
274 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::KeyPoint, requestedAttribute.type(), requestedAttribute.size() ) );
276 else if ( requestedAttribute.name().compare( QLatin1String(
"Withheld" ), Qt::CaseInsensitive ) == 0 )
278 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Withheld, requestedAttribute.type(), requestedAttribute.size() ) );
280 else if ( requestedAttribute.name().compare( QLatin1String(
"Overlap" ), Qt::CaseInsensitive ) == 0 )
282 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Overlap, requestedAttribute.type(), requestedAttribute.size() ) );
284 else if ( requestedAttribute.name().compare( QLatin1String(
"Infrared" ), Qt::CaseInsensitive ) == 0 )
286 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NIR, requestedAttribute.type(), requestedAttribute.size() ) );
290 bool foundAttr =
false;
293 if ( requestedAttribute.name().compare( eba.attribute.trimmed() ) == 0 )
295 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ExtraBytes, eba.type, eba.size, eba.offset ) );
303 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
307 return requestedAttributeDetails;
310bool decodePoint(
char *buf,
int lasPointFormat,
char *dataBuffer, std::size_t &outputOffset, std::vector< QgsLazDecoder::RequestedAttributeDetails > &requestedAttributeDetails )
312 lazperf::las::point10 p10;
313 lazperf::las::gpstime gps;
314 lazperf::las::rgb rgb;
315 lazperf::las::nir14 nir;
316 lazperf::las::point14 p14;
320 const bool isLas14 = ( lasPointFormat == 6 || lasPointFormat == 7 || lasPointFormat == 8 || lasPointFormat == 9 || lasPointFormat == 10 );
322 switch ( lasPointFormat )
330 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
334 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) );
338 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
339 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) +
sizeof( lazperf::las::gpstime ) );
348 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
352 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
353 nir.unpack( buf +
sizeof( lazperf::las::point14 ) +
sizeof( lazperf::las::rgb ) );
361 for (
const QgsLazDecoder::RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
363 switch ( requestedAttribute.attribute )
365 case QgsLazDecoder::LazAttribute::X:
366 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.x() : p10.x );
368 case QgsLazDecoder::LazAttribute::Y:
369 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.y() : p10.y );
371 case QgsLazDecoder::LazAttribute::Z:
372 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.z() : p10.z );
374 case QgsLazDecoder::LazAttribute::Classification:
378 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p14.classification() );
383 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 0 : p10.classification & 0x1F );
387 case QgsLazDecoder::LazAttribute::Intensity:
388 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.intensity() : p10.intensity );
390 case QgsLazDecoder::LazAttribute::ReturnNumber:
391 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.returnNum() : p10.return_number );
393 case QgsLazDecoder::LazAttribute::NumberOfReturns:
394 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.numReturns() : p10.number_of_returns_of_given_pulse );
396 case QgsLazDecoder::LazAttribute::ScanDirectionFlag:
397 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanDirFlag() : p10.scan_direction_flag );
399 case QgsLazDecoder::LazAttribute::EdgeOfFlightLine:
400 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.eofFlag() : p10.edge_of_flight_line );
402 case QgsLazDecoder::LazAttribute::ScanAngleRank:
403 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type,
406 ? p14.scanAngle() * 0.006f
408 : p10.scan_angle_rank );
410 case QgsLazDecoder::LazAttribute::UserData:
411 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
413 case QgsLazDecoder::LazAttribute::PointSourceId:
414 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
416 case QgsLazDecoder::LazAttribute::GpsTime:
418 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type,
419 isLas14 ? p14.gpsTime() : *reinterpret_cast<const double *>( reinterpret_cast<const void *>( &gps.value ) ) );
421 case QgsLazDecoder::LazAttribute::Red:
422 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
424 case QgsLazDecoder::LazAttribute::Green:
425 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
427 case QgsLazDecoder::LazAttribute::Blue:
428 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
430 case QgsLazDecoder::LazAttribute::ScannerChannel:
431 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( p14.scannerChannel() ) : 0 );
433 case QgsLazDecoder::LazAttribute::Synthetic:
434 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 0 ) & 0x01 ) : char( ( p10.classification >> 5 ) & 0x01 ) );
436 case QgsLazDecoder::LazAttribute::KeyPoint:
437 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 1 ) & 0x01 ) : char( ( p10.classification >> 6 ) & 0x01 ) );
439 case QgsLazDecoder::LazAttribute::Withheld:
440 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 2 ) & 0x01 ) : char( ( p10.classification >> 7 ) & 0x01 ) );
442 case QgsLazDecoder::LazAttribute::Overlap:
446 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( ( p14.classFlags() >> 3 ) & 0x01 ) );
451 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 1 : 0 );
455 case QgsLazDecoder::LazAttribute::NIR:
457 if ( lasPointFormat == 8 || lasPointFormat == 10 )
459 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
464 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
469 case QgsLazDecoder::LazAttribute::ExtraBytes:
471 switch ( requestedAttribute.type )
474 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
477 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
480 lazStoreToStream_<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
483 lazStoreToStream_<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
486 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
489 lazStoreToStream_<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
492 lazStoreToStream_<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
495 lazStoreToStream_<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
498 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
501 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
506 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
508 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
512 outputOffset += requestedAttribute.size;
517template<
typename FileType>
531 lazperf::reader::generic_file f( file );
538 int lasPointFormat = f.header().pointFormat();
539 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 &&
540 lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
542 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported" ).arg( lasPointFormat ) );
546 const size_t count = f.header().point_count;
547 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
548 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
550 QByteArray bufArray( f.header().point_record_length, 0 );
551 char *buf = bufArray.data();
553 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
555 data.resize( requestedPointRecordSize * count );
556 char *dataBuffer = data.data();
558 std::size_t outputOffset = 0;
560 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
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( QLatin1String(
"X" ), xAttributeOffset );
582 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), 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( QStringLiteral(
"LAZ-PERF Read through the points in %1 seconds." ).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(
const QString &filename,
637 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
639 std::ifstream file( toNativePath( filename ), std::ios::binary );
641 return decompressLaz_<std::ifstream>( file, requestedAttributes, filterExpression, filterRect );
644std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QByteArray &byteArrayData,
646 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
648 std::istringstream file( byteArrayData.toStdString() );
649 return decompressLaz_<std::istringstream>( file, requestedAttributes, filterExpression, filterRect );
656 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
658 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format" ).arg( lasPointFormat ) );
664 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
666 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
667 QByteArray blockData;
668 blockData.resize( requestedPointRecordSize * pointCount );
669 char *dataBuffer = blockData.data();
671 std::size_t outputOffset = 0;
673 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
674 std::vector< RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
675 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
676 pointCount, requestedAttributes,
680 int skippedPoints = 0;
681 const bool filterIsValid = filterExpression.isValid();
682 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
685 block->setPointCount( 0 );
689 int xAttributeOffset, yAttributeOffset;
692 const bool hasFilterRect = !filterRect.
isEmpty();
695 attributeX = requestedAttributes.
find( QLatin1String(
"X" ), xAttributeOffset );
696 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), yAttributeOffset );
702 for (
int i = 0 ; i < pointCount; ++i )
704 decompressor.decompress( decodedData.get() );
705 char *buf = decodedData.get();
707 bool skipThisPoint = !decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
710 if ( !skipThisPoint && hasFilterRect && attributeX && attributeY )
712 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
713 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
715 skipThisPoint =
true;
717 if ( !skipThisPoint && filterIsValid )
720 double eval = filterExpression.evaluate( i - skippedPoints );
721 if ( !eval || std::isnan( eval ) )
722 skipThisPoint =
true;
727 outputOffset -= requestedPointRecordSize;
732 block->setPointCount( pointCount - skippedPoints );
737std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
739 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
740 return converter.from_bytes( filename.toStdString() );
743std::string QgsLazDecoder::toNativePath(
const QString &filename )
745 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.
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.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
void setYMinimum(double y)
Set the minimum y value.
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
void setXMinimum(double x)
Set the minimum x value.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
bool isEmpty() const
Returns true if the rectangle has no area.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
double y() const
Returns Y coordinate.
double x() const
Returns X coordinate.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)