QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgspointcloudindex.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudindex.cpp
3 --------------------
4 begin : October 2020
5 copyright : (C) 2020 by Peter Petrik
6 email : zilolv 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
18#include "qgspointcloudindex.h"
19#include <QFile>
20#include <QFileInfo>
21#include <QDir>
22#include <QJsonArray>
23#include <QJsonDocument>
24#include <QJsonObject>
25#include <QTime>
26#include <QtDebug>
27
30#include "qgslogger.h"
31
33 mD( -1 ),
34 mX( 0 ),
35 mY( 0 ),
36 mZ( 0 )
37{}
38
39IndexedPointCloudNode::IndexedPointCloudNode( int _d, int _x, int _y, int _z ):
40 mD( _d ),
41 mX( _x ),
42 mY( _y ),
43 mZ( _z )
44{}
45
47{
48 return IndexedPointCloudNode( mD - 1, mX / 2, mY / 2, mZ / 2 );
49}
50
52{
53 QStringList lst = str.split( '-' );
54 if ( lst.count() != 4 )
55 return IndexedPointCloudNode();
56 return IndexedPointCloudNode( lst[0].toInt(), lst[1].toInt(), lst[2].toInt(), lst[3].toInt() );
57}
58
60{
61 return QStringLiteral( "%1-%2-%3-%4" ).arg( mD ).arg( mX ).arg( mY ).arg( mZ );
62}
63
65{
66 return mD;
67}
68
70{
71 return mX;
72}
73
75{
76 return mY;
77}
78
80{
81 return mZ;
82}
83
85{
86 return id.d() + id.x() + id.y() + id.z();
87}
88
90
91//
92// QgsPointCloudCacheKey
93//
94
95QgsPointCloudCacheKey::QgsPointCloudCacheKey( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
96 : mNode( n )
97 , mUri( uri )
98 , mRequest( request )
99 , mFilterExpression( expression )
100{
101}
102
104{
105 return mNode == other.mNode &&
106 mUri == other.mUri &&
107 mRequest == other.mRequest &&
108 mFilterExpression == other.mFilterExpression;
109}
110
111uint qHash( const QgsPointCloudCacheKey &key )
112{
113 return qHash( key.node() ) ^ qHash( key.request() ) ^ qHash( key.uri() ) ^ qHash( key.filterExpression() );
114}
115
116//
117// QgsPointCloudDataBounds
118//
119
121
122QgsPointCloudDataBounds::QgsPointCloudDataBounds( qint64 xmin, qint64 ymin, qint64 zmin, qint64 xmax, qint64 ymax, qint64 zmax )
123 : mXMin( xmin )
124 , mYMin( ymin )
125 , mZMin( zmin )
126 , mXMax( xmax )
127 , mYMax( ymax )
128 , mZMax( zmax )
129{
130
131}
132
134{
135 return mXMin;
136}
137
139{
140 return mYMin;
141}
142
144{
145 return mXMax;
146}
147
149{
150 return mYMax;
151}
152
154{
155 return mZMin;
156}
157
159{
160 return mZMax;
161}
162
164{
165 return QgsRectangle(
166 mXMin * scale.x() + offset.x(), mYMin * scale.y() + offset.y(),
167 mXMax * scale.x() + offset.x(), mYMax * scale.y() + offset.y()
168 );
169}
170
172{
173 return QgsDoubleRange( mZMin * scale.z() + offset.z(), mZMax * scale.z() + offset.z() );
174}
175
177
178//
179// QgsPointCloudIndex
180//
181
183QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
184
186
188
190{
191 QMutexLocker locker( &mHierarchyMutex );
192 return mHierarchy.contains( n );
193}
194
196{
197 QMutexLocker locker( &mHierarchyMutex );
198 return mHierarchy.value( n, -1 );
199}
200
201QList<IndexedPointCloudNode> QgsPointCloudIndex::nodeChildren( const IndexedPointCloudNode &n ) const
202{
203 QMutexLocker locker( &mHierarchyMutex );
204 Q_ASSERT( mHierarchy.contains( n ) );
205 QList<IndexedPointCloudNode> lst;
206 const int d = n.d() + 1;
207 const int x = n.x() * 2;
208 const int y = n.y() * 2;
209 const int z = n.z() * 2;
210
211 for ( int i = 0; i < 8; ++i )
212 {
213 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
214 const IndexedPointCloudNode n2( d, x + dx, y + dy, z + dz );
215 if ( mHierarchy.contains( n2 ) )
216 lst.append( n2 );
217 }
218 return lst;
219}
220
222{
223 return mAttributes;
224}
225
227{
228 qint64 xMin, yMin, zMin, xMax, yMax, zMax;
229
230 const qint64 d = mRootBounds.xMax() - mRootBounds.xMin();
231 const double dLevel = ( double )d / pow( 2, n.d() );
232
233 xMin = round( mRootBounds.xMin() + dLevel * n.x() );
234 xMax = round( mRootBounds.xMin() + dLevel * ( n.x() + 1 ) );
235 yMin = round( mRootBounds.yMin() + dLevel * n.y() );
236 yMax = round( mRootBounds.yMin() + dLevel * ( n.y() + 1 ) );
237 zMin = round( mRootBounds.zMin() + dLevel * n.z() );
238 zMax = round( mRootBounds.zMin() + dLevel * ( n.z() + 1 ) );
239
240 QgsPointCloudDataBounds db( xMin, yMin, zMin, xMax, yMax, zMax );
241 return db;
242}
243
245{
246 return nodeBounds( node ).mapExtent( mOffset, mScale );
247}
248
250{
251 return nodeBounds( node ).zRange( mOffset, mScale );
252}
253
255{
256 const double w = nodeMapExtent( n ).width();
257 return w / mSpan;
258}
259
261{
262 return mScale;
263}
264
266{
267 return mOffset;
268}
269
271{
273}
274
276{
277 return mSpan;
278}
279
281{
282 mHierarchyMutex.lock();
283 const int count = mHierarchy.value( n, -1 );
284 mHierarchyMutex.unlock();
285 return count;
286}
287
288bool QgsPointCloudIndex::setSubsetString( const QString &subset )
289{
290 const QString lastExpression = mFilterExpression;
291 mFilterExpression.setExpression( subset );
292 if ( mFilterExpression.hasParserError() && !subset.isEmpty() )
293 {
294 mFilterExpression.setExpression( lastExpression );
295 return false;
296 }
297
298 // fail if expression references unknown attributes
299 int offset;
300 const QSet<QString> attributes = mFilterExpression.referencedAttributes();
301 for ( const QString &attribute : attributes )
302 {
303 if ( !mAttributes.find( attribute, offset ) )
304 {
305 mFilterExpression.setExpression( lastExpression );
306 return false;
307 }
308 }
309 return true;
310}
311
313{
314 return mFilterExpression;
315}
316
317QVariant QgsPointCloudIndex::metadataStatistic( const QString &attribute, Qgis::Statistic statistic ) const
318{
319 if ( attribute == QLatin1String( "X" ) && statistic == Qgis::Statistic::Min )
320 return mExtent.xMinimum();
321 if ( attribute == QLatin1String( "X" ) && statistic == Qgis::Statistic::Max )
322 return mExtent.xMaximum();
323
324 if ( attribute == QLatin1String( "Y" ) && statistic == Qgis::Statistic::Min )
325 return mExtent.yMinimum();
326 if ( attribute == QLatin1String( "Y" ) && statistic == Qgis::Statistic::Max )
327 return mExtent.yMaximum();
328
329 if ( attribute == QLatin1String( "Z" ) && statistic == Qgis::Statistic::Min )
330 return mZMin;
331 if ( attribute == QLatin1String( "Z" ) && statistic == Qgis::Statistic::Max )
332 return mZMax;
333
334 return QVariant();
335}
336
337QVariantList QgsPointCloudIndex::metadataClasses( const QString &attribute ) const
338{
339 Q_UNUSED( attribute );
340 return QVariantList();
341}
342
343QVariant QgsPointCloudIndex::metadataClassStatistic( const QString &attribute, const QVariant &value, Qgis::Statistic statistic ) const
344{
345 Q_UNUSED( attribute );
346 Q_UNUSED( value );
347 Q_UNUSED( statistic );
348 return QVariant();
349}
350
352{
353 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
354 for ( QgsPointCloudAttribute attribute : attributes().attributes() )
355 {
356 QString name = attribute.name();
358 QVariant min = metadataStatistic( name, Qgis::Statistic::Min );
359 QVariant max = metadataStatistic( name, Qgis::Statistic::Max );
360 QVariant mean = metadataStatistic( name, Qgis::Statistic::Mean );
361 QVariant stDev = metadataStatistic( name, Qgis::Statistic::StDev );
362 if ( !min.isValid() )
363 continue;
364
365 s.minimum = min.toDouble();
366 s.maximum = max.toDouble();
367 s.mean = mean.toDouble();
368 s.stDev = stDev.toDouble();
370 QVariantList classes = metadataClasses( name );
371 for ( QVariant c : classes )
372 {
373 s.classCount[ c.toInt() ] = metadataClassStatistic( name, c, Qgis::Statistic::Count ).toInt();
374 }
375 statsMap[ name ] = s;
376 }
377 return QgsPointCloudStatistics( pointCount(), statsMap );
378}
379
381{
382 // Base QgsPointCloudIndex fields
383 destination->mUri = mUri;
384 destination->mExtent = mExtent;
385 destination->mZMin = mZMin;
386 destination->mZMax = mZMax;
387 destination->mHierarchy = mHierarchy;
388 destination->mScale = mScale;
389 destination->mOffset = mOffset;
390 destination->mRootBounds = mRootBounds;
391 destination->mAttributes = mAttributes;
392 destination->mSpan = mSpan;
394}
395
397{
398 QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
399
400 QMutexLocker l( &sBlockCacheMutex );
401 QgsPointCloudBlock *cached = sBlockCache.object( key );
402 return cached ? cached->clone() : nullptr;
403}
404
406{
407 storeNodeDataToCacheStatic( data, node, request, mFilterExpression, mUri );
408}
409
410void QgsPointCloudIndex::storeNodeDataToCacheStatic( QgsPointCloudBlock *data, const IndexedPointCloudNode &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
411{
412 if ( !data )
413 return;
414
415 QgsPointCloudCacheKey key( node, request, expression, uri );
416
417 const int cost = data->pointCount() * data->pointRecordSize();
418
419 QMutexLocker l( &sBlockCacheMutex );
420 QgsDebugMsgLevel( QStringLiteral( "(%1/%2): Caching node %3 of %4" ).arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
421 sBlockCache.insert( key, data->clone(), cost );
422}
Represents a indexed point cloud node in octree.
int y() const
Returns y.
static IndexedPointCloudNode fromString(const QString &str)
Creates node from string.
int x() const
Returns x.
QString toString() const
Encode node to string.
int d() const
Returns d.
IndexedPointCloudNode parentNode() const
Returns the parent of the node.
int z() const
Returns z.
IndexedPointCloudNode()
Constructs invalid node.
Statistic
Available generic statistics.
Definition: qgis.h:4747
@ StDev
Standard deviation of values.
@ Mean
Mean of values.
@ Max
Max of values.
@ Min
Min of values.
QgsRange which stores a range of double values.
Definition: qgsrange.h:231
Collection of point cloud attributes.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
Attribute for point cloud data pair of name and size in bytes.
Base class for storing raw data from point cloud nodes.
int pointCount() const
Returns number of points that are stored in the block.
int pointRecordSize() const
Returns the total size of each individual point record.
QgsPointCloudBlock * clone() const
Clones the QgsPointCloudBlock returning a new copy.
Container class for QgsPointCloudBlock cache keys.
IndexedPointCloudNode node() const
Returns the key's IndexedPointCloudNode.
QgsPointCloudExpression filterExpression() const
Returns the key's QgsPointCloudExpression.
QgsPointCloudRequest request() const
Returns the key's QgsPointCloudRequest.
QgsPointCloudCacheKey(const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri)
Ctor.
bool operator==(const QgsPointCloudCacheKey &other) const
Compares keys.
QString uri() const
Returns the key's uri.
Represents packaged data bounds.
qint64 xMin() const
Returns x min.
qint64 zMin() const
Returns z min.
qint64 yMax() const
Returns y max.
qint64 xMax() const
Returns x max.
QgsDoubleRange zRange(const QgsVector3D &offset, const QgsVector3D &scale) const
Returns the z range, applying the specified offset and scale.
QgsPointCloudDataBounds()
Constructs invalid bounds.
QgsRectangle mapExtent(const QgsVector3D &offset, const QgsVector3D &scale) const
Returns 2D rectangle in map coordinates.
qint64 zMax() const
Returns z max.
qint64 yMin() const
Returns y min.
Represents a indexed point clouds data in octree.
int span() const
Returns the number of points in one direction in a single node.
double zMax() const
Returns z max.
QgsPointCloudBlock * getNodeDataFromCache(const IndexedPointCloudNode &node, const QgsPointCloudRequest &request)
Fetches the requested node data from the cache for the specified node and request.
static void storeNodeDataToCacheStatic(QgsPointCloudBlock *data, const IndexedPointCloudNode &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri)
Stores existing data to the cache for the specified node, request, expression and uri.
virtual qint64 nodePointCount(const IndexedPointCloudNode &n) const
Returns the number of points of a given node n.
QgsRectangle nodeMapExtent(const IndexedPointCloudNode &node) const
Returns the extent of a node in map coordinates.
virtual QList< IndexedPointCloudNode > nodeChildren(const IndexedPointCloudNode &n) const
Returns all children of node.
QgsPointCloudIndex()
Constructs index.
QString subsetString() const
Returns the string used to define a subset of the point cloud.
double zMin() const
Returns z min.
void storeNodeDataToCache(QgsPointCloudBlock *data, const IndexedPointCloudNode &node, const QgsPointCloudRequest &request)
Stores existing data to the cache for the specified node and request.
QgsVector3D offset() const
Returns offset.
QgsVector3D scale() const
Returns scale.
virtual qint64 pointCount() const =0
Returns the number of points in the point cloud.
virtual QVariantList metadataClasses(const QString &attribute) const
Returns the classes of attribute.
void copyCommonProperties(QgsPointCloudIndex *destination) const
Copies common properties to the destination index.
bool setSubsetString(const QString &subset)
Sets the string used to define a subset of the point cloud.
QHash< IndexedPointCloudNode, int > mHierarchy
Data hierarchy.
double mZMax
Vertical extent of data.
QgsPointCloudDataBounds mRootBounds
Bounds of the root node's cube (in int32 coordinates)
virtual QgsPointCloudStatistics metadataStatistics() const
Returns the object containing the statistics metadata extracted from the dataset.
QgsRectangle mExtent
2D extent of data
QgsPointCloudAttributeCollection mAttributes
QgsPointCloudDataBounds nodeBounds(const IndexedPointCloudNode &node) const
Returns bounds of particular node.
virtual bool hasNode(const IndexedPointCloudNode &n) const
Returns whether the octree contain given node.
QgsVector3D mOffset
Offset of our int32 coordinates compared to CRS coords.
static QMutex sBlockCacheMutex
QgsDoubleRange nodeZRange(const IndexedPointCloudNode &node) const
Returns the z range of a node.
static QCache< QgsPointCloudCacheKey, QgsPointCloudBlock > sBlockCache
virtual QVariant metadataStatistic(const QString &attribute, Qgis::Statistic statistic) const
Returns the statistic statistic of attribute.
float nodeError(const IndexedPointCloudNode &n) const
Returns node's error in map units (used to determine in whether the node has enough detail for the cu...
int mSpan
All native attributes stored in the file.
QgsVector3D mScale
Scale of our int32 coordinates compared to CRS coords.
virtual QVariant metadataClassStatistic(const QString &attribute, const QVariant &value, Qgis::Statistic statistic) const
Returns the statistic statistic of the class value of the attribute attribute.
QgsPointCloudExpression mFilterExpression
The filter expression to be evaluated when fetching node data.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
Point cloud data request.
Class used to store statistics of a point cloud dataset.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:236
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:196
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition: qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:50
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:52
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:48
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
#define str(x)
Definition: qgis.cpp:38
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
uint qHash(IndexedPointCloudNode id)
Hash function for indexed nodes.
Class used to store statistics of one attribute of a point cloud dataset.