QGIS API Documentation 3.43.0-Master (e01d6d7c4c0)
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 const double newMean = ( mean * count + stats.mean * stats.count ) / ( count + stats.count );
35 const double delta1 = newMean - mean;
36 const double variance1 = stDev * stDev * ( count - 1 ) + count * delta1 * delta1;
37 const double delta2 = newMean - stats.mean;
38 const double variance2 = stats.stDev * stats.stDev * ( stats.count - 1 ) + stats.count * delta2 * delta2;
39 const double variance = ( variance1 + variance2 ) / ( count + stats.count );
40 stDev = std::sqrt( variance );
41
42 mean = newMean;
43 count += stats.count;
44
45 for ( auto it = stats.classCount.constBegin(); it != stats.classCount.constEnd(); it++ )
46 {
47 classCount[ it.key() ] += it.value();
48 }
49}
50
52{
53 return classCount.value( cls, -1 );
54}
55
56// QgsPointCloudStatistics
57
62
63QgsPointCloudStatistics::QgsPointCloudStatistics( int sampledPointsCount, const QMap<QString, QgsPointCloudAttributeStatistics> &stats )
64 : mSampledPointsCount( sampledPointsCount ), mStatisticsMap( stats )
65{
66
67}
68
70{
71 mStatisticsMap.clear();
72}
73
74void QgsPointCloudStatistics::clear( const QVector<QgsPointCloudAttribute> &attributes )
75{
76 for ( QgsPointCloudAttribute attribute : attributes )
77 {
78 mStatisticsMap.remove( attribute.name() );
79 }
80}
81
83{
85 defaultVal.minimum = std::numeric_limits<double>::max();
86 defaultVal.maximum = std::numeric_limits<double>::lowest();
87 defaultVal.count = 0;
88 return mStatisticsMap.value( attribute, defaultVal );
89}
90
91QList<int> QgsPointCloudStatistics::classesOf( const QString &attribute ) const
92{
93 if ( !mStatisticsMap.contains( attribute ) )
94 return QList<int>();
95 QgsPointCloudAttributeStatistics s = mStatisticsMap[ attribute ];
96 return s.classCount.keys();
97}
98
99QMap<int, int> QgsPointCloudStatistics::availableClasses( const QString &attribute ) const
100{
101 if ( !mStatisticsMap.contains( attribute ) )
102 return QMap<int, int>();
103 return mStatisticsMap[ attribute ].classCount;
104}
105
106double QgsPointCloudStatistics::minimum( const QString &attribute ) const
107{
108 if ( !mStatisticsMap.contains( attribute ) )
109 return std::numeric_limits<double>::quiet_NaN();
110 return mStatisticsMap[ attribute ].minimum;
111}
112
113double QgsPointCloudStatistics::maximum( const QString &attribute ) const
114{
115 if ( !mStatisticsMap.contains( attribute ) )
116 return std::numeric_limits<double>::quiet_NaN();
117 return mStatisticsMap[ attribute ].maximum;
118}
119
120double QgsPointCloudStatistics::mean( const QString &attribute ) const
121{
122 if ( !mStatisticsMap.contains( attribute ) )
123 return std::numeric_limits<double>::quiet_NaN();
124 return mStatisticsMap[ attribute ].mean;
125}
126
127double QgsPointCloudStatistics::stDev( const QString &attribute ) const
128{
129 if ( !mStatisticsMap.contains( attribute ) )
130 return std::numeric_limits<double>::quiet_NaN();
131 return mStatisticsMap[ attribute ].stDev;
132}
133
134
136{
137 for ( auto it = stats.mStatisticsMap.constBegin(); it != stats.mStatisticsMap.constEnd(); it++ )
138 {
139 const QString attribute = it.key();
141 if ( mStatisticsMap.contains( attribute ) )
142 {
143 s.cumulateStatistics( mStatisticsMap[ attribute ] );
144 }
145 mStatisticsMap[ attribute ] = s;
146 }
147 mSampledPointsCount += stats.mSampledPointsCount;
148}
149
151{
152 QJsonObject obj;
153 obj.insert( QStringLiteral( "sampled-points" ), QJsonValue::fromVariant( sampledPointsCount() ) );
154 QJsonObject stats;
155 for ( auto it = mStatisticsMap.constBegin(); it != mStatisticsMap.constEnd(); it++ )
156 {
157 const QgsPointCloudAttributeStatistics stat = it.value();
158 stats.insert( it.key(), attributeStatisticsToJson( stat ) );
159 }
160 obj.insert( QStringLiteral( "stats" ), stats );
161
162 QJsonDocument statsDoc( obj );
163 return statsDoc.toJson( QJsonDocument::Compact );
164}
165
167{
168 QJsonParseError error;
169 QJsonDocument document = QJsonDocument::fromJson( statsByteArray, &error );
170 if ( error.error != QJsonParseError::NoError )
171 {
172 QgsMessageLog::logMessage( QObject::tr( "Failed to load statistics JSON from COPC file, reason: %1" ).arg( error.errorString() ) );
174 }
175
176 QJsonObject statsJson = document.object();
177
179 stats.mSampledPointsCount = statsJson.value( QStringLiteral( "sampled-points" ) ).toInt();
180 if ( statsJson.contains( QStringLiteral( "stats" ) ) )
181 {
182 QJsonObject statsObj = statsJson.value( QStringLiteral( "stats" ) ).toObject();
183 for ( const QString &attr : statsObj.keys() )
184 {
185 QJsonObject obj = statsObj.value( attr ).toObject();
186 QgsPointCloudAttributeStatistics attrStats = fromAttributeStatisticsJson( obj );
187 attrStats.count = stats.mSampledPointsCount;
188 stats.mStatisticsMap.insert( attr, attrStats );
189 }
190 }
191 return stats;
192}
193
194QJsonObject QgsPointCloudStatistics::attributeStatisticsToJson( const QgsPointCloudAttributeStatistics &stats )
195{
196 QJsonObject obj;
197 obj.insert( QStringLiteral( "minimum" ), stats.minimum );
198 obj.insert( QStringLiteral( "maximum" ), stats.maximum );
199 obj.insert( QStringLiteral( "mean" ), stats.mean );
200 if ( !std::isnan( stats.stDev ) )
201 {
202 obj.insert( QStringLiteral( "standard-deviation" ), stats.stDev );
203 }
204 QJsonObject classCount;
205 for ( auto it = stats.classCount.constBegin(); it != stats.classCount.constEnd(); it++ )
206 {
207 classCount.insert( QString::number( it.key() ), it.value() );
208 }
209 obj.insert( QStringLiteral( "class-count" ), classCount );
210 return obj;
211}
212
213QgsPointCloudAttributeStatistics QgsPointCloudStatistics::fromAttributeStatisticsJson( QJsonObject &statsJson )
214{
216 QVariantMap m = statsJson.toVariantMap();
217 statsObj.minimum = m.value( QStringLiteral( "minimum" ), std::numeric_limits<double>::max() ).toDouble();
218 statsObj.maximum = m.value( QStringLiteral( "maximum" ), std::numeric_limits<double>::lowest() ).toDouble();
219 statsObj.mean = m.value( QStringLiteral( "mean" ), 0 ).toDouble();
220 statsObj.stDev = m.value( QStringLiteral( "standard-deviation" ), std::numeric_limits<double>::quiet_NaN() ).toDouble();
221 QJsonObject classCountJson = statsJson.value( QStringLiteral( "class-count" ) ).toObject();
222 for ( const QString &key : classCountJson.keys() )
223 {
224 statsObj.classCount.insert( key.toInt(), classCountJson.value( key ).toInt() );
225 }
226 return statsObj;
227}
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Attribute for point cloud data pair of name and size in bytes.
Used to store statistics of a point cloud dataset.
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 ...
static QgsPointCloudStatistics fromStatisticsJson(const QByteArray &stats)
Creates a statistics object from the JSON object stats.
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.
Stores 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.