QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgseptdecoder.cpp
Go to the documentation of this file.
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"
20#include "qgslazdecoder.h"
22#include "qgsvector3d.h"
23#include "qgsconfig.h"
24#include "qgslogger.h"
25
26#include <QFile>
27#include <QDir>
28#include <iostream>
29#include <memory>
30#include <cstring>
31#include <QElapsedTimer>
32#include <QTemporaryFile>
33#include <string>
34
35#include <zstd.h>
36
37#include "lazperf/las.hpp"
38
39
41
42QgsPointCloudBlock *_decompressBinary( const QByteArray &dataUncompressed, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset, QgsPointCloudExpression &filterExpression )
43{
44 const std::size_t pointRecordSize = attributes.pointRecordSize( );
45 const std::size_t requestedPointRecordSize = requestedAttributes.pointRecordSize();
46 const int count = dataUncompressed.size() / pointRecordSize;
47 QByteArray data;
48 data.resize( requestedPointRecordSize * count );
49 char *destinationBuffer = data.data();
50 const char *s = dataUncompressed.data();
51
52 const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.attributes();
53
54 // calculate input attributes and offsets once in advance
55
56 struct AttributeData
57 {
58 AttributeData( int inputOffset, int inputSize, QgsPointCloudAttribute::DataType inputType, int requestedSize, QgsPointCloudAttribute::DataType requestedType )
59 : inputOffset( inputOffset )
60 , inputSize( inputSize )
61 , inputType( inputType )
62 , requestedSize( requestedSize )
63 , requestedType( requestedType )
64 {}
65
66 int inputOffset;
67 int inputSize;
69 int requestedSize;
71 };
72
73 std::vector< AttributeData > attributeData;
74 attributeData.reserve( requestedAttributesVector.size() );
75 for ( const QgsPointCloudAttribute &requestedAttribute : requestedAttributesVector )
76 {
77 int inputAttributeOffset;
78 const QgsPointCloudAttribute *inputAttribute = attributes.find( requestedAttribute.name(), inputAttributeOffset );
79 if ( !inputAttribute )
80 {
81 return nullptr;
82 }
83 attributeData.emplace_back( AttributeData( inputAttributeOffset, inputAttribute->size(), inputAttribute->type(),
84 requestedAttribute.size(), requestedAttribute.type() ) );
85 }
86
87 int skippedPoints = 0;
88 std::unique_ptr< QgsPointCloudBlock > block = std::make_unique< QgsPointCloudBlock >(
89 count,
90 requestedAttributes,
91 data, scale, offset
92 );
93
94 const bool filterIsValid = filterExpression.isValid();
95 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
96 {
97 // skip processing if the expression cannot be prepared
98 block->setPointCount( 0 );
99 return block.release();
100 }
101
102 // now loop through points
103 size_t outputOffset = 0;
104 for ( int i = 0; i < count; ++i )
105 {
106 for ( const AttributeData &attribute : attributeData )
107 {
108 _lazSerialize( destinationBuffer, outputOffset,
109 attribute.requestedType, s,
110 attribute.inputType, attribute.inputSize, i * pointRecordSize + attribute.inputOffset );
111
112 outputOffset += attribute.requestedSize;
113 }
114
115 // check if point needs to be filtered out
116 if ( filterIsValid )
117 {
118 // we're always evaluating the last written point in the buffer
119 double eval = filterExpression.evaluate( i - skippedPoints );
120 if ( !eval || std::isnan( eval ) )
121 {
122 // if the point is filtered out, rewind the offset so the next point is written over it
123 outputOffset -= requestedPointRecordSize;
124 ++skippedPoints;
125 }
126 }
127 }
128 block->setPointCount( count - skippedPoints );
129 return block.release();
130}
131
132QgsPointCloudBlock *QgsEptDecoder::decompressBinary( const QString &filename, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset, QgsPointCloudExpression &filterExpression )
133{
134 if ( ! QFile::exists( filename ) )
135 return nullptr;
136
137 QFile f( filename );
138 const bool r = f.open( QIODevice::ReadOnly );
139 if ( !r )
140 return nullptr;
141
142 const QByteArray dataUncompressed = f.read( f.size() );
143 return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset, filterExpression );
144}
145
146QgsPointCloudBlock *QgsEptDecoder::decompressBinary( const QByteArray &data, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset, QgsPointCloudExpression &filterExpression )
147{
148 return _decompressBinary( data, attributes, requestedAttributes, scale, offset, filterExpression );
149}
150
151/* *************************************************************************************** */
152
153QByteArray decompressZtdStream( const QByteArray &dataCompressed )
154{
155 // NOTE: this is very primitive implementation because we expect the uncompressed
156 // data will be always less than 10 MB
157
158 const int MAXSIZE = 10000000;
159 QByteArray dataUncompressed;
160 dataUncompressed.resize( MAXSIZE );
161
162 ZSTD_DStream *strm = ZSTD_createDStream();
163 ZSTD_initDStream( strm );
164
165 ZSTD_inBuffer m_inBuf;
166 m_inBuf.src = reinterpret_cast<const void *>( dataCompressed.constData() );
167 m_inBuf.size = dataCompressed.size();
168 m_inBuf.pos = 0;
169
170 ZSTD_outBuffer outBuf { reinterpret_cast<void *>( dataUncompressed.data() ), MAXSIZE, 0 };
171 const size_t ret = ZSTD_decompressStream( strm, &outBuf, &m_inBuf );
172 Q_ASSERT( !ZSTD_isError( ret ) );
173 Q_ASSERT( outBuf.pos );
174 Q_ASSERT( outBuf.pos < outBuf.size );
175
176 ZSTD_freeDStream( strm );
177 dataUncompressed.resize( outBuf.pos );
178 return dataUncompressed;
179}
180
181QgsPointCloudBlock *QgsEptDecoder::decompressZStandard( const QString &filename, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset, QgsPointCloudExpression &filterExpression )
182{
183 if ( ! QFile::exists( filename ) )
184 return nullptr;
185
186 QFile f( filename );
187 const bool r = f.open( QIODevice::ReadOnly );
188 if ( !r )
189 return nullptr;
190
191 const QByteArray dataCompressed = f.readAll();
192 const QByteArray dataUncompressed = decompressZtdStream( dataCompressed );
193 return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset, filterExpression );
194}
195
196QgsPointCloudBlock *QgsEptDecoder::decompressZStandard( const QByteArray &data, const QgsPointCloudAttributeCollection &attributes, const QgsPointCloudAttributeCollection &requestedAttributes, const QgsVector3D &scale, const QgsVector3D &offset, QgsPointCloudExpression &filterExpression )
197{
198 const QByteArray dataUncompressed = decompressZtdStream( data );
199 return _decompressBinary( dataUncompressed, attributes, requestedAttributes, scale, offset, filterExpression );
200}
201
202/* *************************************************************************************** */
203
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.
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.