QGIS API Documentation 3.41.0-Master (d5b93354e9c)
Loading...
Searching...
No Matches
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
54{
55 return classCount.value( cls, -1 );
56}
57
58// QgsPointCloudStatistics
59
64
65QgsPointCloudStatistics::QgsPointCloudStatistics( int sampledPointsCount, const QMap<QString, QgsPointCloudAttributeStatistics> &stats )
66 : mSampledPointsCount( sampledPointsCount ), mStatisticsMap( stats )
67{
68
69}
70
72{
73 mStatisticsMap.clear();
74}
75
76void QgsPointCloudStatistics::clear( const QVector<QgsPointCloudAttribute> &attributes )
77{
78 for ( QgsPointCloudAttribute attribute : attributes )
79 {
80 mStatisticsMap.remove( attribute.name() );
81 }
82}
83
85{
87 defaultVal.minimum = std::numeric_limits<double>::max();
88 defaultVal.maximum = std::numeric_limits<double>::lowest();
89 defaultVal.count = 0;
90 return mStatisticsMap.value( attribute, defaultVal );
91}
92
93QList<int> QgsPointCloudStatistics::classesOf( const QString &attribute ) const
94{
95 if ( !mStatisticsMap.contains( attribute ) )
96 return QList<int>();
97 QgsPointCloudAttributeStatistics s = mStatisticsMap[ attribute ];
98 return s.classCount.keys();
99}
100
101QMap<int, int> QgsPointCloudStatistics::availableClasses( const QString &attribute ) const
102{
103 if ( !mStatisticsMap.contains( attribute ) )
104 return QMap<int, int>();
105 return mStatisticsMap[ attribute ].classCount;
106}
107
108double QgsPointCloudStatistics::minimum( const QString &attribute ) const
109{
110 if ( !mStatisticsMap.contains( attribute ) )
111 return std::numeric_limits<double>::quiet_NaN();
112 return mStatisticsMap[ attribute ].minimum;
113}
114
115double QgsPointCloudStatistics::maximum( const QString &attribute ) const
116{
117 if ( !mStatisticsMap.contains( attribute ) )
118 return std::numeric_limits<double>::quiet_NaN();
119 return mStatisticsMap[ attribute ].maximum;
120}
121
122double QgsPointCloudStatistics::mean( const QString &attribute ) const
123{
124 if ( !mStatisticsMap.contains( attribute ) )
125 return std::numeric_limits<double>::quiet_NaN();
126 return mStatisticsMap[ attribute ].mean;
127}
128
129double QgsPointCloudStatistics::stDev( const QString &attribute ) const
130{
131 if ( !mStatisticsMap.contains( attribute ) )
132 return std::numeric_limits<double>::quiet_NaN();
133 return mStatisticsMap[ attribute ].stDev;
134}
135
136
138{
139 for ( auto it = stats.mStatisticsMap.constBegin(); it != stats.mStatisticsMap.constEnd(); it++ )
140 {
141 const QString attribute = it.key();
143 if ( mStatisticsMap.contains( attribute ) )
144 {
145 s.cumulateStatistics( mStatisticsMap[ attribute ] );
146 }
147 mStatisticsMap[ attribute ] = s;
148 }
149 mSampledPointsCount += stats.mSampledPointsCount;
150}
151
153{
154 QJsonObject obj;
155 obj.insert( QStringLiteral( "sampled-points" ), QJsonValue::fromVariant( sampledPointsCount() ) );
156 QJsonObject stats;
157 for ( auto it = mStatisticsMap.constBegin(); it != mStatisticsMap.constEnd(); it++ )
158 {
159 const QgsPointCloudAttributeStatistics stat = it.value();
160 stats.insert( it.key(), attributeStatisticsToJson( stat ) );
161 }
162 obj.insert( QStringLiteral( "stats" ), stats );
163
164 QJsonDocument statsDoc( obj );
165 return statsDoc.toJson( QJsonDocument::Compact );
166}
167
169{
170 QJsonParseError error;
171 QJsonDocument document = QJsonDocument::fromJson( statsByteArray, &error );
172 if ( error.error != QJsonParseError::NoError )
173 {
174 QgsMessageLog::logMessage( QObject::tr( "Failed to load statistics JSON from COPC file, reason: %1" ).arg( error.errorString() ) );
176 }
177
178 QJsonObject statsJson = document.object();
179
181 stats.mSampledPointsCount = statsJson.value( QStringLiteral( "sampled-points" ) ).toInt();
182 if ( statsJson.contains( QStringLiteral( "stats" ) ) )
183 {
184 QJsonObject statsObj = statsJson.value( QStringLiteral( "stats" ) ).toObject();
185 for ( const QString &attr : statsObj.keys() )
186 {
187 QJsonObject obj = statsObj.value( attr ).toObject();
188 QgsPointCloudAttributeStatistics attrStats = fromAttributeStatisticsJson( obj );
189 attrStats.count = stats.mSampledPointsCount;
190 stats.mStatisticsMap.insert( attr, attrStats );
191 }
192 }
193 return stats;
194}
195
196QJsonObject QgsPointCloudStatistics::attributeStatisticsToJson( const QgsPointCloudAttributeStatistics &stats )
197{
198 QJsonObject obj;
199 obj.insert( QStringLiteral( "minimum" ), stats.minimum );
200 obj.insert( QStringLiteral( "maximum" ), stats.maximum );
201 obj.insert( QStringLiteral( "mean" ), stats.mean );
202 if ( !std::isnan( stats.stDev ) )
203 {
204 obj.insert( QStringLiteral( "standard-deviation" ), stats.stDev );
205 }
206 QJsonObject classCount;
207 for ( auto it = stats.classCount.constBegin(); it != stats.classCount.constEnd(); it++ )
208 {
209 classCount.insert( QString::number( it.key() ), it.value() );
210 }
211 obj.insert( QStringLiteral( "class-count" ), classCount );
212 return obj;
213}
214
215QgsPointCloudAttributeStatistics QgsPointCloudStatistics::fromAttributeStatisticsJson( QJsonObject &statsJson )
216{
218 QVariantMap m = statsJson.toVariantMap();
219 statsObj.minimum = m.value( QStringLiteral( "minimum" ), std::numeric_limits<double>::max() ).toDouble();
220 statsObj.maximum = m.value( QStringLiteral( "maximum" ), std::numeric_limits<double>::lowest() ).toDouble();
221 statsObj.mean = m.value( QStringLiteral( "mean" ), 0 ).toDouble();
222 statsObj.stDev = m.value( QStringLiteral( "standard-deviation" ), std::numeric_limits<double>::quiet_NaN() ).toDouble();
223 QJsonObject classCountJson = statsJson.value( QStringLiteral( "class-count" ) ).toObject();
224 for ( const QString &key : classCountJson.keys() )
225 {
226 statsObj.classCount.insert( key.toInt(), classCountJson.value( key ).toInt() );
227 }
228 return statsObj;
229}
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...
int singleClassCount(int cls) const
Returns the count of points in given class or -1 on error.