QGIS API Documentation 3.41.0-Master (25ec5511245)
Loading...
Searching...
No Matches
qgspointcloudstatscalculator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudstatscalculator.cpp
3 --------------------
4 begin : April 2022
5 copyright : (C) 2022 by Belgacem Nedjima
6 email : belgacem dot nedjima 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
19#include "moc_qgspointcloudstatscalculator.cpp"
20
22
23#include "qgspointcloudindex.h"
24#include "qgsmessagelog.h"
27
29
30#include "qgsfeedback.h"
32
33#include <QQueue>
34#include <QtConcurrent/QtConcurrentMap>
35
37{
40
41 StatsProcessor( QgsPointCloudIndex *index, QgsPointCloudRequest request, QgsFeedback *feedback, double progressValue )
42 : mIndex( index->clone().release() ), mRequest( request ), mFeedback( feedback ), mProgressValue( progressValue )
43 {
44 }
45
46 StatsProcessor( const StatsProcessor &processor )
47 : mIndex( processor.mIndex->clone().release() ), mRequest( processor.mRequest ), mFeedback( processor.mFeedback ), mProgressValue( processor.mProgressValue )
48 {
49 }
50
52 {
53 mIndex.reset( rhs.mIndex->clone().release() );
54 mRequest = rhs.mRequest;
55 mFeedback = rhs.mFeedback;
56 mProgressValue = rhs.mProgressValue;
57 return *this;
58 }
59
61 {
62 if ( mIndex->nodePointCount( node ) < 1 )
64
65 std::unique_ptr<QgsPointCloudBlock> block = nullptr;
66 if ( mIndex->accessType() == QgsPointCloudIndex::Local )
67 {
68 block = mIndex->nodeData( node, mRequest );
69 }
70 else
71 {
72 QgsPointCloudBlockRequest *request = mIndex->asyncNodeData( node, mRequest );
73 if ( request == nullptr )
74 {
75 QgsDebugError( QStringLiteral( "Unable to calculate statistics for node %1: Got nullptr async request" ).arg( node.toString() ) );
77 }
78 QEventLoop loop;
79 QObject::connect( request, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
80 QObject::connect( mFeedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
81 loop.exec();
82 if ( !mFeedback->isCanceled() )
83 {
84 block = request->takeBlock();
85 if ( !block )
86 {
87 QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics for node %1, error: \"%2\"" ).arg( node.toString(), request->errorStr() ) );
88 }
89 }
90 }
91
92 if ( !block.get() )
93 {
94 updateFeedback();
96 }
97
98 const QgsPointCloudAttributeCollection attributesCollection = block->attributes();
99 const QVector<QgsPointCloudAttribute> attributes = attributesCollection.attributes();
100 const char *ptr = block->data();
101 int count = block->pointCount();
102 int recordSize = attributesCollection.pointRecordSize();
103
104 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
105 for ( const QgsPointCloudAttribute &attribute : attributes )
106 {
108 summary.minimum = std::numeric_limits<double>::max();
109 summary.maximum = std::numeric_limits<double>::lowest();
110 summary.count = 0;
111 summary.mean = 0;
112 summary.stDev = std::numeric_limits<double>::quiet_NaN();
113 summary.classCount.clear();
114 statsMap[ attribute.name() ] = summary;
115 }
116
117 QVector<int> attributeOffsetVector;
118 QSet<int> classifiableAttributesOffsetSet;
119 for ( const QgsPointCloudAttribute &attribute : attributes )
120 {
121 int attributeOffset = 0;
122 attributesCollection.find( attribute.name(), attributeOffset );
123 attributeOffsetVector.push_back( attributeOffset );
124 if ( attribute.name() == QLatin1String( "ScannerChannel" ) ||
125 attribute.name() == QLatin1String( "ReturnNumber" ) ||
126 attribute.name() == QLatin1String( "NumberOfReturns" ) ||
127 attribute.name() == QLatin1String( "ScanDirectionFlag" ) ||
128 attribute.name() == QLatin1String( "Classification" ) ||
129 attribute.name() == QLatin1String( "EdgeOfFlightLine" ) ||
130 attribute.name() == QLatin1String( "PointSourceId" ) ||
131 attribute.name() == QLatin1String( "Synthetic" ) ||
132 attribute.name() == QLatin1String( "KeyPoint" ) ||
133 attribute.name() == QLatin1String( "Withheld" ) ||
134 attribute.name() == QLatin1String( "Overlap" ) )
135 {
136 classifiableAttributesOffsetSet.insert( attributeOffset );
137 }
138 }
139
140 for ( int i = 0; i < count; ++i )
141 {
142 for ( int j = 0; j < attributes.size(); ++j )
143 {
144 if ( mFeedback->isCanceled() )
145 {
147 }
148 QString attributeName = attributes.at( j ).name();
149 QgsPointCloudAttribute::DataType attributeType = attributes.at( j ).type();
150
151 double attributeValue = 0;
152 int attributeOffset = attributeOffsetVector[ j ];
153
154 QgsPointCloudAttributeStatistics &stats = statsMap[ attributeName ];
155 QgsPointCloudRenderContext::getAttribute( ptr, i * recordSize + attributeOffset, attributeType, attributeValue );
156 stats.minimum = std::min( stats.minimum, attributeValue );
157 stats.maximum = std::max( stats.maximum, attributeValue );
158 stats.mean += attributeValue / count;
159 // TODO: add stDev calculation
160 stats.count++;
161 if ( classifiableAttributesOffsetSet.contains( attributeOffset ) )
162 {
163 stats.classCount[( int )attributeValue ]++;
164 }
165 }
166 }
167 updateFeedback();
168 return QgsPointCloudStatistics( count, statsMap );
169 }
170 private:
171 std::unique_ptr<QgsPointCloudIndex> mIndex = nullptr;
172 QgsPointCloudRequest mRequest;
173 QgsFeedback *mFeedback = nullptr;
174 double mProgressValue = 0.0;
175
176 void updateFeedback()
177 {
178 QMutexLocker locker( &sStatsProcessorFeedbackMutex );
179 mFeedback->setProgress( mFeedback->progress() + mProgressValue );
180 }
181};
182
184
186 : mIndex( index->clone() )
187{
188
189}
190
191bool QgsPointCloudStatsCalculator::calculateStats( QgsFeedback *feedback, const QVector<QgsPointCloudAttribute> &attributes, qint64 pointsLimit )
192{
193 if ( !mIndex->isValid() )
194 {
195 QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics of an invalid index" ) );
196 return false;
197 }
198 mRequest.setAttributes( attributes );
199
200 qint64 pointCount = 0;
201 QVector<IndexedPointCloudNode> nodes;
202 QQueue<IndexedPointCloudNode> queue;
203 queue.push_back( mIndex->root() );
204 while ( !queue.empty() )
205 {
206 IndexedPointCloudNode node = queue.front();
207 queue.pop_front();
208 if ( !mProcessedNodes.contains( node ) )
209 pointCount += mIndex->nodePointCount( node );
210 if ( pointsLimit != -1 && pointCount > pointsLimit )
211 break;
212 if ( !mProcessedNodes.contains( node ) )
213 {
214 nodes.push_back( node );
215 mProcessedNodes.insert( node );
216 }
217 for ( const IndexedPointCloudNode &child : mIndex->nodeChildren( node ) )
218 {
219 queue.push_back( child );
220 }
221 }
222
223 feedback->setProgress( 0 );
224
225 QVector<QgsPointCloudStatistics> list = QtConcurrent::blockingMapped( nodes, StatsProcessor( mIndex.get(), mRequest, feedback, 100.0 / ( double )nodes.size() ) );
226
227 for ( QgsPointCloudStatistics &s : list )
228 {
229 mStats.combineWith( s );
230 }
231 return !feedback->isCanceled() && mStats.sampledPointsCount() != 0;
232}
Represents a indexed point cloud node in octree.
QString toString() const
Encode node to string.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void canceled()
Internal routines can connect to this signal if they use event loop.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
double progress() const
Returns the current progress reported by the feedback object.
Definition qgsfeedback.h:77
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
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.
Base class for handling loading QgsPointCloudBlock asynchronously.
QString errorStr()
Returns the error message string of the request.
void finished()
Emitted when the request processing has finished.
std::unique_ptr< QgsPointCloudBlock > takeBlock()
Returns the requested block.
Represents a indexed point clouds data in octree.
@ Local
Local means the source is a local file on the machine.
static void getAttribute(const char *data, std::size_t offset, QgsPointCloudAttribute::DataType type, T &value)
Retrieves the attribute value from data at the specified offset, where type indicates the original da...
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
Class used to store statistics of a point cloud dataset.
void combineWith(const QgsPointCloudStatistics &stats)
Merges the current statistics with the statistics from stats.
int sampledPointsCount() const
Returns the number of points used to calculate the statistics.
QgsPointCloudStatsCalculator(QgsPointCloudIndex *index)
Constructor.
bool calculateStats(QgsFeedback *feedback, const QVector< QgsPointCloudAttribute > &attributes, qint64 pointsLimit=-1)
Calculates the statistics of given attributes attributes up to new pointsLimit points Note: the alrea...
#define QgsDebugError(str)
Definition qgslogger.h:38
Class used to store statistics of one attribute of a point cloud dataset.
StatsProcessor(QgsPointCloudIndex *index, QgsPointCloudRequest request, QgsFeedback *feedback, double progressValue)
static QMutex sStatsProcessorFeedbackMutex
StatsProcessor(const StatsProcessor &processor)
QgsPointCloudStatistics result_type
StatsProcessor & operator=(const StatsProcessor &rhs)
QgsPointCloudStatistics operator()(IndexedPointCloudNode node)