QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgspointcloudstatistics.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudstatistics.h
3 --------------------
4 begin : May 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
20#include <limits>
21#include <QJsonObject>
22#include <QJsonDocument>
23
25#include "qgsmessagelog.h"
26
27// QgsPointCloudAttributeStatistics
28
30{
31 minimum = std::min( minimum, stats.minimum );
32 maximum = std::max( maximum, stats.maximum );
33
34 double newMean = ( mean * count + stats.mean * stats.count ) / ( count + stats.count );
35 double delta1 = newMean - mean;
36 double variance1 = stDev * stDev + delta1 * delta1 - 2 * count * delta1 * mean;
37 double delta2 = newMean - stats.mean;
38 double variance2 = stats.stDev * stats.stDev + delta2 * delta2 - 2 * stats.count * delta2 * stats.mean;
39 stDev = ( variance1 * count + variance2 * stats.count ) / ( count + stats.count );
40 stDev = std::sqrt( stDev );
41
42 mean = newMean;
43 count += stats.count;
44
45 for ( auto it = stats.classCount.constBegin(); it != stats.classCount.constEnd(); it++ )
46 {
47 int c = classCount.value( it.key(), 0 );
48 c += it.value();
49 classCount[ it.key() ] = c;
50 }
51}
52
53// QgsPointCloudStatistics
54
56{
57
58}
59
60QgsPointCloudStatistics::QgsPointCloudStatistics( int sampledPointsCount, const QMap<QString, QgsPointCloudAttributeStatistics> &stats )
61 : mSampledPointsCount( sampledPointsCount ), mStatisticsMap( stats )
62{
63
64}
65
67{
68 mStatisticsMap.clear();
69}
70
71void QgsPointCloudStatistics::clear( const QVector<QgsPointCloudAttribute> &attributes )
72{
73 for ( QgsPointCloudAttribute attribute : attributes )
74 {
75 mStatisticsMap.remove( attribute.name() );
76 }
77}
78
80{
82 defaultVal.minimum = std::numeric_limits<double>::max();
83 defaultVal.maximum = std::numeric_limits<double>::lowest();
84 defaultVal.count = 0;
85 return mStatisticsMap.value( attribute, defaultVal );
86}
87
88QList<int> QgsPointCloudStatistics::classesOf( const QString &attribute ) const
89{
90 if ( !mStatisticsMap.contains( attribute ) )
91 return QList<int>();
92 QgsPointCloudAttributeStatistics s = mStatisticsMap[ attribute ];
93 return s.classCount.keys();
94}
95
96QMap<int, int> QgsPointCloudStatistics::availableClasses( const QString &attribute ) const
97{
98 if ( !mStatisticsMap.contains( attribute ) )
99 return QMap<int, int>();
100 return mStatisticsMap[ attribute ].classCount;
101}
102
103double QgsPointCloudStatistics::minimum( const QString &attribute ) const
104{
105 if ( !mStatisticsMap.contains( attribute ) )
106 return std::numeric_limits<double>::quiet_NaN();
107 return mStatisticsMap[ attribute ].minimum;
108}
109
110double QgsPointCloudStatistics::maximum( const QString &attribute ) const
111{
112 if ( !mStatisticsMap.contains( attribute ) )
113 return std::numeric_limits<double>::quiet_NaN();
114 return mStatisticsMap[ attribute ].maximum;
115}
116
117double QgsPointCloudStatistics::mean( const QString &attribute ) const
118{
119 if ( !mStatisticsMap.contains( attribute ) )
120 return std::numeric_limits<double>::quiet_NaN();
121 return mStatisticsMap[ attribute ].mean;
122}
123
124double QgsPointCloudStatistics::stDev( const QString &attribute ) const
125{
126 if ( !mStatisticsMap.contains( attribute ) )
127 return std::numeric_limits<double>::quiet_NaN();
128 return mStatisticsMap[ attribute ].stDev;
129}
130
131
133{
134 for ( auto it = stats.mStatisticsMap.constBegin(); it != stats.mStatisticsMap.constEnd(); it++ )
135 {
136 const QString attribute = it.key();
138 if ( mStatisticsMap.contains( attribute ) )
139 {
140 s.cumulateStatistics( mStatisticsMap[ attribute ] );
141 }
142 mStatisticsMap[ attribute ] = s;
143 }
144 mSampledPointsCount += stats.mSampledPointsCount;
145}
146
148{
149 QJsonObject obj;
150 obj.insert( QStringLiteral( "sampled-points" ), QJsonValue::fromVariant( sampledPointsCount() ) );
151 QJsonObject stats;
152 for ( auto it = mStatisticsMap.constBegin(); it != mStatisticsMap.constEnd(); it++ )
153 {
154 const QgsPointCloudAttributeStatistics stat = it.value();
155 stats.insert( it.key(), attributeStatisticsToJson( stat ) );
156 }
157 obj.insert( QStringLiteral( "stats" ), stats );
158
159 QJsonDocument statsDoc( obj );
160 return statsDoc.toJson( QJsonDocument::Compact );
161}
162
164{
165 QJsonParseError error;
166 QJsonDocument document = QJsonDocument::fromJson( statsByteArray, &error );
167 if ( error.error != QJsonParseError::NoError )
168 {
169 QgsMessageLog::logMessage( QObject::tr( "Failed to load statistics JSON from COPC file, reason: %1" ).arg( error.errorString() ) );
171 }
172
173 QJsonObject statsJson = document.object();
174
176 stats.mSampledPointsCount = statsJson.value( QStringLiteral( "sampled-points" ) ).toInt();
177 if ( statsJson.contains( QStringLiteral( "stats" ) ) )
178 {
179 QJsonObject statsObj = statsJson.value( QStringLiteral( "stats" ) ).toObject();
180 for ( const QString &attr : statsObj.keys() )
181 {
182 QJsonObject obj = statsObj.value( attr ).toObject();
183 QgsPointCloudAttributeStatistics attrStats = fromAttributeStatisticsJson( obj );
184 attrStats.count = stats.mSampledPointsCount;
185 stats.mStatisticsMap.insert( attr, attrStats );
186 }
187 }
188 return stats;
189}
190
191QJsonObject QgsPointCloudStatistics::attributeStatisticsToJson( const QgsPointCloudAttributeStatistics &stats )
192{
193 QJsonObject obj;
194 obj.insert( QStringLiteral( "minimum" ), stats.minimum );
195 obj.insert( QStringLiteral( "maximum" ), stats.maximum );
196 obj.insert( QStringLiteral( "mean" ), stats.mean );
197 if ( !std::isnan( stats.stDev ) )
198 {
199 obj.insert( QStringLiteral( "standard-deviation" ), stats.stDev );
200 }
201 QJsonObject classCount;
202 for ( auto it = stats.classCount.constBegin(); it != stats.classCount.constEnd(); it++ )
203 {
204 classCount.insert( QString::number( it.key() ), it.value() );
205 }
206 obj.insert( QStringLiteral( "class-count" ), classCount );
207 return obj;
208}
209
210QgsPointCloudAttributeStatistics QgsPointCloudStatistics::fromAttributeStatisticsJson( QJsonObject &statsJson )
211{
213 QVariantMap m = statsJson.toVariantMap();
214 statsObj.minimum = m.value( QStringLiteral( "minimum" ), std::numeric_limits<double>::max() ).toDouble();
215 statsObj.maximum = m.value( QStringLiteral( "maximum" ), std::numeric_limits<double>::lowest() ).toDouble();
216 statsObj.mean = m.value( QStringLiteral( "mean" ), 0 ).toDouble();
217 statsObj.stDev = m.value( QStringLiteral( "standard-deviation" ), std::numeric_limits<double>::quiet_NaN() ).toDouble();
218 QJsonObject classCountJson = statsJson.value( QStringLiteral( "class-count" ) ).toObject();
219 for ( const QString &key : classCountJson.keys() )
220 {
221 statsObj.classCount.insert( key.toInt(), classCountJson.value( key ).toInt() );
222 }
223 return statsObj;
224}
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).
Attribute for point cloud data pair of name and size in bytes.
Class used to store statistics of a point cloud dataset.
static QgsPointCloudStatistics fromStatisticsJson(QByteArray stats)
Creates a statistics object from the JSON object stats.
double maximum(const QString &attribute) const
Returns the maximum value for the attribute attribute If no matching statistic is available then NaN ...
double stDev(const QString &attribute) const
Returns the standard deviation value for the attribute attribute If no matching statistic is availabl...
void clear()
Clears the statistics of all attributes.
QMap< int, int > availableClasses(const QString &attribute) const
Returns a map containing the count of each class of the attribute attribute If no matching statistic ...
QList< int > classesOf(const QString &attribute) const
Returns a list of existing classes which are present for the specified attribute.
double mean(const QString &attribute) const
Returns the mean value for the attribute attribute If no matching statistic is available then NaN wil...
void combineWith(const QgsPointCloudStatistics &stats)
Merges the current statistics with the statistics from stats.
double minimum(const QString &attribute) const
Returns the minimum value for the attribute attribute If no matching statistic is available then NaN ...
int sampledPointsCount() const
Returns the number of points used to calculate the statistics.
QByteArray toStatisticsJson() const
Converts the current statistics object into JSON object.
QgsPointCloudAttributeStatistics statisticsOf(const QString &attribute) const
Returns the calculated statistics of attribute attribute.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Class used to store statistics of one attribute of a point cloud dataset.
void cumulateStatistics(const QgsPointCloudAttributeStatistics &stats)
Updates the current point cloud statistics to hold the cumulation of the current statistics and stats...