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;
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 ) );
360 for (
const QgsLazDecoder::RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
362 switch ( requestedAttribute.attribute )
364 case QgsLazDecoder::LazAttribute::X:
365 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.x() : p10.x );
367 case QgsLazDecoder::LazAttribute::Y:
368 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.y() : p10.y );
370 case QgsLazDecoder::LazAttribute::Z:
371 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.z() : p10.z );
373 case QgsLazDecoder::LazAttribute::Classification:
377 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p14.classification() );
382 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 0 : p10.classification & 0x1F );
386 case QgsLazDecoder::LazAttribute::Intensity:
387 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.intensity() : p10.intensity );
389 case QgsLazDecoder::LazAttribute::ReturnNumber:
390 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.returnNum() : p10.return_number );
392 case QgsLazDecoder::LazAttribute::NumberOfReturns:
393 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.numReturns() : p10.number_of_returns_of_given_pulse );
395 case QgsLazDecoder::LazAttribute::ScanDirectionFlag:
396 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanDirFlag() : p10.scan_direction_flag );
398 case QgsLazDecoder::LazAttribute::EdgeOfFlightLine:
399 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.eofFlag() : p10.edge_of_flight_line );
401 case QgsLazDecoder::LazAttribute::ScanAngleRank:
402 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type,
405 ? p14.scanAngle() * 0.006f
407 : p10.scan_angle_rank );
409 case QgsLazDecoder::LazAttribute::UserData:
410 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
412 case QgsLazDecoder::LazAttribute::PointSourceId:
413 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
415 case QgsLazDecoder::LazAttribute::GpsTime:
417 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type,
418 isLas14 ? p14.gpsTime() : *reinterpret_cast<const double *>( reinterpret_cast<const void *>( &gps.value ) ) );
420 case QgsLazDecoder::LazAttribute::Red:
421 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
423 case QgsLazDecoder::LazAttribute::Green:
424 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
426 case QgsLazDecoder::LazAttribute::Blue:
427 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
429 case QgsLazDecoder::LazAttribute::ScannerChannel:
430 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( p14.scannerChannel() ) );
432 case QgsLazDecoder::LazAttribute::Synthetic:
433 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 0 ) & 0x01 ) : char( ( p10.classification >> 5 ) & 0x01 ) );
435 case QgsLazDecoder::LazAttribute::KeyPoint:
436 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 1 ) & 0x01 ) : char( ( p10.classification >> 6 ) & 0x01 ) );
438 case QgsLazDecoder::LazAttribute::Withheld:
439 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ?
char( ( p14.classFlags() >> 2 ) & 0x01 ) : char( ( p10.classification >> 7 ) & 0x01 ) );
441 case QgsLazDecoder::LazAttribute::Overlap:
445 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type,
char( ( p14.classFlags() >> 3 ) & 0x01 ) );
450 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, ( p10.classification & 0x1F ) == 12 ? 1 : 0 );
454 case QgsLazDecoder::LazAttribute::NIR:
456 if ( lasPointFormat == 8 || lasPointFormat == 10 )
458 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
463 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
468 case QgsLazDecoder::LazAttribute::ExtraBytes:
470 switch ( requestedAttribute.type )
473 lazStoreToStream_<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
476 lazStoreToStream_<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
479 lazStoreToStream_<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
482 lazStoreToStream_<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
485 lazStoreToStream_<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
488 lazStoreToStream_<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
491 lazStoreToStream_<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
494 lazStoreToStream_<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
497 lazStoreToStream_<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
500 lazStoreToStream_<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
505 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
507 lazStoreToStream_<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
511 outputOffset += requestedAttribute.size;
515template<
typename FileType>
529 lazperf::reader::generic_file f( file );
536 int lasPointFormat = f.header().pointFormat();
537 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 &&
538 lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
540 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported" ).arg( lasPointFormat ) );
544 const size_t count = f.header().point_count;
545 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
546 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
548 QByteArray bufArray( f.header().point_record_length, 0 );
549 char *buf = bufArray.data();
551 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
553 data.resize( requestedPointRecordSize * count );
554 char *dataBuffer = data.data();
556 std::size_t outputOffset = 0;
558 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
564 int skippedPoints = 0;
565 const bool filterIsValid = filterExpression.isValid();
566 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
569 block->setPointCount( 0 );
573 int xAttributeOffset, yAttributeOffset;
576 const bool hasFilterRect = !filterRect.
isEmpty();
579 attributeX = requestedAttributes.
find( QLatin1String(
"X" ), xAttributeOffset );
580 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), yAttributeOffset );
587 std::vector<char> rawExtrabytes = f.vlrData(
"LASF_Spec", 4 );
588 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails =
QgsLazInfo::parseExtrabytes( rawExtrabytes.data(), rawExtrabytes.size(), f.header().point_record_length );
589 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
591 for (
size_t i = 0 ; i < count ; i ++ )
595 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
598 bool skipThisPoint =
false;
599 if ( hasFilterRect && attributeX && attributeY )
601 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
602 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
604 skipThisPoint =
true;
606 if ( !skipThisPoint && filterIsValid )
609 double eval = filterExpression.evaluate( i - skippedPoints );
610 if ( !eval || std::isnan( eval ) )
611 skipThisPoint =
true;
616 outputOffset -= requestedPointRecordSize;
622 QgsDebugMsgLevel( QStringLiteral(
"LAZ-PERF Read through the points in %1 seconds." ).arg( t.elapsed() / 1000. ), 2 );
624 block->setPointCount( count - skippedPoints );
627 catch ( std::exception &e )
629 QgsDebugError(
"Error decompressing laz file: " + QString::fromLatin1( e.what() ) );
634std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QString &filename,
636 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
638 std::ifstream file( toNativePath( filename ), std::ios::binary );
640 return decompressLaz_<std::ifstream>( file, requestedAttributes, filterExpression, filterRect );
643std::unique_ptr<QgsPointCloudBlock> QgsLazDecoder::decompressLaz(
const QByteArray &byteArrayData,
645 QgsPointCloudExpression &filterExpression,
QgsRectangle &filterRect )
647 std::istringstream file( byteArrayData.toStdString() );
648 return decompressLaz_<std::istringstream>( file, requestedAttributes, filterExpression, filterRect );
655 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
657 QgsDebugError( QStringLiteral(
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format" ).arg( lasPointFormat ) );
663 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
665 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
666 QByteArray blockData;
667 blockData.resize( requestedPointRecordSize * pointCount );
668 char *dataBuffer = blockData.data();
670 std::size_t outputOffset = 0;
672 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
673 std::vector< RequestedAttributeDetails > requestedAttributeDetails = prepareRequestedAttributeDetails_( requestedAttributes, extrabyteAttributesDetails );
674 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
675 pointCount, requestedAttributes,
679 int skippedPoints = 0;
680 const bool filterIsValid = filterExpression.isValid();
681 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
684 block->setPointCount( 0 );
688 int xAttributeOffset, yAttributeOffset;
691 const bool hasFilterRect = !filterRect.
isEmpty();
694 attributeX = requestedAttributes.
find( QLatin1String(
"X" ), xAttributeOffset );
695 attributeY = requestedAttributes.
find( QLatin1String(
"Y" ), yAttributeOffset );
701 for (
int i = 0 ; i < pointCount; ++i )
703 decompressor.decompress( decodedData.get() );
704 char *buf = decodedData.get();
706 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
709 bool skipThisPoint =
false;
711 if ( hasFilterRect && attributeX && attributeY )
713 const double x = attributeX->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
714 const double y = attributeY->
convertValueToDouble( dataBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
716 skipThisPoint =
true;
718 if ( !skipThisPoint && filterIsValid )
721 double eval = filterExpression.evaluate( i - skippedPoints );
722 if ( !eval || std::isnan( eval ) )
723 skipThisPoint =
true;
728 outputOffset -= requestedPointRecordSize;
733 block->setPointCount( pointCount - skippedPoints );
738std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
740 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
741 return converter.from_bytes( filename.toStdString() );
744std::string QgsLazDecoder::toNativePath(
const QString &filename )
746 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)