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;
310void 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;
318 bool isLas14 = ( lasPointFormat == 6 || lasPointFormat == 7 || lasPointFormat == 8 );
320 switch ( lasPointFormat )
328 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
332 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) );
336 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
337 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) +
sizeof( lazperf::las::gpstime ) );
346 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
350 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
351 nir.unpack( buf +
sizeof( lazperf::las::point14 ) +
sizeof( lazperf::las::rgb ) );
358 for (
const QgsLazDecoder::RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
360 switch ( requestedAttribute.attribute )
362 case QgsLazDecoder::LazAttribute::X:
363 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.x() : p10.x );
365 case QgsLazDecoder::LazAttribute::Y:
366 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.y() : p10.y );
368 case QgsLazDecoder::LazAttribute::Z:
369 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.z() : p10.z );
371 case QgsLazDecoder::LazAttribute::Classification:
375 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p14.classification() );
380 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 0 : p10.classification & 0x1F );
384 case QgsLazDecoder::LazAttribute::Intensity:
385 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.intensity() : p10.intensity );
387 case QgsLazDecoder::LazAttribute::ReturnNumber:
388 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.returnNum() : p10.return_number );
390 case QgsLazDecoder::LazAttribute::NumberOfReturns:
391 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.numReturns() : p10.number_of_returns_of_given_pulse );
393 case QgsLazDecoder::LazAttribute::ScanDirectionFlag:
394 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanDirFlag() : p10.scan_direction_flag );
396 case QgsLazDecoder::LazAttribute::EdgeOfFlightLine:
397 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.eofFlag() : p10.edge_of_flight_line );
399 case QgsLazDecoder::LazAttribute::ScanAngleRank:
400 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( isLas14 ? p14.scanAngle() : p10.scan_angle_rank ) );
402 case QgsLazDecoder::LazAttribute::UserData:
403 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
405 case QgsLazDecoder::LazAttribute::PointSourceId:
406 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
408 case QgsLazDecoder::LazAttribute::GpsTime:
410 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type,
411 isLas14 ? p14.gpsTime() : *reinterpret_cast<const double *>( reinterpret_cast<const void *>( &gps.value ) ) );
413 case QgsLazDecoder::LazAttribute::Red:
414 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
416 case QgsLazDecoder::LazAttribute::Green:
417 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
419 case QgsLazDecoder::LazAttribute::Blue:
420 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
422 case QgsLazDecoder::LazAttribute::ScannerChannel:
423 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( p14.scannerChannel() ) );
425 case QgsLazDecoder::LazAttribute::Synthetic:
426 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 0 ) & 0x01 ) : char( ( p10.classification >> 5 ) & 0x01 ) );
428 case QgsLazDecoder::LazAttribute::KeyPoint:
429 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 1 ) & 0x01 ) : char( ( p10.classification >> 6 ) & 0x01 ) );
431 case QgsLazDecoder::LazAttribute::Withheld:
432 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 2 ) & 0x01 ) : char( ( p10.classification >> 7 ) & 0x01 ) );
434 case QgsLazDecoder::LazAttribute::Overlap:
438 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( ( p14.classFlags() >> 3 ) & 0x01 ) );
443 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 1 : 0 );
447 case QgsLazDecoder::LazAttribute::NIR:
449 if ( lasPointFormat == 8 || lasPointFormat == 10 )
451 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
456 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
461 case QgsLazDecoder::LazAttribute::ExtraBytes:
463 switch ( requestedAttribute.type )
466 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
469 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
472 lazStoreToStream_<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
475 lazStoreToStream_<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
478 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
481 lazStoreToStream_<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
484 lazStoreToStream_<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
487 lazStoreToStream_<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
490 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
493 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
498 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
500 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
504 outputOffset += requestedAttribute.size;
508template<
typename FileType>
522 lazperf::reader::generic_file f( file );
529 int lasPointFormat = f.header().pointFormat();
530 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 &&
531 lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
533 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported" ).arg( lasPointFormat ) );
537 const size_t count = f.header().point_count;
538 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
539 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
541 QByteArray bufArray( f.header().point_record_length, 0 );
542 char *buf = bufArray.data();
544 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
546 data.resize( requestedPointRecordSize * count );
547 char *dataBuffer = data.data();
549 std::size_t outputOffset = 0;
551 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
557 int skippedPoints = 0;
558 const bool filterIsValid = filterExpression.isValid();
559 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
562 block->setPointCount( 0 );
566 int xAttributeOffset, yAttributeOffset;
569 const bool hasFilterRect = !filterRect.
isEmpty();
572 attributeX = requestedAttributes.
find( QLatin1String(
"X" ), xAttributeOffset );
573 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), yAttributeOffset );
580 std::vector<char> rawExtrabytes = f.vlrData(
"LASF_Spec", 4 );
581 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails =
QgsLazInfo::parseExtrabytes( rawExtrabytes.data(), rawExtrabytes.size(), f.header().point_record_length );
582 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
584 for (
size_t i = 0 ; i < count ; i ++ )
588 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
591 bool skipThisPoint =
false;
592 if ( hasFilterRect && attributeX && attributeY )
594 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
595 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
597 skipThisPoint =
true;
599 if ( !skipThisPoint && filterIsValid )
602 double eval = filterExpression.evaluate( i - skippedPoints );
603 if ( !eval || std::isnan( eval ) )
604 skipThisPoint =
true;
609 outputOffset -= requestedPointRecordSize;
615 QgsDebugMsgLevel( QStringLiteral(
"LAZ-PERF Read through the points in %1 seconds." ).arg( t.elapsed() / 1000. ), 2 );
617 block->setPointCount( count - skippedPoints );
620 catch ( std::exception &e )
622 QgsDebugError(
"Error decompressing laz file: " + QString::fromLatin1( e.what() ) );
627std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QString &filename,
629 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
631 std::ifstream file( toNativePath( filename ), std::ios::binary );
633 return decompressLaz_<std::ifstream>( file, requestedAttributes, filterExpression, filterRect );
636std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QByteArray &byteArrayData,
638 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
640 std::istringstream file( byteArrayData.toStdString() );
641 return decompressLaz_<std::istringstream>( file, requestedAttributes, filterExpression, filterRect );
648 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
650 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format" ).arg( lasPointFormat ) );
656 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
658 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
659 QByteArray blockData;
660 blockData.resize( requestedPointRecordSize * pointCount );
661 char *dataBuffer = blockData.data();
663 std::size_t outputOffset = 0;
665 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
666 std::vector< RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
667 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
668 pointCount, requestedAttributes,
672 int skippedPoints = 0;
673 const bool filterIsValid = filterExpression.isValid();
674 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
677 block->setPointCount( 0 );
681 int xAttributeOffset, yAttributeOffset;
684 const bool hasFilterRect = !filterRect.
isEmpty();
687 attributeX = requestedAttributes.
find( QLatin1String(
"X" ), xAttributeOffset );
688 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), yAttributeOffset );
694 for (
int i = 0 ; i < pointCount; ++i )
696 decompressor.decompress( decodedData.get() );
697 char *buf = decodedData.get();
699 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
702 bool skipThisPoint =
false;
704 if ( hasFilterRect && attributeX && attributeY )
706 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
707 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
709 skipThisPoint =
true;
711 if ( !skipThisPoint && filterIsValid )
714 double eval = filterExpression.evaluate( i - skippedPoints );
715 if ( !eval || std::isnan( eval ) )
716 skipThisPoint =
true;
721 outputOffset -= requestedPointRecordSize;
726 block->setPointCount( pointCount - skippedPoints );
731std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
733 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
734 return converter.from_bytes( filename.toStdString() );
737std::string QgsLazDecoder::toNativePath(
const QString &filename )
739 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)