24#include "qgspointcloudexpression.h"
28#include <QElapsedTimer>
29#include <QTemporaryFile>
37#include "lazperf/las.hpp"
38#include "lazperf/readers.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() );
198 if ( requestedAttribute.name().compare( QLatin1String(
"X" ), Qt::CaseInsensitive ) == 0 )
200 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
202 else if ( requestedAttribute.name().compare( QLatin1String(
"Y" ), Qt::CaseInsensitive ) == 0 )
204 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
206 else if ( requestedAttribute.name().compare( QLatin1String(
"Z" ), Qt::CaseInsensitive ) == 0 )
208 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
210 else if ( requestedAttribute.name().compare( QLatin1String(
"Classification" ), Qt::CaseInsensitive ) == 0 )
212 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
214 else if ( requestedAttribute.name().compare( QLatin1String(
"Intensity" ), Qt::CaseInsensitive ) == 0 )
216 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
218 else if ( requestedAttribute.name().compare( QLatin1String(
"ReturnNumber" ), Qt::CaseInsensitive ) == 0 )
220 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
222 else if ( requestedAttribute.name().compare( QLatin1String(
"NumberOfReturns" ), Qt::CaseInsensitive ) == 0 )
224 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
226 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanDirectionFlag" ), Qt::CaseInsensitive ) == 0 )
228 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
230 else if ( requestedAttribute.name().compare( QLatin1String(
"EdgeOfFlightLine" ), Qt::CaseInsensitive ) == 0 )
232 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
234 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanAngleRank" ), Qt::CaseInsensitive ) == 0 )
236 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
238 else if ( requestedAttribute.name().compare( QLatin1String(
"UserData" ), Qt::CaseInsensitive ) == 0 )
240 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
242 else if ( requestedAttribute.name().compare( QLatin1String(
"PointSourceId" ), Qt::CaseInsensitive ) == 0 )
244 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
246 else if ( requestedAttribute.name().compare( QLatin1String(
"GpsTime" ), Qt::CaseInsensitive ) == 0 )
248 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::GpsTime, requestedAttribute.type(), requestedAttribute.size() ) );
250 else if ( requestedAttribute.name().compare( QLatin1String(
"Red" ), Qt::CaseInsensitive ) == 0 )
252 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
254 else if ( requestedAttribute.name().compare( QLatin1String(
"Green" ), Qt::CaseInsensitive ) == 0 )
256 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
258 else if ( requestedAttribute.name().compare( QLatin1String(
"Blue" ), Qt::CaseInsensitive ) == 0 )
260 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
262 else if ( requestedAttribute.name().compare( QLatin1String(
"ScannerChannel" ), Qt::CaseInsensitive ) == 0 )
264 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScannerChannel, requestedAttribute.type(), requestedAttribute.size() ) );
266 else if ( requestedAttribute.name().compare( QLatin1String(
"Synthetic" ), Qt::CaseInsensitive ) == 0 )
268 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Synthetic, requestedAttribute.type(), requestedAttribute.size() ) );
270 else if ( requestedAttribute.name().compare( QLatin1String(
"KeyPoint" ), Qt::CaseInsensitive ) == 0 )
272 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::KeyPoint, requestedAttribute.type(), requestedAttribute.size() ) );
274 else if ( requestedAttribute.name().compare( QLatin1String(
"Withheld" ), Qt::CaseInsensitive ) == 0 )
276 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Withheld, requestedAttribute.type(), requestedAttribute.size() ) );
278 else if ( requestedAttribute.name().compare( QLatin1String(
"Overlap" ), Qt::CaseInsensitive ) == 0 )
280 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Overlap, requestedAttribute.type(), requestedAttribute.size() ) );
282 else if ( requestedAttribute.name().compare( QLatin1String(
"Infrared" ), Qt::CaseInsensitive ) == 0 )
284 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NIR, requestedAttribute.type(), requestedAttribute.size() ) );
288 bool foundAttr =
false;
291 if ( requestedAttribute.name().compare( eba.attribute.trimmed() ) == 0 )
293 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ExtraBytes, eba.type, eba.size, eba.offset ) );
301 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
305 return requestedAttributeDetails;
308void decodePoint(
char *buf,
int lasPointFormat,
char *dataBuffer, std::size_t &outputOffset, std::vector< QgsLazDecoder::RequestedAttributeDetails > &requestedAttributeDetails )
310 lazperf::las::point10 p10;
311 lazperf::las::gpstime gps;
312 lazperf::las::rgb rgb;
313 lazperf::las::nir14 nir;
314 lazperf::las::point14 p14;
316 bool isLas14 = ( lasPointFormat == 6 || lasPointFormat == 7 || lasPointFormat == 8 );
318 switch ( lasPointFormat )
326 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
330 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) );
334 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
335 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) +
sizeof( lazperf::las::gpstime ) );
344 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
348 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
349 nir.unpack( buf +
sizeof( lazperf::las::point14 ) +
sizeof( lazperf::las::rgb ) );
356 for (
const QgsLazDecoder::RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
358 switch ( requestedAttribute.attribute )
360 case QgsLazDecoder::LazAttribute::X:
361 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.x() : p10.x );
363 case QgsLazDecoder::LazAttribute::Y:
364 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.y() : p10.y );
366 case QgsLazDecoder::LazAttribute::Z:
367 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.z() : p10.z );
369 case QgsLazDecoder::LazAttribute::Classification:
373 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p14.classification() );
378 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 0 : p10.classification & 0x1F );
382 case QgsLazDecoder::LazAttribute::Intensity:
383 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.intensity() : p10.intensity );
385 case QgsLazDecoder::LazAttribute::ReturnNumber:
386 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.returnNum() : p10.return_number );
388 case QgsLazDecoder::LazAttribute::NumberOfReturns:
389 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.numReturns() : p10.number_of_returns_of_given_pulse );
391 case QgsLazDecoder::LazAttribute::ScanDirectionFlag:
392 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanDirFlag() : p10.scan_direction_flag );
394 case QgsLazDecoder::LazAttribute::EdgeOfFlightLine:
395 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.eofFlag() : p10.edge_of_flight_line );
397 case QgsLazDecoder::LazAttribute::ScanAngleRank:
398 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( isLas14 ? p14.scanAngle() : p10.scan_angle_rank ) );
400 case QgsLazDecoder::LazAttribute::UserData:
401 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
403 case QgsLazDecoder::LazAttribute::PointSourceId:
404 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
406 case QgsLazDecoder::LazAttribute::GpsTime:
408 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type,
409 isLas14 ? p14.gpsTime() : *
reinterpret_cast<const double *
>(
reinterpret_cast<const void *
>( &gps.value ) ) );
411 case QgsLazDecoder::LazAttribute::Red:
412 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
414 case QgsLazDecoder::LazAttribute::Green:
415 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
417 case QgsLazDecoder::LazAttribute::Blue:
418 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
420 case QgsLazDecoder::LazAttribute::ScannerChannel:
421 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( p14.scannerChannel() ) );
423 case QgsLazDecoder::LazAttribute::Synthetic:
424 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 0 ) & 0x01 ) :
char( ( p10.classification >> 5 ) & 0x01 ) );
426 case QgsLazDecoder::LazAttribute::KeyPoint:
427 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 1 ) & 0x01 ) :
char( ( p10.classification >> 6 ) & 0x01 ) );
429 case QgsLazDecoder::LazAttribute::Withheld:
430 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 2 ) & 0x01 ) :
char( ( p10.classification >> 7 ) & 0x01 ) );
432 case QgsLazDecoder::LazAttribute::Overlap:
436 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( ( p14.classFlags() >> 3 ) & 0x01 ) );
441 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 1 : 0 );
445 case QgsLazDecoder::LazAttribute::NIR:
447 if ( lasPointFormat == 8 || lasPointFormat == 10 )
449 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
454 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
459 case QgsLazDecoder::LazAttribute::ExtraBytes:
461 switch ( requestedAttribute.type )
464 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
467 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
470 lazStoreToStream_<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
473 lazStoreToStream_<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
476 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
479 lazStoreToStream_<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
482 lazStoreToStream_<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
485 lazStoreToStream_<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
488 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
491 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
496 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
498 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
502 outputOffset += requestedAttribute.size;
506template<
typename FileType>
520 lazperf::reader::generic_file f( file );
527 int lasPointFormat = f.header().pointFormat();
528 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 &&
529 lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
531 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported" ).arg( lasPointFormat ) );
535 const size_t count = f.header().point_count;
536 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
537 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
539 QByteArray bufArray( f.header().point_record_length, 0 );
540 char *buf = bufArray.data();
542 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
544 data.resize( requestedPointRecordSize * count );
545 char *dataBuffer = data.data();
547 std::size_t outputOffset = 0;
549 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
555 int skippedPoints = 0;
556 const bool filterIsValid = filterExpression.isValid();
557 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
560 block->setPointCount( 0 );
564 int xAttributeOffset, yAttributeOffset;
567 const bool hasFilterRect = !filterRect.
isEmpty();
570 attributeX = requestedAttributes.
find( QLatin1String(
"X" ), xAttributeOffset );
571 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), yAttributeOffset );
578 std::vector<char> rawExtrabytes = f.vlrData(
"LASF_Spec", 4 );
579 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails =
QgsLazInfo::parseExtrabytes( rawExtrabytes.data(), rawExtrabytes.size(), f.header().point_record_length );
580 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
582 for (
size_t i = 0 ; i < count ; i ++ )
586 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
589 bool skipThisPoint =
false;
590 if ( hasFilterRect && attributeX && attributeY )
592 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
593 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
595 skipThisPoint =
true;
597 if ( !skipThisPoint && filterIsValid )
600 double eval = filterExpression.evaluate( i - skippedPoints );
601 if ( !eval || std::isnan( eval ) )
602 skipThisPoint =
true;
607 outputOffset -= requestedPointRecordSize;
613 QgsDebugMsgLevel( QStringLiteral(
"LAZ-PERF Read through the points in %1 seconds." ).arg( t.elapsed() / 1000. ), 2 );
615 block->setPointCount( count - skippedPoints );
618 catch ( std::exception &e )
620 QgsDebugError(
"Error decompressing laz file: " + QString::fromLatin1( e.what() ) );
625std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QString &filename,
627 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
629 std::ifstream file( toNativePath( filename ), std::ios::binary );
631 return decompressLaz_<std::ifstream>( file, requestedAttributes, filterExpression, filterRect );
634std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QByteArray &byteArrayData,
636 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
638 std::istringstream file( byteArrayData.toStdString() );
639 return decompressLaz_<std::istringstream>( file, requestedAttributes, filterExpression, filterRect );
646 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
648 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format" ).arg( lasPointFormat ) );
654 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
656 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
657 QByteArray blockData;
658 blockData.resize( requestedPointRecordSize * pointCount );
659 char *dataBuffer = blockData.data();
661 std::size_t outputOffset = 0;
663 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
664 std::vector< RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
665 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
666 pointCount, requestedAttributes,
670 int skippedPoints = 0;
671 const bool filterIsValid = filterExpression.isValid();
672 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
675 block->setPointCount( 0 );
679 int xAttributeOffset, yAttributeOffset;
682 const bool hasFilterRect = !filterRect.
isEmpty();
685 attributeX = requestedAttributes.
find( QLatin1String(
"X" ), xAttributeOffset );
686 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), yAttributeOffset );
692 for (
int i = 0 ; i < pointCount; ++i )
694 decompressor.decompress( decodedData.get() );
695 char *buf = decodedData.get();
697 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
700 bool skipThisPoint =
false;
702 if ( hasFilterRect && attributeX && attributeY )
704 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
705 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
707 skipThisPoint =
true;
709 if ( !skipThisPoint && filterIsValid )
712 double eval = filterExpression.evaluate( i - skippedPoints );
713 if ( !eval || std::isnan( eval ) )
714 skipThisPoint =
true;
719 outputOffset -= requestedPointRecordSize;
724 block->setPointCount( pointCount - skippedPoints );
729std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
731 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
732 return converter.from_bytes( filename.toStdString() );
735std::string QgsLazDecoder::toNativePath(
const QString &filename )
737 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)