22 #include "qgsconfig.h"
30 #include <QTemporaryFile>
34 #include "laz-perf/io.hpp"
35 #include "laz-perf/common/common.hpp"
46 const char val = char( value );
52 short val = short( value );
53 memcpy( s + position,
reinterpret_cast<char *
>( &val ),
sizeof(
short ) );
59 unsigned short val =
static_cast< unsigned short>( value );
60 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
unsigned short ) );
66 float val = float( value );
67 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
float ) );
72 qint32 val = qint32( value );
73 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof( qint32 ) );
78 double val = double( value );
79 memcpy( s + position,
reinterpret_cast< char *
>( &val ),
sizeof(
double ) );
90 if ( outputType == inputType )
92 memcpy( data + outputPosition, input + inputPosition, inputSize );
100 const char val = *( input + inputPosition );
101 return _storeToStream<char>( data, outputPosition, outputType, val );
105 const short val = *
reinterpret_cast< const short *
>( input + inputPosition );
106 return _storeToStream<short>( data, outputPosition, outputType, val );
110 const unsigned short val = *
reinterpret_cast< const unsigned short *
>( input + inputPosition );
111 return _storeToStream<unsigned short>( data, outputPosition, outputType, val );
115 const float val = *
reinterpret_cast< const float *
>( input + inputPosition );
116 return _storeToStream<float>( data, outputPosition, outputType, val );
120 const qint32 val = *
reinterpret_cast<const qint32 *
>( input + inputPosition );
121 return _storeToStream<qint32>( data, outputPosition, outputType, val );
125 const double val = *
reinterpret_cast< const double *
>( input + inputPosition );
126 return _storeToStream<double>( data, outputPosition, outputType, val );
137 const std::size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
138 const int count = dataUncompressed.size() / pointRecordSize;
140 data.resize( requestedPointRecordSize * count );
141 char *destinationBuffer = data.data();
142 const char *s = dataUncompressed.data();
144 const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.
attributes();
151 : inputOffset( inputOffset )
152 , inputSize( inputSize )
153 , inputType( inputType )
154 , requestedSize( requestedSize )
155 , requestedType( requestedType )
165 std::vector< AttributeData > attributeData;
166 attributeData.reserve( requestedAttributesVector.size() );
169 int inputAttributeOffset;
171 if ( !inputAttribute )
175 attributeData.emplace_back( AttributeData( inputAttributeOffset, inputAttribute->
size(), inputAttribute->
type(),
176 requestedAttribute.size(), requestedAttribute.type() ) );
180 size_t outputOffset = 0;
181 for (
int i = 0; i < count; ++i )
183 for (
const AttributeData &attribute : attributeData )
185 __serialize( destinationBuffer, outputOffset,
186 attribute.requestedType, s,
187 attribute.inputType, attribute.inputSize, i * pointRecordSize + attribute.inputOffset );
189 outputOffset += attribute.requestedSize;
201 if ( ! QFile::exists( filename ) )
205 const bool r = f.open( QIODevice::ReadOnly );
209 const QByteArray dataUncompressed = f.read( f.size() );
210 return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset );
215 return _decompressBinary( data, attributes, requestedAttributes, scale, offset );
220 QByteArray decompressZtdStream(
const QByteArray &dataCompressed )
225 const int MAXSIZE = 10000000;
226 QByteArray dataUncompressed;
227 dataUncompressed.resize( MAXSIZE );
229 ZSTD_DStream *strm = ZSTD_createDStream();
230 ZSTD_initDStream( strm );
232 ZSTD_inBuffer m_inBuf;
233 m_inBuf.src =
reinterpret_cast<const void *
>( dataCompressed.constData() );
234 m_inBuf.size = dataCompressed.size();
237 ZSTD_outBuffer outBuf {
reinterpret_cast<void *
>( dataUncompressed.data() ), MAXSIZE, 0 };
238 const size_t ret = ZSTD_decompressStream( strm, &outBuf, &m_inBuf );
239 Q_ASSERT( !ZSTD_isError( ret ) );
240 Q_ASSERT( outBuf.pos );
241 Q_ASSERT( outBuf.pos < outBuf.size );
243 ZSTD_freeDStream( strm );
244 dataUncompressed.resize( outBuf.pos );
245 return dataUncompressed;
250 if ( ! QFile::exists( filename ) )
254 const bool r = f.open( QIODevice::ReadOnly );
258 const QByteArray dataCompressed = f.readAll();
259 const QByteArray dataUncompressed = decompressZtdStream( dataCompressed );
260 return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset );
265 const QByteArray dataUncompressed = decompressZtdStream( data );
266 return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset );
271 template<
typename FileType>
274 Q_UNUSED( attributes );
282 const auto start = common::tick();
285 laszip::io::reader::basic_file<FileType> f( file );
287 const size_t count = f.get_header().point_count;
288 const QgsVector3D scale( f.get_header().scale.x, f.get_header().scale.y, f.get_header().scale.z );
289 const QgsVector3D offset( f.get_header().offset.x, f.get_header().offset.y, f.get_header().offset.z );
291 QByteArray bufArray( f.get_header().point_record_length, 0 );
292 char *buf = bufArray.data();
294 const size_t requestedPointRecordSize = requestedAttributes.
pointRecordSize();
296 data.resize( requestedPointRecordSize * count );
297 char *dataBuffer = data.data();
299 const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.
attributes();
301 std::size_t outputOffset = 0;
303 enum class LazAttribute
324 struct RequestedAttributeDetails
327 : attribute( attribute )
332 LazAttribute attribute;
337 std::vector< RequestedAttributeDetails > requestedAttributeDetails;
338 requestedAttributeDetails.reserve( requestedAttributesVector.size() );
341 if ( requestedAttribute.name().compare( QLatin1String(
"X" ), Qt::CaseInsensitive ) == 0 )
343 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
345 else if ( requestedAttribute.name().compare( QLatin1String(
"Y" ), Qt::CaseInsensitive ) == 0 )
347 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
349 else if ( requestedAttribute.name().compare( QLatin1String(
"Z" ), Qt::CaseInsensitive ) == 0 )
351 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
353 else if ( requestedAttribute.name().compare( QLatin1String(
"Classification" ), Qt::CaseInsensitive ) == 0 )
355 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
357 else if ( requestedAttribute.name().compare( QLatin1String(
"Intensity" ), Qt::CaseInsensitive ) == 0 )
359 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
361 else if ( requestedAttribute.name().compare( QLatin1String(
"ReturnNumber" ), Qt::CaseInsensitive ) == 0 )
363 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
365 else if ( requestedAttribute.name().compare( QLatin1String(
"NumberOfReturns" ), Qt::CaseInsensitive ) == 0 )
367 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
369 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanDirectionFlag" ), Qt::CaseInsensitive ) == 0 )
371 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
373 else if ( requestedAttribute.name().compare( QLatin1String(
"EdgeOfFlightLine" ), Qt::CaseInsensitive ) == 0 )
375 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
377 else if ( requestedAttribute.name().compare( QLatin1String(
"ScanAngleRank" ), Qt::CaseInsensitive ) == 0 )
379 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
381 else if ( requestedAttribute.name().compare( QLatin1String(
"UserData" ), Qt::CaseInsensitive ) == 0 )
383 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
385 else if ( requestedAttribute.name().compare( QLatin1String(
"PointSourceId" ), Qt::CaseInsensitive ) == 0 )
387 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
389 else if ( requestedAttribute.name().compare( QLatin1String(
"GpsTime" ), Qt::CaseInsensitive ) == 0 )
391 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::GpsTime, requestedAttribute.type(), requestedAttribute.size() ) );
393 else if ( requestedAttribute.name().compare( QLatin1String(
"Red" ), Qt::CaseInsensitive ) == 0 )
395 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
397 else if ( requestedAttribute.name().compare( QLatin1String(
"Green" ), Qt::CaseInsensitive ) == 0 )
399 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
401 else if ( requestedAttribute.name().compare( QLatin1String(
"Blue" ), Qt::CaseInsensitive ) == 0 )
403 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
408 requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
412 for (
size_t i = 0 ; i < count ; i ++ )
415 const laszip::formats::las::point10 p = laszip::formats::packers<laszip::formats::las::point10>::unpack( buf );
416 const laszip::formats::las::gpstime gps = laszip::formats::packers<laszip::formats::las::gpstime>::unpack( buf +
sizeof( laszip::formats::las::point10 ) );
417 const laszip::formats::las::rgb rgb = laszip::formats::packers<laszip::formats::las::rgb>::unpack( buf +
sizeof( laszip::formats::las::point10 ) +
sizeof( laszip::formats::las::gpstime ) );
419 for (
const RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
421 switch ( requestedAttribute.attribute )
423 case LazAttribute::X:
424 _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.x );
426 case LazAttribute::Y:
427 _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.y );
429 case LazAttribute::Z:
430 _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.z );
432 case LazAttribute::Classification:
433 _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.classification );
435 case LazAttribute::Intensity:
436 _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, p.intensity );
438 case LazAttribute::ReturnNumber:
439 _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.return_number );
441 case LazAttribute::NumberOfReturns:
442 _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.number_of_returns_of_given_pulse );
444 case LazAttribute::ScanDirectionFlag:
445 _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.scan_direction_flag );
447 case LazAttribute::EdgeOfFlightLine:
448 _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.edge_of_flight_line );
450 case LazAttribute::ScanAngleRank:
451 _storeToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, p.scan_angle_rank );
453 case LazAttribute::UserData:
454 _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.user_data );
456 case LazAttribute::PointSourceId:
457 _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, p.point_source_ID );
459 case LazAttribute::GpsTime:
461 _storeToStream<double>( dataBuffer, outputOffset, requestedAttribute.type,
462 *
reinterpret_cast<const double *
>(
reinterpret_cast<const void *
>( &gps.value ) ) );
464 case LazAttribute::Red:
465 _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
467 case LazAttribute::Green:
468 _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
470 case LazAttribute::Blue:
471 _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
473 case LazAttribute::MissingOrUnknown:
475 _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
479 outputOffset += requestedAttribute.size;
484 const float t = common::since( start );
485 QgsDebugMsgLevel( QStringLiteral(
"LAZ-PERF Read through the points in %1 seconds." ).arg( t ), 2 );
500 const QByteArray arr = filename.toUtf8();
501 std::ifstream file( arr.constData(), std::ios::binary );
503 return __decompressLaz<std::ifstream>( file, attributes, requestedAttributes, scale, offset );
511 std::istringstream file( byteArrayData.toStdString() );
512 return __decompressLaz<std::istringstream>( file, attributes, requestedAttributes, scale, offset );
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.
int size() const
Returns size of the attribute in bytes.
DataType type() const
Returns the data type.
Base class for storing raw data from point cloud nodes.
#define QgsDebugMsgLevel(str, level)