QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
22#include "qgsmessagelog.h"
24
25#include <QJsonDocument>
26#include <QJsonObject>
27
28// QgsPointCloudAttributeStatistics
29
31{
32 minimum = std::min( minimum, stats.minimum );
33 maximum = std::max( maximum, stats.maximum );
34
35 const double newMean = ( mean * count + stats.mean * stats.count ) / ( count + stats.count );
36 const double delta1 = newMean - mean;
37 const double variance1 = stDev * stDev * ( count - 1 ) + count * delta1 * delta1;
38 const double delta2 = newMean - stats.mean;
39 const double variance2 = stats.stDev * stats.stDev * ( stats.count - 1 ) + stats.count * delta2 * delta2;
40 const double variance = ( variance1 + variance2 ) / ( count + stats.count );
41 stDev = std::sqrt( variance );
42
43 mean = newMean;
44 count += stats.count;
45
46 for ( auto it = stats.classCount.constBegin(); it != stats.classCount.constEnd(); it++ )
47 {
48 classCount[ it.key() ] += it.value();
49 }
50}
51
53{
54 return classCount.value( cls, -1 );
55}
56
57// QgsPointCloudStatistics
58
63
64QgsPointCloudStatistics::QgsPointCloudStatistics( int sampledPointsCount, const QMap<QString, QgsPointCloudAttributeStatistics> &stats )
65 : mSampledPointsCount( sampledPointsCount ), mStatisticsMap( stats )
66{
67
68}
69
71{
72 mStatisticsMap.clear();
73}
74
75void QgsPointCloudStatistics::clear( const QVector<QgsPointCloudAttribute> &attributes )
76{
77 for ( QgsPointCloudAttribute attribute : attributes )
78 {
79 mStatisticsMap.remove( attribute.name() );
80 }
81}
82
84{
86 defaultVal.minimum = std::numeric_limits<double>::max();
87 defaultVal.maximum = std::numeric_limits<double>::lowest();
88 defaultVal.count = 0;
89 return mStatisticsMap.value( attribute, defaultVal );
90}
91
92QList<int> QgsPointCloudStatistics::classesOf( const QString &attribute ) const
93{
94 if ( !mStatisticsMap.contains( attribute ) )
95 return QList<int>();
96 QgsPointCloudAttributeStatistics s = mStatisticsMap[ attribute ];
97 return s.classCount.keys();
98}
99
100QMap<int, int> QgsPointCloudStatistics::availableClasses( const QString &attribute ) const
101{
102 if ( !mStatisticsMap.contains( attribute ) )
103 return QMap<int, int>();
104 return mStatisticsMap[ attribute ].classCount;
105}
106
107double QgsPointCloudStatistics::minimum( const QString &attribute ) const
108{
109 if ( !mStatisticsMap.contains( attribute ) )
110 return std::numeric_limits<double>::quiet_NaN();
111 return mStatisticsMap[ attribute ].minimum;
112}
113
114double QgsPointCloudStatistics::maximum( const QString &attribute ) const
115{
116 if ( !mStatisticsMap.contains( attribute ) )
117 return std::numeric_limits<double>::quiet_NaN();
118 return mStatisticsMap[ attribute ].maximum;
119}
120
121double QgsPointCloudStatistics::mean( const QString &attribute ) const
122{
123 if ( !mStatisticsMap.contains( attribute ) )
124 return std::numeric_limits<double>::quiet_NaN();
125 return mStatisticsMap[ attribute ].mean;
126}
127
128double QgsPointCloudStatistics::stDev( const QString &attribute ) const
129{
130 if ( !mStatisticsMap.contains( attribute ) )
131 return std::numeric_limits<double>::quiet_NaN();
132 return mStatisticsMap[ attribute ].stDev;
133}
134
135
137{
138 for ( auto it = stats.mStatisticsMap.constBegin(); it != stats.mStatisticsMap.constEnd(); it++ )
139 {
140 const QString attribute = it.key();
142 if ( mStatisticsMap.contains( attribute ) )
143 {
144 s.cumulateStatistics( mStatisticsMap[ attribute ] );
145 }
146 mStatisticsMap[ attribute ] = s;
147 }
148 mSampledPointsCount += stats.mSampledPointsCount;
149}
150
152{
153 QJsonObject obj;
154 obj.insert( QStringLiteral( "sampled-points" ), QJsonValue::fromVariant( sampledPointsCount() ) );
155 QJsonObject stats;
156 for ( auto it = mStatisticsMap.constBegin(); it != mStatisticsMap.constEnd(); it++ )
157 {
158 const QgsPointCloudAttributeStatistics stat = it.value();
159 stats.insert( it.key(), attributeStatisticsToJson( stat ) );
160 }
161 obj.insert( QStringLiteral( "stats" ), stats );
162
163 QJsonDocument statsDoc( obj );
164 return statsDoc.toJson( QJsonDocument::Compact );
165}
166
168{
169 QJsonParseError error;
170 QJsonDocument document = QJsonDocument::fromJson( statsByteArray, &error );
171 if ( error.error != QJsonParseError::NoError )
172 {
173 QgsMessageLog::logMessage( QObject::tr( "Failed to load statistics JSON from COPC file, reason: %1" ).arg( error.errorString() ) );
175 }
176
177 QJsonObject statsJson = document.object();
178
180 stats.mSampledPointsCount = statsJson.value( QStringLiteral( "sampled-points" ) ).toInt();
181 if ( statsJson.contains( QStringLiteral( "stats" ) ) )
182 {
183 QJsonObject statsObj = statsJson.value( QStringLiteral( "stats" ) ).toObject();
184 for ( const QString &attr : statsObj.keys() )
185 {
186 QJsonObject obj = statsObj.value( attr ).toObject();
187 QgsPointCloudAttributeStatistics attrStats = fromAttributeStatisticsJson( obj );
188 attrStats.count = stats.mSampledPointsCount;
189 stats.mStatisticsMap.insert( attr, attrStats );
190 }
191 }
192 return stats;
193}
194
195QJsonObject QgsPointCloudStatistics::attributeStatisticsToJson( const QgsPointCloudAttributeStatistics &stats )
196{
197 QJsonObject obj;
198 obj.insert( QStringLiteral( "minimum" ), stats.minimum );
199 obj.insert( QStringLiteral( "maximum" ), stats.maximum );
200 obj.insert( QStringLiteral( "mean" ), stats.mean );
201 if ( !std::isnan( stats.stDev ) )
202 {
203 obj.insert( QStringLiteral( "standard-deviation" ), stats.stDev );
204 }
205 QJsonObject classCount;
206 for ( auto it = stats.classCount.constBegin(); it != stats.classCount.constEnd(); it++ )
207 {
208 classCount.insert( QString::number( it.key() ), it.value() );
209 }
210 obj.insert( QStringLiteral( "class-count" ), classCount );
211 return obj;
212}
213
214QgsPointCloudAttributeStatistics QgsPointCloudStatistics::fromAttributeStatisticsJson( QJsonObject &statsJson )
215{
216 QgsPointCloudAttributeStatistics statsObj;
217 QVariantMap m = statsJson.toVariantMap();
218 statsObj.minimum = m.value( QStringLiteral( "minimum" ), std::numeric_limits<double>::max() ).toDouble();
219 statsObj.maximum = m.value( QStringLiteral( "maximum" ), std::numeric_limits<double>::lowest() ).toDouble();
220 statsObj.mean = m.value( QStringLiteral( "mean" ), 0 ).toDouble();
221 statsObj.stDev = m.value( QStringLiteral( "standard-deviation" ), std::numeric_limits<double>::quiet_NaN() ).toDouble();
222 QJsonObject classCountJson = statsJson.value( QStringLiteral( "class-count" ) ).toObject();
223 for ( const QString &key : classCountJson.keys() )
224 {
225 statsObj.classCount.insert( key.toInt(), classCountJson.value( key ).toInt() );
226 }
227 return statsObj;
228}
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.
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.