QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 #include "qgseptpointcloudindex.h"
20 #include "qgslazdecoder.h"
21 #include "qgspointcloudattribute.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 
42 QgsPointCloudBlock *_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 
132 QgsPointCloudBlock *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 
146 QgsPointCloudBlock *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 
153 QByteArray 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 
181 QgsPointCloudBlock *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 
196 QgsPointCloudBlock *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 
qgseptpointcloudindex.h
QgsPointCloudAttribute::DataType
DataType
Systems of unit measurement.
Definition: qgspointcloudattribute.h:44
QgsPointCloudAttributeCollection::find
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
Definition: qgspointcloudattribute.cpp:168
QgsVector3D
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition: qgsvector3d.h:31
qgspointcloudattribute.h
QgsPointCloudBlock
Base class for storing raw data from point cloud nodes.
Definition: qgspointcloudblock.h:38
qgseptdecoder.h
QgsPointCloudAttributeCollection::attributes
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
Definition: qgspointcloudattribute.cpp:163
QgsPointCloudAttributeCollection::pointRecordSize
int pointRecordSize() const
Returns total size of record.
Definition: qgspointcloudattribute.h:187
QgsPointCloudAttributeCollection
Collection of point cloud attributes.
Definition: qgspointcloudattribute.h:141
QgsPointCloudAttribute
Attribute for point cloud data pair of name and size in bytes.
Definition: qgspointcloudattribute.h:40
QgsPointCloudAttribute::size
int size() const
Returns size of the attribute in bytes.
Definition: qgspointcloudattribute.h:67
qgslazdecoder.h
qgsvector3d.h
qgslogger.h
QgsPointCloudAttribute::type
DataType type() const
Returns the data type.
Definition: qgspointcloudattribute.h:74