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