Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgspointcloudrenderer.cpp
3 : : --------------------
4 : : begin : October 2020
5 : : copyright : (C) 2020 by Peter Petrik
6 : : email : zilolv at gmail dot com
7 : : ***************************************************************************/
8 : :
9 : : /***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : : #include "qgseptdecoder.h"
19 : : #include "qgseptpointcloudindex.h"
20 : : #include "qgspointcloudattribute.h"
21 : : #include "qgsvector3d.h"
22 : : #include "qgsconfig.h"
23 : : #include "qgslogger.h"
24 : :
25 : : #include <QFile>
26 : : #include <QDir>
27 : : #include <iostream>
28 : : #include <memory>
29 : : #include <cstring>
30 : : #include <QTemporaryFile>
31 : :
32 : : #include <zstd.h>
33 : :
34 : : #include "laz-perf/io.hpp"
35 : : #include "laz-perf/common/common.hpp"
36 : :
37 : : ///@cond PRIVATE
38 : :
39 : : template <typename T>
40 : 0 : bool _storeToStream( char *s, size_t position, QgsPointCloudAttribute::DataType type, T value )
41 : : {
42 : 0 : switch ( type )
43 : : {
44 : : case QgsPointCloudAttribute::Char:
45 : : {
46 : 0 : char val = char( value );
47 : 0 : s[position] = val;
48 : 0 : break;
49 : : }
50 : : case QgsPointCloudAttribute::Short:
51 : : {
52 : 0 : short val = short( value );
53 : 0 : memcpy( s + position, reinterpret_cast<char * >( &val ), sizeof( short ) );
54 : 0 : break;
55 : : }
56 : :
57 : : case QgsPointCloudAttribute::UShort:
58 : : {
59 : 0 : unsigned short val = static_cast< unsigned short>( value );
60 : 0 : memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( unsigned short ) );
61 : 0 : break;
62 : : }
63 : :
64 : : case QgsPointCloudAttribute::Float:
65 : : {
66 : 0 : float val = float( value );
67 : 0 : memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( float ) );
68 : 0 : break;
69 : : }
70 : : case QgsPointCloudAttribute::Int32:
71 : : {
72 : 0 : qint32 val = qint32( value );
73 : 0 : memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( qint32 ) );
74 : 0 : break;
75 : : }
76 : : case QgsPointCloudAttribute::Double:
77 : : {
78 : 0 : double val = double( value );
79 : 0 : memcpy( s + position, reinterpret_cast< char * >( &val ), sizeof( double ) );
80 : 0 : break;
81 : : }
82 : : }
83 : :
84 : 0 : return true;
85 : : }
86 : :
87 : 0 : bool _serialize( char *data, size_t outputPosition, QgsPointCloudAttribute::DataType outputType,
88 : : const char *input, QgsPointCloudAttribute::DataType inputType, int inputSize, size_t inputPosition )
89 : : {
90 : 0 : if ( outputType == inputType )
91 : : {
92 : 0 : memcpy( data + outputPosition, input + inputPosition, inputSize );
93 : 0 : return true;
94 : : }
95 : :
96 : 0 : switch ( inputType )
97 : : {
98 : : case QgsPointCloudAttribute::Char:
99 : : {
100 : 0 : char val = *( input + inputPosition );
101 : 0 : return _storeToStream<char>( data, outputPosition, outputType, val );
102 : : }
103 : : case QgsPointCloudAttribute::Short:
104 : : {
105 : 0 : const short val = *reinterpret_cast< const short * >( input + inputPosition );
106 : 0 : return _storeToStream<short>( data, outputPosition, outputType, val );
107 : : }
108 : : case QgsPointCloudAttribute::UShort:
109 : : {
110 : 0 : const unsigned short val = *reinterpret_cast< const unsigned short * >( input + inputPosition );
111 : 0 : return _storeToStream<unsigned short>( data, outputPosition, outputType, val );
112 : : }
113 : : case QgsPointCloudAttribute::Float:
114 : : {
115 : 0 : const float val = *reinterpret_cast< const float * >( input + inputPosition );
116 : 0 : return _storeToStream<float>( data, outputPosition, outputType, val );
117 : : }
118 : : case QgsPointCloudAttribute::Int32:
119 : : {
120 : 0 : const qint32 val = *reinterpret_cast<const qint32 * >( input + inputPosition );
121 : 0 : return _storeToStream<qint32>( data, outputPosition, outputType, val );
122 : : }
123 : : case QgsPointCloudAttribute::Double:
124 : : {
125 : 0 : const double val = *reinterpret_cast< const double * >( input + inputPosition );
126 : 0 : return _storeToStream<double>( data, outputPosition, outputType, val );
127 : : }
128 : : }
129 : 0 : return true;
130 : 0 : }
131 : :
132 : : // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
133 : :
134 : 0 : QgsPointCloudBlock *_decompressBinary( const QByteArray &dataUncompressed, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes )
135 : : {
136 : 0 : const std::size_t pointRecordSize = attributes.pointRecordSize( );
137 : 0 : const std::size_t requestedPointRecordSize = requestedAttributes.pointRecordSize();
138 : 0 : const int count = dataUncompressed.size() / pointRecordSize;
139 : 0 : QByteArray data;
140 : 0 : data.resize( requestedPointRecordSize * count );
141 : 0 : char *destinationBuffer = data.data();
142 : 0 : const char *s = dataUncompressed.data();
143 : :
144 : 0 : const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.attributes();
145 : :
146 : : // calculate input attributes and offsets once in advance
147 : :
148 : : struct AttributeData
149 : : {
150 : 0 : AttributeData( int inputOffset, int inputSize, QgsPointCloudAttribute::DataType inputType, int requestedSize, QgsPointCloudAttribute::DataType requestedType )
151 : 0 : : inputOffset( inputOffset )
152 : 0 : , inputSize( inputSize )
153 : 0 : , inputType( inputType )
154 : 0 : , requestedSize( requestedSize )
155 : 0 : , requestedType( requestedType )
156 : 0 : {}
157 : :
158 : : int inputOffset;
159 : : int inputSize;
160 : : QgsPointCloudAttribute::DataType inputType;
161 : : int requestedSize;
162 : : QgsPointCloudAttribute::DataType requestedType;
163 : : };
164 : :
165 : 0 : std::vector< AttributeData > attributeData;
166 : 0 : attributeData.reserve( requestedAttributesVector.size() );
167 : 0 : for ( const QgsPointCloudAttribute &requestedAttribute : requestedAttributesVector )
168 : : {
169 : : int inputAttributeOffset;
170 : 0 : const QgsPointCloudAttribute *inputAttribute = attributes.find( requestedAttribute.name(), inputAttributeOffset );
171 : 0 : if ( !inputAttribute )
172 : : {
173 : 0 : return nullptr;
174 : : }
175 : 0 : attributeData.emplace_back( AttributeData( inputAttributeOffset, inputAttribute->size(), inputAttribute->type(),
176 : 0 : requestedAttribute.size(), requestedAttribute.type() ) );
177 : : }
178 : :
179 : : // now loop through points
180 : 0 : size_t outputOffset = 0;
181 : 0 : for ( int i = 0; i < count; ++i )
182 : : {
183 : 0 : for ( const AttributeData &attribute : attributeData )
184 : : {
185 : 0 : _serialize( destinationBuffer, outputOffset,
186 : 0 : attribute.requestedType, s,
187 : 0 : attribute.inputType, attribute.inputSize, i * pointRecordSize + attribute.inputOffset );
188 : :
189 : 0 : outputOffset += attribute.requestedSize;
190 : : }
191 : 0 : }
192 : 0 : return new QgsPointCloudBlock(
193 : 0 : count,
194 : 0 : requestedAttributes,
195 : : data
196 : : );
197 : 0 : }
198 : :
199 : :
200 : 0 : QgsPointCloudBlock *QgsEptDecoder::decompressBinary( const QString &filename, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes )
201 : : {
202 : 0 : if ( ! QFile::exists( filename ) )
203 : 0 : return nullptr;
204 : :
205 : 0 : QFile f( filename );
206 : 0 : bool r = f.open( QIODevice::ReadOnly );
207 : 0 : if ( !r )
208 : 0 : return nullptr;
209 : :
210 : 0 : QByteArray dataUncompressed = f.read( f.size() );
211 : 0 : return _decompressBinary( dataUncompressed, attributes, requestedAttributes );
212 : 0 : }
213 : :
214 : 0 : QgsPointCloudBlock *QgsEptDecoder::decompressBinary( const QByteArray &data, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes )
215 : : {
216 : 0 : return _decompressBinary( data, attributes, requestedAttributes );
217 : : }
218 : :
219 : : /* *************************************************************************************** */
220 : :
221 : 0 : QByteArray decompressZtdStream( const QByteArray &dataCompressed )
222 : : {
223 : : // NOTE: this is very primitive implementation because we expect the uncompressed
224 : : // data will be always less than 10 MB
225 : :
226 : 0 : const int MAXSIZE = 10000000;
227 : 0 : QByteArray dataUncompressed;
228 : 0 : dataUncompressed.resize( MAXSIZE );
229 : :
230 : 0 : ZSTD_DStream *strm = ZSTD_createDStream();
231 : 0 : ZSTD_initDStream( strm );
232 : :
233 : : ZSTD_inBuffer m_inBuf;
234 : 0 : m_inBuf.src = reinterpret_cast<const void *>( dataCompressed.constData() );
235 : 0 : m_inBuf.size = dataCompressed.size();
236 : 0 : m_inBuf.pos = 0;
237 : :
238 : 0 : ZSTD_outBuffer outBuf { reinterpret_cast<void *>( dataUncompressed.data() ), MAXSIZE, 0 };
239 : 0 : size_t ret = ZSTD_decompressStream( strm, &outBuf, &m_inBuf );
240 : : Q_ASSERT( !ZSTD_isError( ret ) );
241 : : Q_ASSERT( outBuf.pos );
242 : : Q_ASSERT( outBuf.pos < outBuf.size );
243 : :
244 : 0 : ZSTD_freeDStream( strm );
245 : 0 : dataUncompressed.resize( outBuf.pos );
246 : 0 : return dataUncompressed;
247 : 0 : }
248 : :
249 : 0 : QgsPointCloudBlock *QgsEptDecoder::decompressZStandard( const QString &filename, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes )
250 : : {
251 : 0 : if ( ! QFile::exists( filename ) )
252 : 0 : return nullptr;
253 : :
254 : 0 : QFile f( filename );
255 : 0 : bool r = f.open( QIODevice::ReadOnly );
256 : 0 : if ( !r )
257 : 0 : return nullptr;
258 : :
259 : 0 : QByteArray dataCompressed = f.readAll();
260 : 0 : QByteArray dataUncompressed = decompressZtdStream( dataCompressed );
261 : 0 : return _decompressBinary( dataUncompressed, attributes, requestedAttributes );
262 : 0 : }
263 : :
264 : 0 : QgsPointCloudBlock *QgsEptDecoder::decompressZStandard( const QByteArray &data, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes )
265 : : {
266 : 0 : QByteArray dataUncompressed = decompressZtdStream( data );
267 : 0 : return _decompressBinary( dataUncompressed, attributes, requestedAttributes );
268 : 0 : }
269 : :
270 : : /* *************************************************************************************** */
271 : :
272 : : template<typename FileType>
273 : 0 : QgsPointCloudBlock *__decompressLaz( FileType &file, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes )
274 : : {
275 : 0 : Q_UNUSED( attributes );
276 : :
277 : 0 : if ( ! file.good() )
278 : 0 : return nullptr;
279 : :
280 : : #ifdef QGISDEBUG
281 : : auto start = common::tick();
282 : : #endif
283 : :
284 : 0 : laszip::io::reader::basic_file<FileType> f( file );
285 : :
286 : 0 : const size_t count = f.get_header().point_count;
287 : : char buf[sizeof( laszip::formats::las::point10 ) + sizeof( laszip::formats::las::gpstime ) + sizeof( laszip::formats::las::rgb ) ]; // a buffer large enough to hold our point
288 : :
289 : 0 : const size_t requestedPointRecordSize = requestedAttributes.pointRecordSize();
290 : 0 : QByteArray data;
291 : 0 : data.resize( requestedPointRecordSize * count );
292 : 0 : char *dataBuffer = data.data();
293 : :
294 : 0 : const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.attributes();
295 : :
296 : 0 : std::size_t outputOffset = 0;
297 : :
298 : : enum class LazAttribute
299 : : {
300 : : X,
301 : : Y,
302 : : Z,
303 : : Classification,
304 : : Intensity,
305 : : ReturnNumber,
306 : : NumberOfReturns,
307 : : ScanDirectionFlag,
308 : : EdgeOfFlightLine,
309 : : ScanAngleRank,
310 : : UserData,
311 : : PointSourceId,
312 : : Red,
313 : : Green,
314 : : Blue,
315 : : MissingOrUnknown
316 : : };
317 : :
318 : : struct RequestedAttributeDetails
319 : : {
320 : 0 : RequestedAttributeDetails( LazAttribute attribute, QgsPointCloudAttribute::DataType type, int size )
321 : 0 : : attribute( attribute )
322 : 0 : , type( type )
323 : 0 : , size( size )
324 : 0 : {}
325 : :
326 : : LazAttribute attribute;
327 : : QgsPointCloudAttribute::DataType type;
328 : : int size;
329 : : };
330 : :
331 : 0 : std::vector< RequestedAttributeDetails > requestedAttributeDetails;
332 : 0 : requestedAttributeDetails.reserve( requestedAttributesVector.size() );
333 : 0 : for ( const QgsPointCloudAttribute &requestedAttribute : requestedAttributesVector )
334 : : {
335 : 0 : if ( requestedAttribute.name().compare( QLatin1String( "X" ), Qt::CaseInsensitive ) == 0 )
336 : : {
337 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::X, requestedAttribute.type(), requestedAttribute.size() ) );
338 : 0 : }
339 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "Y" ), Qt::CaseInsensitive ) == 0 )
340 : : {
341 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Y, requestedAttribute.type(), requestedAttribute.size() ) );
342 : 0 : }
343 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) == 0 )
344 : : {
345 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Z, requestedAttribute.type(), requestedAttribute.size() ) );
346 : 0 : }
347 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "Classification" ), Qt::CaseInsensitive ) == 0 )
348 : : {
349 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Classification, requestedAttribute.type(), requestedAttribute.size() ) );
350 : 0 : }
351 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "Intensity" ), Qt::CaseInsensitive ) == 0 )
352 : : {
353 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Intensity, requestedAttribute.type(), requestedAttribute.size() ) );
354 : 0 : }
355 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "ReturnNumber" ), Qt::CaseInsensitive ) == 0 )
356 : : {
357 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ReturnNumber, requestedAttribute.type(), requestedAttribute.size() ) );
358 : 0 : }
359 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "NumberOfReturns" ), Qt::CaseInsensitive ) == 0 )
360 : : {
361 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::NumberOfReturns, requestedAttribute.type(), requestedAttribute.size() ) );
362 : 0 : }
363 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "ScanDirectionFlag" ), Qt::CaseInsensitive ) == 0 )
364 : : {
365 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ScanDirectionFlag, requestedAttribute.type(), requestedAttribute.size() ) );
366 : 0 : }
367 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "EdgeOfFlightLine" ), Qt::CaseInsensitive ) == 0 )
368 : : {
369 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::EdgeOfFlightLine, requestedAttribute.type(), requestedAttribute.size() ) );
370 : 0 : }
371 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "ScanAngleRank" ), Qt::CaseInsensitive ) == 0 )
372 : : {
373 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::ScanAngleRank, requestedAttribute.type(), requestedAttribute.size() ) );
374 : 0 : }
375 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "UserData" ), Qt::CaseInsensitive ) == 0 )
376 : : {
377 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::UserData, requestedAttribute.type(), requestedAttribute.size() ) );
378 : 0 : }
379 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "PointSourceId" ), Qt::CaseInsensitive ) == 0 )
380 : : {
381 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::PointSourceId, requestedAttribute.type(), requestedAttribute.size() ) );
382 : 0 : }
383 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "Red" ), Qt::CaseInsensitive ) == 0 )
384 : : {
385 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Red, requestedAttribute.type(), requestedAttribute.size() ) );
386 : 0 : }
387 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "Green" ), Qt::CaseInsensitive ) == 0 )
388 : : {
389 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Green, requestedAttribute.type(), requestedAttribute.size() ) );
390 : 0 : }
391 : 0 : else if ( requestedAttribute.name().compare( QLatin1String( "Blue" ), Qt::CaseInsensitive ) == 0 )
392 : : {
393 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::Blue, requestedAttribute.type(), requestedAttribute.size() ) );
394 : 0 : }
395 : : else
396 : : {
397 : : // this can possibly happen -- e.g. if a style built using a different point cloud format references an attribute which isn't available from the laz file
398 : 0 : requestedAttributeDetails.emplace_back( RequestedAttributeDetails( LazAttribute::MissingOrUnknown, requestedAttribute.type(), requestedAttribute.size() ) );
399 : : }
400 : : }
401 : :
402 : 0 : for ( size_t i = 0 ; i < count ; i ++ )
403 : : {
404 : 0 : f.readPoint( buf ); // read the point out
405 : 0 : laszip::formats::las::point10 p = laszip::formats::packers<laszip::formats::las::point10>::unpack( buf );
406 : 0 : laszip::formats::las::rgb rgb = laszip::formats::packers<laszip::formats::las::rgb>::unpack( buf + sizeof( laszip::formats::las::point10 ) + sizeof( laszip::formats::las::gpstime ) );
407 : :
408 : 0 : for ( const RequestedAttributeDetails &requestedAttribute : requestedAttributeDetails )
409 : : {
410 : 0 : switch ( requestedAttribute.attribute )
411 : : {
412 : : case LazAttribute::X:
413 : 0 : _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.x );
414 : 0 : break;
415 : : case LazAttribute::Y:
416 : 0 : _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.y );
417 : 0 : break;
418 : : case LazAttribute::Z:
419 : 0 : _storeToStream<qint32>( dataBuffer, outputOffset, requestedAttribute.type, p.z );
420 : 0 : break;
421 : : case LazAttribute::Classification:
422 : 0 : _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.classification );
423 : 0 : break;
424 : : case LazAttribute::Intensity:
425 : 0 : _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, p.intensity );
426 : 0 : break;
427 : : case LazAttribute::ReturnNumber:
428 : 0 : _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.return_number );
429 : 0 : break;
430 : : case LazAttribute::NumberOfReturns:
431 : 0 : _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.number_of_returns_of_given_pulse );
432 : 0 : break;
433 : : case LazAttribute::ScanDirectionFlag:
434 : 0 : _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.scan_direction_flag );
435 : 0 : break;
436 : : case LazAttribute::EdgeOfFlightLine:
437 : 0 : _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.edge_of_flight_line );
438 : 0 : break;
439 : : case LazAttribute::ScanAngleRank:
440 : 0 : _storeToStream<char>( dataBuffer, outputOffset, requestedAttribute.type, p.scan_angle_rank );
441 : 0 : break;
442 : : case LazAttribute::UserData:
443 : 0 : _storeToStream<unsigned char>( dataBuffer, outputOffset, requestedAttribute.type, p.user_data );
444 : 0 : break;
445 : : case LazAttribute::PointSourceId:
446 : 0 : _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, p.point_source_ID );
447 : 0 : break;
448 : : case LazAttribute::Red:
449 : 0 : _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.r );
450 : 0 : break;
451 : : case LazAttribute::Green:
452 : 0 : _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.g );
453 : 0 : break;
454 : : case LazAttribute::Blue:
455 : 0 : _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, rgb.b );
456 : 0 : break;
457 : : case LazAttribute::MissingOrUnknown:
458 : : // just store 0 for unknown/missing attributes
459 : 0 : _storeToStream<unsigned short>( dataBuffer, outputOffset, requestedAttribute.type, 0 );
460 : 0 : break;
461 : : }
462 : :
463 : 0 : outputOffset += requestedAttribute.size;
464 : : }
465 : 0 : }
466 : :
467 : : #ifdef QGISDEBUG
468 : : float t = common::since( start );
469 : : QgsDebugMsgLevel( QStringLiteral( "LAZ-PERF Read through the points in %1 seconds." ).arg( t ), 2 );
470 : : #endif
471 : :
472 : 0 : return new QgsPointCloudBlock(
473 : 0 : count,
474 : 0 : requestedAttributes,
475 : : data
476 : : );
477 : 0 : }
478 : :
479 : 0 : QgsPointCloudBlock *QgsEptDecoder::decompressLaz( const QString &filename,
480 : : const QgsPointCloudAttributeCollection &attributes,
481 : : const QgsPointCloudAttributeCollection &requestedAttributes )
482 : : {
483 : 0 : const QByteArray arr = filename.toUtf8();
484 : 0 : std::ifstream file( arr.constData(), std::ios::binary );
485 : :
486 : 0 : return __decompressLaz<std::ifstream>( file, attributes, requestedAttributes );
487 : 0 : }
488 : :
489 : 0 : QgsPointCloudBlock *QgsEptDecoder::decompressLaz( const QByteArray &byteArrayData,
490 : : const QgsPointCloudAttributeCollection &attributes,
491 : : const QgsPointCloudAttributeCollection &requestedAttributes )
492 : : {
493 : 0 : std::istringstream file( byteArrayData.toStdString() );
494 : 0 : return __decompressLaz<std::istringstream>( file, attributes, requestedAttributes );
495 : 0 : }
496 : :
497 : : ///@endcond
|