QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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"
24 #include "qgspointcloudattribute.h"
25 #include "qgspointcloudrequest.h"
26 
27 #include "qgspointcloudrenderer.h"
28 
29 #include "qgsapplication.h"
31 #include "qgsfeedback.h"
33 
34 #include <QQueue>
35 #include <QtConcurrent/QtConcurrentMap>
36 
38 {
40  static QMutex sStatsProcessorFeedbackMutex;
41 
42  StatsProcessor( QgsPointCloudIndex *index, QgsPointCloudRequest request, QgsFeedback *feedback, double progressValue )
43  : mIndex( index->clone().release() ), mRequest( request ), mFeedback( feedback ), mProgressValue( progressValue )
44  {
45  }
46 
47  StatsProcessor( const StatsProcessor &processor )
48  : mIndex( processor.mIndex->clone().release() ), mRequest( processor.mRequest ), mFeedback( processor.mFeedback ), mProgressValue( processor.mProgressValue )
49  {
50  }
51 
53  {
54  mIndex.reset( rhs.mIndex->clone().release() );
55  mRequest = rhs.mRequest;
56  mFeedback = rhs.mFeedback;
57  mProgressValue = rhs.mProgressValue;
58  return *this;
59  }
60 
62  {
63  std::unique_ptr<QgsPointCloudBlock> block = nullptr;
64  if ( mIndex->accessType() == QgsPointCloudIndex::Local )
65  {
66  block.reset( mIndex->nodeData( node, mRequest ) );
67  }
68  else
69  {
70  QgsPointCloudBlockRequest *request = mIndex->asyncNodeData( node, mRequest );
71  QEventLoop loop;
72  QObject::connect( request, &QgsPointCloudBlockRequest::finished, &loop, &QEventLoop::quit );
73  QObject::connect( mFeedback, &QgsFeedback::canceled, &loop, &QEventLoop::quit );
74  loop.exec();
75  if ( !mFeedback->isCanceled() )
76  block.reset( request->block() );
77  if ( !request->block() )
78  {
79  QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics for node %1, error: \"%2\"" ).arg( node.toString() ).arg( request->errorStr() ) );
80  }
81  }
82 
83  if ( !block.get() )
84  {
85  updateFeedback();
86  return QgsPointCloudStatistics();
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  {
123  classifiableAttributesOffsetSet.insert( attributeOffset );
124  }
125  }
126 
127  for ( int i = 0; i < count; ++i )
128  {
129  for ( int j = 0; j < attributes.size(); ++j )
130  {
131  if ( mFeedback->isCanceled() )
132  {
133  return QgsPointCloudStatistics();
134  }
135  QString attributeName = attributes.at( j ).name();
136  QgsPointCloudAttribute::DataType attributeType = attributes.at( j ).type();
137 
138  double attributeValue = 0;
139  int attributeOffset = attributeOffsetVector[ j ];
140 
141  QgsPointCloudAttributeStatistics &stats = statsMap[ attributeName ];
142  QgsPointCloudRenderContext::getAttribute( ptr, i * recordSize + attributeOffset, attributeType, attributeValue );
143  stats.minimum = std::min( stats.minimum, attributeValue );
144  stats.maximum = std::max( stats.maximum, attributeValue );
145  stats.mean += attributeValue / count;
146  // TODO: add stDev calculation
147  stats.count++;
148  if ( classifiableAttributesOffsetSet.contains( attributeOffset ) )
149  {
150  stats.classCount[( int )attributeValue ]++;
151  }
152  }
153  }
154  updateFeedback();
155  return QgsPointCloudStatistics( count, statsMap );
156  }
157  private:
158  std::unique_ptr<QgsPointCloudIndex> mIndex = nullptr;
159  QgsPointCloudRequest mRequest;
160  QgsFeedback *mFeedback = nullptr;
161  double mProgressValue = 0.0;
162 
163  void updateFeedback()
164  {
165  QMutexLocker locker( &sStatsProcessorFeedbackMutex );
166  mFeedback->setProgress( mFeedback->progress() + mProgressValue );
167  }
168 };
169 
171 
173  : mIndex( index->clone() )
174 {
175 
176 }
177 
178 bool QgsPointCloudStatsCalculator::calculateStats( QgsFeedback *feedback, const QVector<QgsPointCloudAttribute> &attributes, qint64 pointsLimit )
179 {
180  if ( !mIndex->isValid() )
181  {
182  QgsMessageLog::logMessage( QObject::tr( "Unable to calculate statistics of an invalid index" ) );
183  return false;
184  }
185  mRequest.setAttributes( attributes );
186 
187  qint64 pointCount = 0;
188  QVector<IndexedPointCloudNode> nodes;
189  QQueue<IndexedPointCloudNode> queue;
190  queue.push_back( mIndex->root() );
191  while ( !queue.empty() )
192  {
193  IndexedPointCloudNode node = queue.front();
194  queue.pop_front();
195  if ( !mProcessedNodes.contains( node ) )
196  pointCount += mIndex->nodePointCount( node );
197  if ( pointsLimit != -1 && pointCount > pointsLimit )
198  break;
199  if ( !mProcessedNodes.contains( node ) )
200  {
201  nodes.push_back( node );
202  mProcessedNodes.insert( node );
203  }
204  for ( const IndexedPointCloudNode &child : mIndex->nodeChildren( node ) )
205  {
206  queue.push_back( child );
207  }
208  }
209 
210  feedback->setProgress( 0 );
211 
212  QVector<QgsPointCloudStatistics> list = QtConcurrent::blockingMapped( nodes, StatsProcessor( mIndex.get(), mRequest, feedback, 100.0 / ( double )nodes.size() ) );
213 
214  for ( QgsPointCloudStatistics &s : list )
215  {
216  mStats.combineWith( s );
217  }
218  return !feedback->isCanceled() && mStats.sampledPointsCount() != 0;
219 }
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:76
QgsPointCloudStatsCalculator::calculateStats
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...
Definition: qgspointcloudstatscalculator.cpp:178
QgsPointCloudAttributeStatistics::classCount
QMap< int, int > classCount
Definition: qgspointcloudstatistics.h:47
QgsPointCloudStatistics::combineWith
void combineWith(const QgsPointCloudStatistics &stats)
Merges the current statistics with the statistics from stats.
Definition: qgspointcloudstatistics.cpp:132
QgsPointCloudBlockRequest::block
QgsPointCloudBlock * block()
Returns the requested block.
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
StatsProcessor::sStatsProcessorFeedbackMutex
static QMutex sStatsProcessorFeedbackMutex
Definition: qgspointcloudstatscalculator.cpp:53
qgspointcloudblockrequest.h
QgsPointCloudAttributeStatistics::minimum
double minimum
Definition: qgspointcloudstatistics.h:41
QgsFeedback::canceled
void canceled()
Internal routines can connect to this signal if they use event loop.
QgsFeedback::isCanceled
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:67
qgspointcloudattribute.h
QgsPointCloudStatistics
Class used to store statistics of a point cloud dataset.
Definition: qgspointcloudstatistics.h:61
QgsPointCloudIndex::clone
virtual std::unique_ptr< QgsPointCloudIndex > clone() const =0
Returns a clone of the current point cloud index object.
QgsPointCloudRequest
Point cloud data request.
Definition: qgspointcloudrequest.h:39
qgsapplication.h
QgsPointCloudRenderContext::getAttribute
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...
Definition: qgspointcloudrenderer.h:183
QgsPointCloudStatistics::sampledPointsCount
int sampledPointsCount() const
Returns the number of points used to calculate the statistics.
Definition: qgspointcloudstatistics.h:73
qgspointcloudstatistics.h
StatsProcessor::result_type
QgsPointCloudStatistics result_type
Definition: qgspointcloudstatscalculator.cpp:52
IndexedPointCloudNode
Represents a indexed point cloud node in octree.
Definition: qgspointcloudindex.h:57
QgsPointCloudAttributeStatistics
Class used to store statistics of one attribute of a point cloud dataset.
Definition: qgspointcloudstatistics.h:39
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
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsPointCloudAttributeCollection
Collection of point cloud attributes.
Definition: qgspointcloudattribute.h:141
QgsPointCloudAttributeStatistics::count
int count
Definition: qgspointcloudstatistics.h:45
QgsPointCloudBlockRequest::errorStr
QString errorStr()
Returns the error message string of the request.
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
QgsPointCloudAttribute
Attribute for point cloud data pair of name and size in bytes.
Definition: qgspointcloudattribute.h:40
qgspointcloudstatscalculator.h
QgsPointCloudStatsCalculator::QgsPointCloudStatsCalculator
QgsPointCloudStatsCalculator(QgsPointCloudIndex *index)
Constructor.
Definition: qgspointcloudstatscalculator.cpp:172
qgspointcloudindex.h
QgsPointCloudBlockRequest
Base class for handling loading QgsPointCloudBlock asynchronously.
Definition: qgspointcloudblockrequest.h:36
QgsPointCloudIndex::Local
@ Local
Local means the source is a local file on the machine.
Definition: qgspointcloudindex.h:170
QgsPointCloudAttributeStatistics::maximum
double maximum
Definition: qgspointcloudstatistics.h:42
QgsPointCloudAttributeStatistics::stDev
double stDev
Definition: qgspointcloudstatistics.h:44
QgsPointCloudRequest::setAttributes
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
Definition: qgspointcloudrequest.cpp:29
StatsProcessor::operator()
QgsPointCloudStatistics operator()(IndexedPointCloudNode node)
Definition: qgspointcloudstatscalculator.cpp:74
QgsPointCloudBlockRequest::finished
void finished()
Emitted when the request processing has finished.
QgsPointCloudIndex
Represents a indexed point clouds data in octree.
Definition: qgspointcloudindex.h:163
QgsFeedback::progress
double progress() const SIP_HOLDGIL
Returns the current progress reported by the feedback object.
Definition: qgsfeedback.h:93
StatsProcessor::operator=
StatsProcessor & operator=(const StatsProcessor &rhs)
Definition: qgspointcloudstatscalculator.cpp:65
IndexedPointCloudNode::toString
QString toString() const
Encode node to string.
Definition: qgspointcloudindex.cpp:58
qgspointcloudstatscalculationtask.h
StatsProcessor::StatsProcessor
StatsProcessor(QgsPointCloudIndex *index, QgsPointCloudRequest request, QgsFeedback *feedback, double progressValue)
Definition: qgspointcloudstatscalculator.cpp:55
StatsProcessor
Definition: qgspointcloudstatscalculator.cpp:37
qgspointcloudrequest.h
QgsPointCloudAttributeStatistics::mean
double mean
Definition: qgspointcloudstatistics.h:43
qgspointcloudrenderer.h
qgsfeedback.h
qgsmessagelog.h