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