QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
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 QEventLoop loop;
74 QObject::connect( request, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
75 QObject::connect( mFeedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
76 loop.exec();
77 if ( !mFeedback->isCanceled() )
78 {
79 block = request->takeBlock();
80 if ( !block )
81 {
82 QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics for node %1, error: \"%2\"" ).arg( node.toString(), request->errorStr() ) );
83 }
84 }
85 }
86
87 if ( !block.get() )
88 {
89 updateFeedback();
91 }
92
93 const QgsPointCloudAttributeCollection attributesCollection = block->attributes();
94 const QVector<QgsPointCloudAttribute> attributes = attributesCollection.attributes();
95 const char *ptr = block->data();
96 int count = block->pointCount();
97 int recordSize = attributesCollection.pointRecordSize();
98
99 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
100 for ( const QgsPointCloudAttribute &attribute : attributes )
101 {
103 summary.minimum = std::numeric_limits<double>::max();
104 summary.maximum = std::numeric_limits<double>::lowest();
105 summary.count = 0;
106 summary.mean = 0;
107 summary.stDev = std::numeric_limits<double>::quiet_NaN();
108 summary.classCount.clear();
109 statsMap[ attribute.name() ] = summary;
110 }
111
112 QVector<int> attributeOffsetVector;
113 QSet<int> classifiableAttributesOffsetSet;
114 for ( const QgsPointCloudAttribute &attribute : attributes )
115 {
116 int attributeOffset = 0;
117 attributesCollection.find( attribute.name(), attributeOffset );
118 attributeOffsetVector.push_back( attributeOffset );
119 if ( attribute.name() == QLatin1String( "ScannerChannel" ) ||
120 attribute.name() == QLatin1String( "ReturnNumber" ) ||
121 attribute.name() == QLatin1String( "NumberOfReturns" ) ||
122 attribute.name() == QLatin1String( "ScanDirectionFlag" ) ||
123 attribute.name() == QLatin1String( "Classification" ) ||
124 attribute.name() == QLatin1String( "EdgeOfFlightLine" ) ||
125 attribute.name() == QLatin1String( "PointSourceId" ) ||
126 attribute.name() == QLatin1String( "Synthetic" ) ||
127 attribute.name() == QLatin1String( "KeyPoint" ) ||
128 attribute.name() == QLatin1String( "Withheld" ) ||
129 attribute.name() == QLatin1String( "Overlap" ) )
130 {
131 classifiableAttributesOffsetSet.insert( attributeOffset );
132 }
133 }
134
135 for ( int i = 0; i < count; ++i )
136 {
137 for ( int j = 0; j < attributes.size(); ++j )
138 {
139 if ( mFeedback->isCanceled() )
140 {
142 }
143 QString attributeName = attributes.at( j ).name();
144 QgsPointCloudAttribute::DataType attributeType = attributes.at( j ).type();
145
146 double attributeValue = 0;
147 int attributeOffset = attributeOffsetVector[ j ];
148
149 QgsPointCloudAttributeStatistics &stats = statsMap[ attributeName ];
150 QgsPointCloudRenderContext::getAttribute( ptr, i * recordSize + attributeOffset, attributeType, attributeValue );
151 stats.minimum = std::min( stats.minimum, attributeValue );
152 stats.maximum = std::max( stats.maximum, attributeValue );
153 stats.mean += attributeValue / count;
154 // TODO: add stDev calculation
155 stats.count++;
156 if ( classifiableAttributesOffsetSet.contains( attributeOffset ) )
157 {
158 stats.classCount[( int )attributeValue ]++;
159 }
160 }
161 }
162 updateFeedback();
163 return QgsPointCloudStatistics( count, statsMap );
164 }
165 private:
166 std::unique_ptr<QgsPointCloudIndex> mIndex = nullptr;
167 QgsPointCloudRequest mRequest;
168 QgsFeedback *mFeedback = nullptr;
169 double mProgressValue = 0.0;
170
171 void updateFeedback()
172 {
173 QMutexLocker locker( &sStatsProcessorFeedbackMutex );
174 mFeedback->setProgress( mFeedback->progress() + mProgressValue );
175 }
176};
177
179
181 : mIndex( index->clone() )
182{
183
184}
185
186bool QgsPointCloudStatsCalculator::calculateStats( QgsFeedback *feedback, const QVector<QgsPointCloudAttribute> &attributes, qint64 pointsLimit )
187{
188 if ( !mIndex->isValid() )
189 {
190 QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics of an invalid index" ) );
191 return false;
192 }
193 mRequest.setAttributes( attributes );
194
195 qint64 pointCount = 0;
196 QVector<IndexedPointCloudNode> nodes;
197 QQueue<IndexedPointCloudNode> queue;
198 queue.push_back( mIndex->root() );
199 while ( !queue.empty() )
200 {
201 IndexedPointCloudNode node = queue.front();
202 queue.pop_front();
203 if ( !mProcessedNodes.contains( node ) )
204 pointCount += mIndex->nodePointCount( node );
205 if ( pointsLimit != -1 && pointCount > pointsLimit )
206 break;
207 if ( !mProcessedNodes.contains( node ) )
208 {
209 nodes.push_back( node );
210 mProcessedNodes.insert( node );
211 }
212 for ( const IndexedPointCloudNode &child : mIndex->nodeChildren( node ) )
213 {
214 queue.push_back( child );
215 }
216 }
217
218 feedback->setProgress( 0 );
219
220 QVector<QgsPointCloudStatistics> list = QtConcurrent::blockingMapped( nodes, StatsProcessor( mIndex.get(), mRequest, feedback, 100.0 / ( double )nodes.size() ) );
221
222 for ( QgsPointCloudStatistics &s : list )
223 {
224 mStats.combineWith( s );
225 }
226 return !feedback->isCanceled() && mStats.sampledPointsCount() != 0;
227}
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...
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)