22 #include "qgsconfig.h"
31 #include <QElapsedTimer>
32 #include <QTemporaryFile>
37 #include "lazperf/las.hpp"
38 #include <lazperf/lazperf.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 );
189 std::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() );
197 if ( requestedAttribute.name().compare( QLatin1String(
"X" ), Qt::CaseInsensitive ) == 0 )
199 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
201 else if ( requestedAttribute.name().compare( QLatin1String(
"Y" ), Qt::CaseInsensitive ) == 0 )
203 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
205 else if ( requestedAttribute.name().compare( QLatin1String(
"Z" ), Qt::CaseInsensitive ) == 0 )
207 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
209 else if ( requestedAttribute.name().compare( QLatin1String(
"Classification" ), Qt::CaseInsensitive ) == 0 )
211 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
213 else if ( requestedAttribute.name().compare( QLatin1String(
"Intensity" ), Qt::CaseInsensitive ) == 0 )
215 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
217 else if ( requestedAttribute.name().compare( QLatin1String(
"ReturnNumber" ), Qt::CaseInsensitive ) == 0 )
219 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
221 else if ( requestedAttribute.name().compare( QLatin1String(
"NumberOfReturns" ), Qt::CaseInsensitive ) == 0 )
223 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
225 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanDirectionFlag" ), Qt::CaseInsensitive ) == 0 )
227 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
229 else if ( requestedAttribute.name().compare( QLatin1String(
"EdgeOfFlightLine" ), Qt::CaseInsensitive ) == 0 )
231 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
233 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanAngleRank" ), Qt::CaseInsensitive ) == 0 )
235 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
237 else if ( requestedAttribute.name().compare( QLatin1String(
"UserData" ), Qt::CaseInsensitive ) == 0 )
239 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
241 else if ( requestedAttribute.name().compare( QLatin1String(
"PointSourceId" ), Qt::CaseInsensitive ) == 0 )
243 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
245 else if ( requestedAttribute.name().compare( QLatin1String(
"GpsTime" ), Qt::CaseInsensitive ) == 0 )
247 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::GpsTime, requestedAttribute.type(), requestedAttribute.size() ) );
249 else if ( requestedAttribute.name().compare( QLatin1String(
"Red" ), Qt::CaseInsensitive ) == 0 )
251 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
253 else if ( requestedAttribute.name().compare( QLatin1String(
"Green" ), Qt::CaseInsensitive ) == 0 )
255 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
257 else if ( requestedAttribute.name().compare( QLatin1String(
"Blue" ), Qt::CaseInsensitive ) == 0 )
259 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
261 else if ( requestedAttribute.name().compare( QLatin1String(
"ScannerChannel" ), Qt::CaseInsensitive ) == 0 )
263 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ScannerChannel, requestedAttribute.type(), requestedAttribute.size() ) );
265 else if ( requestedAttribute.name().compare( QLatin1String(
"ClassificationFlags" ), Qt::CaseInsensitive ) == 0 )
267 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ClassificationFlags, requestedAttribute.type(), requestedAttribute.size() ) );
269 else if ( requestedAttribute.name().compare( QLatin1String(
"Infrared" ), Qt::CaseInsensitive ) == 0 )
271 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::NIR, requestedAttribute.type(), requestedAttribute.size() ) );
275 bool foundAttr =
false;
278 if ( requestedAttribute.name().compare( eba.attribute.trimmed() ) == 0 )
280 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::ExtraBytes, eba.type, eba.size, eba.offset ) );
288 requestedAttributeDetails.emplace_back( QgsLazDecoder::RequestedAttributeDetails( QgsLazDecoder::LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
292 return requestedAttributeDetails;
295 void decodePoint(
char *buf,
int lasPointFormat,
char *dataBuffer, std::size_t &outputOffset, std::vector< QgsLazDecoder::RequestedAttributeDetails > &requestedAttributeDetails )
297 lazperf::las::point10 p10;
298 lazperf::las::gpstime gps;
299 lazperf::las::rgb rgb;
300 lazperf::las::nir14 nir;
301 lazperf::las::point14 p14;
303 bool isLas14 = ( lasPointFormat == 6 || lasPointFormat == 7 || lasPointFormat == 8 );
305 switch ( lasPointFormat )
313 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
317 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) );
321 gps.unpack( buf +
sizeof( lazperf::las::point10 ) );
322 rgb.unpack( buf +
sizeof( lazperf::las::point10 ) +
sizeof( lazperf::las::gpstime ) );
331 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
335 rgb.unpack( buf +
sizeof( lazperf::las::point14 ) );
336 nir.unpack( buf +
sizeof( lazperf::las::point14 ) +
sizeof( lazperf::las::rgb ) );
343 for (
const QgsLazDecoder::RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
345 switch ( requestedAttribute.attribute )
347 case QgsLazDecoder::LazAttribute::X:
348 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.x() : p10.x );
350 case QgsLazDecoder::LazAttribute::Y:
351 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.y() : p10.y );
353 case QgsLazDecoder::LazAttribute::Z:
354 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.z() : p10.z );
356 case QgsLazDecoder::LazAttribute::Classification:
357 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.classification() : p10.classification );
359 case QgsLazDecoder::LazAttribute::Intensity:
360 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.intensity() : p10.intensity );
362 case QgsLazDecoder::LazAttribute::ReturnNumber:
363 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.returnNum() : p10.return_number );
365 case QgsLazDecoder::LazAttribute::NumberOfReturns:
366 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.numReturns() : p10.number_of_returns_of_given_pulse );
368 case QgsLazDecoder::LazAttribute::ScanDirectionFlag:
369 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanDirFlag() : p10.scan_direction_flag );
371 case QgsLazDecoder::LazAttribute::EdgeOfFlightLine:
372 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.eofFlag() : p10.edge_of_flight_line );
374 case QgsLazDecoder::LazAttribute::ScanAngleRank:
375 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.scanAngle() : p10.scan_angle_rank );
377 case QgsLazDecoder::LazAttribute::UserData:
378 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.userData() : p10.user_data );
380 case QgsLazDecoder::LazAttribute::PointSourceId:
381 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, isLas14 ? p14.pointSourceID() : p10.point_source_ID );
383 case QgsLazDecoder::LazAttribute::GpsTime:
385 _lazStoreToStream<double>( dataBuffer, outputOffset, requestedAttribute.type,
386 isLas14 ? p14.gpsTime() : *
reinterpret_cast<const double *
>(
reinterpret_cast<const void *
>( &gps.value ) ) );
388 case QgsLazDecoder::LazAttribute::Red:
389 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
391 case QgsLazDecoder::LazAttribute::Green:
392 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
394 case QgsLazDecoder::LazAttribute::Blue:
395 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
397 case QgsLazDecoder::LazAttribute::ScannerChannel:
398 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, p14.scannerChannel() );
400 case QgsLazDecoder::LazAttribute::ClassificationFlags:
401 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, p14.classFlags() );
403 case QgsLazDecoder::LazAttribute::NIR:
405 if ( lasPointFormat == 8 || lasPointFormat == 10 )
407 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, nir.val );
412 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
417 case QgsLazDecoder::LazAttribute::ExtraBytes:
419 switch ( requestedAttribute.type )
422 _lazStoreToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<char *
>( &buf[requestedAttribute.offset] ) );
425 _lazStoreToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<unsigned char *
>( &buf[requestedAttribute.offset] ) );
428 _lazStoreToStream<qint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint16 *
>( &buf[requestedAttribute.offset] ) );
431 _lazStoreToStream<quint16>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint16 *
>( &buf[requestedAttribute.offset] ) );
434 _lazStoreToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint32 *
>( &buf[requestedAttribute.offset] ) );
437 _lazStoreToStream<quint32>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint32 *
>( &buf[requestedAttribute.offset] ) );
440 _lazStoreToStream<qint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<qint64 *
>( &buf[requestedAttribute.offset] ) );
443 _lazStoreToStream<quint64>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<quint64 *
>( &buf[requestedAttribute.offset] ) );
446 _lazStoreToStream<float>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<float *
>( &buf[requestedAttribute.offset] ) );
449 _lazStoreToStream<double>( dataBuffer, outputOffset, requestedAttribute.type, *
reinterpret_cast<double *
>( &buf[requestedAttribute.offset] ) );
454 case QgsLazDecoder::LazAttribute::MissingOrUnknown:
456 _lazStoreToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
460 outputOffset += requestedAttribute.size;
464 template<
typename FileType>
478 lazperf::reader::generic_file f( file );
485 int lasPointFormat = f.header().pointFormat();
486 if ( lasPointFormat != 0 && lasPointFormat != 1 && lasPointFormat != 2 && lasPointFormat != 3 &&
487 lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
489 QgsDebugMsg( QStringLiteral(
"Unexpected point format record (%1) - only 0, 1, 2, 3, 6, 7, 8 are supported" ).arg( lasPointFormat ) );
493 const size_t count = f.header().point_count;
494 const QgsVector3D scale( f.header().scale.x, f.header().scale.y, f.header().scale.z );
495 const QgsVector3D offset( f.header().offset.x, f.header().offset.y, f.header().offset.z );
497 QByteArray bufArray( f.header().point_record_length, 0 );
498 char *buf = bufArray.data();
500 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
502 data.resize( requestedPointRecordSize * count );
503 char *dataBuffer = data.data();
505 std::size_t outputOffset = 0;
507 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
513 int skippedPoints = 0;
514 const bool filterIsValid = filterExpression.isValid();
515 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
518 block->setPointCount( 0 );
519 return block.release();
522 std::vector<char> rawExtrabytes = f.vlrData(
"LASF_Spec", 4 );
523 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails =
QgsLazInfo::parseExtrabytes( rawExtrabytes.data(), rawExtrabytes.size(), f.header().point_record_length );
524 std::vector< QgsLazDecoder::RequestedAttributeDetails > requestedAttributeDetails = __prepareRequestedAttributeDetails( requestedAttributes, extrabyteAttributesDetails );
526 for (
size_t i = 0 ; i < count ; i ++ )
530 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
536 double eval = filterExpression.evaluate( i - skippedPoints );
537 if ( !eval || std::isnan( eval ) )
540 outputOffset -= requestedPointRecordSize;
547 QgsDebugMsgLevel( QStringLiteral(
"LAZ-PERF Read through the points in %1 seconds." ).arg( t.elapsed() / 1000. ), 2 );
549 block->setPointCount( count - skippedPoints );
550 return block.release();
552 catch ( std::exception &e )
554 QgsDebugMsg(
"Error decompressing laz file: " + QString::fromLatin1( e.what() ) );
561 QgsPointCloudExpression &filterExpression )
563 std::ifstream file( toNativePath( filename ), std::ios::binary );
565 return __decompressLaz<std::ifstream>( file, requestedAttributes, filterExpression );
570 QgsPointCloudExpression &filterExpression )
572 std::istringstream file( byteArrayData.toStdString() );
573 return __decompressLaz<std::istringstream>( file, requestedAttributes, filterExpression );
580 if ( lasPointFormat != 6 && lasPointFormat != 7 && lasPointFormat != 8 )
582 QgsDebugMsg( QStringLiteral(
"Unexpected point format record (%1) - only 6, 7, 8 are supported for COPC format" ).arg( lasPointFormat ) );
588 lazperf::reader::chunk_decompressor decompressor( lasPointFormat, lazInfo.
extrabytesCount(), data.data() );
590 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
591 QByteArray blockData;
592 blockData.resize( requestedPointRecordSize * pointCount );
593 char *dataBuffer = blockData.data();
595 std::size_t outputOffset = 0;
597 QVector<QgsLazInfo::ExtraBytesAttributeDetails> extrabyteAttributesDetails = lazInfo.
extrabytes();
598 std::vector< RequestedAttributeDetails > requestedAttributeDetails = __prepareRequestedAttributeDetails( requestedAttributes, extrabyteAttributesDetails );
599 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
600 pointCount, requestedAttributes,
604 int skippedPoints = 0;
605 const bool filterIsValid = filterExpression.isValid();
606 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
609 block->setPointCount( 0 );
610 return block.release();
613 for (
int i = 0 ; i < pointCount; ++i )
615 decompressor.decompress( decodedData.get() );
616 char *buf = decodedData.get();
618 decodePoint( buf, lasPointFormat, dataBuffer, outputOffset, requestedAttributeDetails );
624 double eval = filterExpression.evaluate( i - skippedPoints );
625 if ( !eval || std::isnan( eval ) )
628 outputOffset -= requestedPointRecordSize;
634 block->setPointCount( pointCount - skippedPoints );
635 return block.release();
638 #if defined(_MSC_VER)
639 std::wstring QgsLazDecoder::toNativePath(
const QString &filename )
641 std::wstring_convert< std::codecvt_utf8_utf16< wchar_t > > converter;
642 return converter.from_bytes( filename.toStdString() );
645 std::string QgsLazDecoder::toNativePath(
const QString &filename )
647 return filename.toStdString();