QGIS API Documentation 3.35.0-Master (6cf940c20d6)
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
21
22#include "qgspointcloudindex.h"
23#include "qgsmessagelog.h"
26
28
29#include "qgsfeedback.h"
31
32#include <QQueue>
33#include <QtConcurrent/QtConcurrentMap>
34
36{
39
40 StatsProcessor( QgsPointCloudIndex *index, QgsPointCloudRequest request, QgsFeedback *feedback, double progressValue )
41 : mIndex( index->clone().release() ), mRequest( request ), mFeedback( feedback ), mProgressValue( progressValue )
42 {
43 }
44
45 StatsProcessor( const StatsProcessor &processor )
46 : mIndex( processor.mIndex->clone().release() ), mRequest( processor.mRequest ), mFeedback( processor.mFeedback ), mProgressValue( processor.mProgressValue )
47 {
48 }
49
51 {
52 mIndex.reset( rhs.mIndex->clone().release() );
53 mRequest = rhs.mRequest;
54 mFeedback = rhs.mFeedback;
55 mProgressValue = rhs.mProgressValue;
56 return *this;
57 }
58
60 {
61 std::unique_ptr<QgsPointCloudBlock> block = nullptr;
62 if ( mIndex->accessType() == QgsPointCloudIndex::Local )
63 {
64 block = mIndex->nodeData( node, mRequest );
65 }
66 else
67 {
68 QgsPointCloudBlockRequest *request = mIndex->asyncNodeData( node, mRequest );
69 QEventLoop loop;
70 QObject::connect( request, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
71 QObject::connect( mFeedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
72 loop.exec();
73 if ( !mFeedback->isCanceled() )
74 {
75 block = request->takeBlock();
76 if ( !block )
77 {
78 QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics for node %1, error: \"%2\"" ).arg( node.toString(), request->errorStr() ) );
79 }
80 }
81 }
82
83 if ( !block.get() )
84 {
85 updateFeedback();
87 }
88
89 const QgsPointCloudAttributeCollection attributesCollection = block->attributes();
90 const QVector<QgsPointCloudAttribute> attributes = attributesCollection.attributes();
91 const char *ptr = block->data();
92 int count = block->pointCount();
93 int recordSize = attributesCollection.pointRecordSize();
94
95 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
96 for ( const QgsPointCloudAttribute &attribute : attributes )
97 {
99 summary.minimum = std::numeric_limits<double>::max();
100 summary.maximum = std::numeric_limits<double>::lowest();
101 summary.count = 0;
102 summary.mean = 0;
103 summary.stDev = std::numeric_limits<double>::quiet_NaN();
104 summary.classCount.clear();
105 statsMap[ attribute.name() ] = summary;
106 }
107
108 QVector<int> attributeOffsetVector;
109 QSet<int> classifiableAttributesOffsetSet;
110 for ( const QgsPointCloudAttribute &attribute : attributes )
111 {
112 int attributeOffset = 0;
113 attributesCollection.find( attribute.name(), attributeOffset );
114 attributeOffsetVector.push_back( attributeOffset );
115 if ( attribute.name() == QLatin1String( "ScannerChannel" ) ||
116 attribute.name() == QLatin1String( "ReturnNumber" ) ||
117 attribute.name() == QLatin1String( "NumberOfReturns" ) ||
118 attribute.name() == QLatin1String( "ScanDirectionFlag" ) ||
119 attribute.name() == QLatin1String( "Classification" ) ||
120 attribute.name() == QLatin1String( "EdgeOfFlightLine" ) ||
121 attribute.name() == QLatin1String( "PointSourceId" ) ||
122 attribute.name() == QLatin1String( "Synthetic" ) ||
123 attribute.name() == QLatin1String( "KeyPoint" ) ||
124 attribute.name() == QLatin1String( "Withheld" ) ||
125 attribute.name() == QLatin1String( "Overlap" ) )
126 {
127 classifiableAttributesOffsetSet.insert( attributeOffset );
128 }
129 }
130
131 for ( int i = 0; i < count; ++i )
132 {
133 for ( int j = 0; j < attributes.size(); ++j )
134 {
135 if ( mFeedback->isCanceled() )
136 {
138 }
139 QString attributeName = attributes.at( j ).name();
140 QgsPointCloudAttribute::DataType attributeType = attributes.at( j ).type();
141
142 double attributeValue = 0;
143 int attributeOffset = attributeOffsetVector[ j ];
144
145 QgsPointCloudAttributeStatistics &stats = statsMap[ attributeName ];
146 QgsPointCloudRenderContext::getAttribute( ptr, i * recordSize + attributeOffset, attributeType, attributeValue );
147 stats.minimum = std::min( stats.minimum, attributeValue );
148 stats.maximum = std::max( stats.maximum, attributeValue );
149 stats.mean += attributeValue / count;
150 // TODO: add stDev calculation
151 stats.count++;
152 if ( classifiableAttributesOffsetSet.contains( attributeOffset ) )
153 {
154 stats.classCount[( int )attributeValue ]++;
155 }
156 }
157 }
158 updateFeedback();
159 return QgsPointCloudStatistics( count, statsMap );
160 }
161 private:
162 std::unique_ptr<QgsPointCloudIndex> mIndex = nullptr;
163 QgsPointCloudRequest mRequest;
164 QgsFeedback *mFeedback = nullptr;
165 double mProgressValue = 0.0;
166
167 void updateFeedback()
168 {
169 QMutexLocker locker( &sStatsProcessorFeedbackMutex );
170 mFeedback->setProgress( mFeedback->progress() + mProgressValue );
171 }
172};
173
175
177 : mIndex( index->clone() )
178{
179
180}
181
182bool QgsPointCloudStatsCalculator::calculateStats( QgsFeedback *feedback, const QVector<QgsPointCloudAttribute> &attributes, qint64 pointsLimit )
183{
184 if ( !mIndex->isValid() )
185 {
186 QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics of an invalid index" ) );
187 return false;
188 }
189 mRequest.setAttributes( attributes );
190
191 qint64 pointCount = 0;
192 QVector<IndexedPointCloudNode> nodes;
193 QQueue<IndexedPointCloudNode> queue;
194 queue.push_back( mIndex->root() );
195 while ( !queue.empty() )
196 {
197 IndexedPointCloudNode node = queue.front();
198 queue.pop_front();
199 if ( !mProcessedNodes.contains( node ) )
200 pointCount += mIndex->nodePointCount( node );
201 if ( pointsLimit != -1 && pointCount > pointsLimit )
202 break;
203 if ( !mProcessedNodes.contains( node ) )
204 {
205 nodes.push_back( node );
206 mProcessedNodes.insert( node );
207 }
208 for ( const IndexedPointCloudNode &child : mIndex->nodeChildren( node ) )
209 {
210 queue.push_back( child );
211 }
212 }
213
214 feedback->setProgress( 0 );
215
216 QThreadPool::globalInstance()->releaseThread();
217 QVector<QgsPointCloudStatistics> list = QtConcurrent::blockingMapped( nodes, StatsProcessor( mIndex.get(), mRequest, feedback, 100.0 / ( double )nodes.size() ) );
218 QThreadPool::globalInstance()->reserveThread();
219
220 for ( QgsPointCloudStatistics &s : list )
221 {
222 mStats.combineWith( s );
223 }
224 return !feedback->isCanceled() && mStats.sampledPointsCount() != 0;
225}
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:45
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:54
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:63
double progress() const
Returns the current progress reported by the feedback object.
Definition qgsfeedback.h:80
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...
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)