QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
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"
19
20#include <zstd.h>
21
22#include "qgslazdecoder.h"
24#include "qgspointcloudexpression.h"
25#include "qgsrectangle.h"
26#include "qgsvector3d.h"
27
28#include <QFile>
29#include <QString>
30
31using namespace Qt::StringLiterals;
32
34
35std::unique_ptr<QgsPointCloudBlock> decompressBinary_(
36 const QByteArray &dataUncompressed,
37 const QgsPointCloudAttributeCollection &attributes,
38 const QgsPointCloudAttributeCollection &requestedAttributes,
39 const QgsVector3D &scale,
40 const QgsVector3D &offset,
41 QgsPointCloudExpression &filterExpression,
42 QgsRectangle &filterRect
43)
44{
45 const std::size_t pointRecordSize = attributes.pointRecordSize();
46 const std::size_t requestedPointRecordSize = requestedAttributes.pointRecordSize();
47 const int count = dataUncompressed.size() / pointRecordSize;
48 QByteArray data;
49 data.resize( requestedPointRecordSize * count );
50 char *destinationBuffer = data.data();
51 const char *s = dataUncompressed.data();
52
53 const QVector<QgsPointCloudAttribute> requestedAttributesVector = requestedAttributes.attributes();
54
55 // calculate input attributes and offsets once in advance
56
57 struct AttributeData
58 {
59 AttributeData( int inputOffset, int inputSize, QgsPointCloudAttribute::DataType inputType, int requestedSize, QgsPointCloudAttribute::DataType requestedType )
60 : inputOffset( inputOffset )
61 , inputSize( inputSize )
62 , inputType( inputType )
63 , requestedSize( requestedSize )
64 , requestedType( requestedType )
65 {}
66
67 int inputOffset;
68 int inputSize;
70 int requestedSize;
72 };
73
74 std::vector< AttributeData > attributeData;
75 attributeData.reserve( requestedAttributesVector.size() );
76 for ( const QgsPointCloudAttribute &requestedAttribute : requestedAttributesVector )
77 {
78 int inputAttributeOffset;
79 const QgsPointCloudAttribute *inputAttribute = attributes.find( requestedAttribute.name(), inputAttributeOffset );
80 if ( !inputAttribute )
81 {
82 return nullptr;
83 }
84 attributeData.emplace_back( AttributeData( inputAttributeOffset, inputAttribute->size(), inputAttribute->type(), requestedAttribute.size(), requestedAttribute.type() ) );
85 }
86
87 int skippedPoints = 0;
88 auto block = std::make_unique< QgsPointCloudBlock >( count, requestedAttributes, data, scale, offset );
89
90 const bool filterIsValid = filterExpression.isValid();
91 if ( !filterExpression.prepare( block.get() ) && filterIsValid )
92 {
93 // skip processing if the expression cannot be prepared
94 block->setPointCount( 0 );
95 return block;
96 }
97
98 int xAttributeOffset, yAttributeOffset;
99 const QgsPointCloudAttribute *attributeX = nullptr;
100 const QgsPointCloudAttribute *attributeY = nullptr;
101 const bool hasFilterRect = !filterRect.isEmpty();
102 if ( hasFilterRect )
103 {
104 attributeX = requestedAttributes.find( "X"_L1, xAttributeOffset );
105 attributeY = requestedAttributes.find( "Y"_L1, yAttributeOffset );
106 filterRect.setXMinimum( ( filterRect.xMinimum() - offset.x() ) / scale.x() );
107 filterRect.setXMaximum( ( filterRect.xMaximum() - offset.x() ) / scale.x() );
108 filterRect.setYMinimum( ( filterRect.yMinimum() - offset.y() ) / scale.y() );
109 filterRect.setYMaximum( ( filterRect.yMaximum() - offset.y() ) / scale.y() );
110 }
111
112 // now loop through points
113 size_t outputOffset = 0;
114 for ( int i = 0; i < count; ++i )
115 {
116 for ( const AttributeData &attribute : attributeData )
117 {
118 lazSerialize_( destinationBuffer, outputOffset, attribute.requestedType, s, attribute.inputType, attribute.inputSize, i * pointRecordSize + attribute.inputOffset );
119
120 outputOffset += attribute.requestedSize;
121 }
122
123 // check if point needs to be filtered out
124 bool skipThisPoint = false;
125 if ( hasFilterRect && attributeX && attributeY )
126 {
127 const double x = attributeX->convertValueToDouble( destinationBuffer + outputOffset - requestedPointRecordSize + xAttributeOffset );
128 const double y = attributeY->convertValueToDouble( destinationBuffer + outputOffset - requestedPointRecordSize + yAttributeOffset );
129 if ( !filterRect.contains( x, y ) )
130 skipThisPoint = true;
131 }
132 if ( !skipThisPoint && filterIsValid )
133 {
134 // we're always evaluating the last written point in the buffer
135 double eval = filterExpression.evaluate( i - skippedPoints );
136 if ( !eval || std::isnan( eval ) )
137 skipThisPoint = true;
138 }
139 if ( skipThisPoint )
140 {
141 // if the point is filtered out, rewind the offset so the next point is written over it
142 outputOffset -= requestedPointRecordSize;
143 ++skippedPoints;
144 }
145 }
146 block->setPointCount( count - skippedPoints );
147 return block;
148}
149
150std::unique_ptr<QgsPointCloudBlock> QgsEptDecoder::decompressBinary(
151 const QString &filename,
152 const QgsPointCloudAttributeCollection &attributes,
153 const QgsPointCloudAttributeCollection &requestedAttributes,
154 const QgsVector3D &scale,
155 const QgsVector3D &offset,
156 QgsPointCloudExpression &filterExpression,
157 QgsRectangle &filterRect
158)
159{
160 if ( !QFile::exists( filename ) )
161 return nullptr;
162
163 QFile f( filename );
164 const bool r = f.open( QIODevice::ReadOnly );
165 if ( !r )
166 return nullptr;
167
168 const QByteArray dataUncompressed = f.read( f.size() );
169 return decompressBinary_( dataUncompressed, attributes, requestedAttributes, scale, offset, filterExpression, filterRect );
170}
171
172std::unique_ptr<QgsPointCloudBlock> QgsEptDecoder::decompressBinary(
173 const QByteArray &data,
174 const QgsPointCloudAttributeCollection &attributes,
175 const QgsPointCloudAttributeCollection &requestedAttributes,
176 const QgsVector3D &scale,
177 const QgsVector3D &offset,
178 QgsPointCloudExpression &filterExpression,
179 QgsRectangle &filterRect
180)
181{
182 return decompressBinary_( data, attributes, requestedAttributes, scale, offset, filterExpression, filterRect );
183}
184
185/* *************************************************************************************** */
186
187QByteArray decompressZtdStream( const QByteArray &dataCompressed )
188{
189 // NOTE: this is very primitive implementation because we expect the uncompressed
190 // data will be always less than 10 MB
191
192 const int MAXSIZE = 10000000;
193 QByteArray dataUncompressed;
194 dataUncompressed.resize( MAXSIZE );
195
196 ZSTD_DStream *strm = ZSTD_createDStream();
197 ZSTD_initDStream( strm );
198
199 ZSTD_inBuffer m_inBuf;
200 m_inBuf.src = reinterpret_cast<const void *>( dataCompressed.constData() );
201 m_inBuf.size = dataCompressed.size();
202 m_inBuf.pos = 0;
203
204 ZSTD_outBuffer outBuf { reinterpret_cast<void *>( dataUncompressed.data() ), MAXSIZE, 0 };
205 const size_t ret = ZSTD_decompressStream( strm, &outBuf, &m_inBuf );
206 Q_ASSERT( !ZSTD_isError( ret ) );
207 Q_ASSERT( outBuf.pos );
208 Q_ASSERT( outBuf.pos < outBuf.size );
209
210 ZSTD_freeDStream( strm );
211 dataUncompressed.resize( outBuf.pos );
212 return dataUncompressed;
213}
214
215std::unique_ptr<QgsPointCloudBlock> QgsEptDecoder::decompressZStandard(
216 const QString &filename,
217 const QgsPointCloudAttributeCollection &attributes,
218 const QgsPointCloudAttributeCollection &requestedAttributes,
219 const QgsVector3D &scale,
220 const QgsVector3D &offset,
221 QgsPointCloudExpression &filterExpression,
222 QgsRectangle &filterRect
223)
224{
225 if ( !QFile::exists( filename ) )
226 return nullptr;
227
228 QFile f( filename );
229 const bool r = f.open( QIODevice::ReadOnly );
230 if ( !r )
231 return nullptr;
232
233 const QByteArray dataCompressed = f.readAll();
234 const QByteArray dataUncompressed = decompressZtdStream( dataCompressed );
235 return decompressBinary_( dataUncompressed, attributes, requestedAttributes, scale, offset, filterExpression, filterRect );
236}
237
238std::unique_ptr<QgsPointCloudBlock> QgsEptDecoder::decompressZStandard(
239 const QByteArray &data,
240 const QgsPointCloudAttributeCollection &attributes,
241 const QgsPointCloudAttributeCollection &requestedAttributes,
242 const QgsVector3D &scale,
243 const QgsVector3D &offset,
244 QgsPointCloudExpression &filterExpression,
245 QgsRectangle &filterRect
246)
247{
248 const QByteArray dataUncompressed = decompressZtdStream( data );
249 return decompressBinary_( dataUncompressed, attributes, requestedAttributes, scale, offset, filterExpression, filterRect );
250}
251
252/* *************************************************************************************** */
253
A 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.
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
double yMinimum
double xMaximum
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
double yMaximum
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58