QGIS API Documentation 3.41.0-Master (d2aaa9c6e02)
Loading...
Searching...
No Matches
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
19#include "qgspointcloudindex.h"
20#include <QFile>
21#include <QFileInfo>
22#include <QDir>
23#include <QJsonArray>
24#include <QJsonDocument>
25#include <QJsonObject>
26#include <QTime>
27#include <QtDebug>
28#include <qglobal.h>
29#include <qstringliteral.h>
30
31#include "qgsbox3d.h"
34#include "qgslogger.h"
36
38 mD( -1 ),
39 mX( 0 ),
40 mY( 0 ),
41 mZ( 0 )
42{}
43
44QgsPointCloudNodeId::QgsPointCloudNodeId( int _d, int _x, int _y, int _z ):
45 mD( _d ),
46 mX( _x ),
47 mY( _y ),
48 mZ( _z )
49{}
50
52{
53 return QgsPointCloudNodeId( mD - 1, mX / 2, mY / 2, mZ / 2 );
54}
55
57{
58 QStringList lst = str.split( '-' );
59 if ( lst.count() != 4 )
60 return QgsPointCloudNodeId();
61 return QgsPointCloudNodeId( lst[0].toInt(), lst[1].toInt(), lst[2].toInt(), lst[3].toInt() );
62}
63
65{
66 return QStringLiteral( "%1-%2-%3-%4" ).arg( mD ).arg( mX ).arg( mY ).arg( mZ );
67}
68
70{
71 return mD;
72}
73
75{
76 return mX;
77}
78
80{
81 return mY;
82}
83
85{
86 return mZ;
87}
88
90{
91 return id.d() + id.x() + id.y() + id.z();
92}
93
95
96//
97// QgsPointCloudCacheKey
98//
99
100QgsPointCloudCacheKey::QgsPointCloudCacheKey( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
101 : mNode( n )
102 , mUri( uri )
103 , mRequest( request )
104 , mFilterExpression( expression )
105{
106}
107
109{
110 return mNode == other.mNode &&
111 mUri == other.mUri &&
112 mRequest == other.mRequest &&
113 mFilterExpression == other.mFilterExpression;
114}
115
116uint qHash( const QgsPointCloudCacheKey &key )
117{
118 return qHash( key.node() ) ^ qHash( key.request() ) ^ qHash( key.uri() ) ^ qHash( key.filterExpression() );
119}
120
121//
122// QgsPointCloudNode
123//
124
126{
127 return mBounds;
128}
129
131{
132 const double d = rootBounds.xMaximum() - rootBounds.xMinimum();
133 const double dLevel = d / pow( 2, id.d() );
134
135 const double xMin = rootBounds.xMinimum() + dLevel * id.x();
136 const double xMax = rootBounds.xMinimum() + dLevel * ( id.x() + 1 );
137 const double yMin = rootBounds.yMinimum() + dLevel * id.y();
138 const double yMax = rootBounds.yMinimum() + dLevel * ( id.y() + 1 );
139 const double zMin = rootBounds.zMinimum() + dLevel * id.z();
140 const double zMax = rootBounds.zMinimum() + dLevel * ( id.z() + 1 );
141
142 return QgsBox3D( xMin, yMin, zMin, xMax, yMax, zMax );
143}
144
145float QgsPointCloudNode::error() const
146{
147 return mError;
148}
149
151
152//
153// QgsAbstractPointCloudIndex
154//
155
157QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsAbstractPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
158
160
162
164{
165 QMutexLocker locker( &mHierarchyMutex );
166 return mHierarchy.contains( n );
167}
168
170{
171 Q_ASSERT( hasNode( id ) );
172
173 qint64 pointCount;
174 {
175 QMutexLocker locker( &mHierarchyMutex );
176 pointCount = mHierarchy.value( id, -1 );
177 }
178
179 QList<QgsPointCloudNodeId> children;
180 {
181 const int d = id.d() + 1;
182 const int x = id.x() * 2;
183 const int y = id.y() * 2;
184 const int z = id.z() * 2;
185
186 for ( int i = 0; i < 8; ++i )
187 {
188 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
189 const QgsPointCloudNodeId n2( d, x + dx, y + dy, z + dz );
190 if ( hasNode( n2 ) )
191 children.append( n2 );
192 }
193 }
194
196 return QgsPointCloudNode( id, pointCount, children, bounds.width() / mSpan, bounds );
197}
198
199bool QgsAbstractPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> & )
200{
201 return false;
202}
203
208
213
218
223
225{
226 return mSpan;
227}
228
230{
231 const QString lastExpression = mFilterExpression;
232 mFilterExpression.setExpression( subset );
233 if ( mFilterExpression.hasParserError() && !subset.isEmpty() )
234 {
235 mFilterExpression.setExpression( lastExpression );
236 return false;
237 }
238
239 // fail if expression references unknown attributes
240 int offset;
241 const QSet<QString> attributes = mFilterExpression.referencedAttributes();
242 for ( const QString &attribute : attributes )
243 {
244 if ( !mAttributes.find( attribute, offset ) )
245 {
246 mFilterExpression.setExpression( lastExpression );
247 return false;
248 }
249 }
250 return true;
251}
252
257
259{
260 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
261 statsMap[ "X" ].minimum = mExtent.xMinimum();
262 statsMap[ "X" ].maximum = mExtent.xMaximum();
263 statsMap[ "Y" ].minimum = mExtent.yMinimum();
264 statsMap[ "Y" ].maximum = mExtent.yMinimum();
265 statsMap[ "Z" ].minimum = mZMin;
266 statsMap[ "Z" ].maximum = mZMax;
267
268 return QgsPointCloudStatistics( pointCount(), statsMap );
269}
270
272{
273 Q_UNUSED( stats );
274 return false;
275}
276
278{
279 // Base QgsAbstractPointCloudIndex fields
280 destination->mUri = mUri;
281 destination->mExtent = mExtent;
282 destination->mZMin = mZMin;
283 destination->mZMax = mZMax;
284 destination->mHierarchy = mHierarchy;
285 destination->mScale = mScale;
286 destination->mOffset = mOffset;
287 destination->mRootBounds = mRootBounds;
288 destination->mAttributes = mAttributes;
289 destination->mSpan = mSpan;
291}
292
294{
295 QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
296
297 QMutexLocker l( &sBlockCacheMutex );
298 QgsPointCloudBlock *cached = sBlockCache.object( key );
299 return cached ? cached->clone() : nullptr;
300}
301
306
307void QgsAbstractPointCloudIndex::storeNodeDataToCacheStatic( QgsPointCloudBlock *data, const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
308{
309 if ( !data )
310 return;
311
312 QgsPointCloudCacheKey key( node, request, expression, uri );
313
314 const int cost = data->pointCount() * data->pointRecordSize();
315
316 QMutexLocker l( &sBlockCacheMutex );
317 QgsDebugMsgLevel( QStringLiteral( "(%1/%2): Caching node %3 of %4" ).arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
318 sBlockCache.insert( key, data->clone(), cost );
319}
320
322{
323 return {};
324}
325
326//
327// QgsPointCloudIndex
328//
329//
330
332{
333 mIndex.reset( index );
334}
335
336QgsPointCloudIndex::operator bool() const
337{
338 return mIndex != nullptr;
339}
340
341void QgsPointCloudIndex::load( const QString &fileName )
342{
343 Q_ASSERT( mIndex );
344 mIndex->load( fileName );
345}
346
348{
349 return mIndex && mIndex->isValid();
350}
351
353{
354 return mIndex ? mIndex->error() : QStringLiteral( "Index is NULL" );
355}
356
358{
359 Q_ASSERT( mIndex );
360 return mIndex->accessType();
361}
362
364{
365 Q_ASSERT( mIndex );
366 return mIndex->crs();
367}
368
370{
371 Q_ASSERT( mIndex );
372 return mIndex->pointCount();
373}
374
376{
377 Q_ASSERT( mIndex );
378 return mIndex->originalMetadata();
379}
380
382{
383 Q_ASSERT( mIndex );
384 return mIndex->metadataStatistics();
385}
386
388{
389 Q_ASSERT( mIndex );
390 return mIndex->writeStatistics( stats );
391}
392
394{
395 Q_ASSERT( mIndex );
396 return mIndex->root();
397}
398
400{
401 Q_ASSERT( mIndex );
402 return mIndex->hasNode( id );
403}
404
406{
407 Q_ASSERT( mIndex );
408 return mIndex->getNode( id );
409}
410
412{
413 Q_ASSERT( mIndex );
414 return mIndex->attributes();
415}
416
417std::unique_ptr<QgsPointCloudBlock> QgsPointCloudIndex::nodeData( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request )
418{
419 Q_ASSERT( mIndex );
420 return mIndex->nodeData( n, request );
421}
422
424{
425 Q_ASSERT( mIndex );
426 return mIndex->asyncNodeData( n, request );
427}
428
429bool QgsPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> &data )
430{
431 Q_ASSERT( mIndex );
432 return mIndex->updateNodeData( data );
433}
434
436{
437 Q_ASSERT( mIndex );
438 return mIndex->extent();
439}
440
442{
443 Q_ASSERT( mIndex );
444 return mIndex->zMin();
445}
447{
448 Q_ASSERT( mIndex );
449 return mIndex->zMax();
450}
451
453{
454 Q_ASSERT( mIndex );
455 return mIndex->rootNodeBounds();
456}
457
459{
460 Q_ASSERT( mIndex );
461 return mIndex->scale();
462}
463
465{
466 Q_ASSERT( mIndex );
467 return mIndex->offset();
468}
469
471{
472 Q_ASSERT( mIndex );
473 return mIndex->span();
474}
475
476
477bool QgsPointCloudIndex::setSubsetString( const QString &subset )
478{
479 Q_ASSERT( mIndex );
480 return mIndex->setSubsetString( subset );
481}
482
484{
485 Q_ASSERT( mIndex );
486 return mIndex->subsetString();
487}
488
490{
491 Q_ASSERT( mIndex );
492 return mIndex->getNodeDataFromCache( node, request );
493}
494
496{
497 Q_ASSERT( mIndex );
498 mIndex->storeNodeDataToCache( data, node, request );
499}
500
502{
503 Q_ASSERT( mIndex );
504 return mIndex->extraMetadata();
505}
506
507bool QgsPointCloudIndex::commitChanges( QString *errorMessage )
508{
509 Q_ASSERT( mIndex );
510 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
511 return index->commitChanges( errorMessage );
512
513 return false;
514}
515
517{
518 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
519 return index->isModified();
520
521 return false;
522}
523
PointCloudAccessType
The access type of the data, local is for local files and remote for remote files (over HTTP).
Definition qgis.h:5731
Represents a indexed point clouds data in octree.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
virtual ~QgsAbstractPointCloudIndex()
virtual qint64 pointCount() const =0
Returns the number of points in the point cloud.
QgsPointCloudAttributeCollection mAttributes
QHash< QgsPointCloudNodeId, int > mHierarchy
Data hierarchy.
void copyCommonProperties(QgsAbstractPointCloudIndex *destination) const
Copies common properties to the destination index.
virtual bool writeStatistics(QgsPointCloudStatistics &stats)
Writes the statistics object stats into the backing file, if possible.
QgsBox3D mRootBounds
Bounds of the root node's cube (in int32 coordinates)
virtual bool hasNode(const QgsPointCloudNodeId &n) const
Returns whether the octree contain given node.
virtual QVariantMap extraMetadata() const
Returns extra metadata that's not accessible through the other methods in an implementation-specific ...
QgsVector3D mOffset
Offset of our int32 coordinates compared to CRS coords.
QgsPointCloudExpression mFilterExpression
The filter expression to be evaluated when fetching node data.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
virtual bool updateNodeData(const QHash< QgsPointCloudNodeId, QByteArray > &data)
Tries to update the data for the specified nodes.
int span() const
Returns the number of points in one direction in a single node.
static QCache< QgsPointCloudCacheKey, QgsPointCloudBlock > sBlockCache
static void storeNodeDataToCacheStatic(QgsPointCloudBlock *data, const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri)
Stores existing data to the cache for the specified node, request, expression and uri.
int mSpan
All native attributes stored in the file.
double mZMax
Vertical extent of data.
void storeNodeDataToCache(QgsPointCloudBlock *data, const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request) const
Stores existing data to the cache for the specified node and request.
QgsPointCloudBlock * getNodeDataFromCache(const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request)
Fetches the requested node data from the cache for the specified node and request.
QgsRectangle mExtent
2D extent of data
QgsVector3D scale() const
Returns scale of data relative to CRS.
virtual QgsPointCloudStatistics metadataStatistics() const
Returns the object containing the statistics metadata extracted from the dataset.
QgsVector3D mScale
Scale of our int32 coordinates compared to CRS coords.
QgsAbstractPointCloudIndex()
Constructs index.
virtual QgsPointCloudNode getNode(const QgsPointCloudNodeId &id) const
Returns object for a given node.
QgsVector3D offset() const
Returns offset of data from CRS.
bool setSubsetString(const QString &subset)
Sets the string used to define a subset of the point cloud.
QString subsetString() const
Returns the string used to define a subset of the point cloud.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:211
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:218
double width() const
Returns the width of the box.
Definition qgsbox3d.h:293
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:267
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:239
This class represents a coordinate reference system (CRS).
Collection of point cloud attributes.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
Base class for handling loading QgsPointCloudBlock asynchronously.
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.
QgsPointCloudExpression filterExpression() const
Returns the key's QgsPointCloudExpression.
QgsPointCloudCacheKey(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri)
Ctor.
QgsPointCloudRequest request() const
Returns the key's QgsPointCloudRequest.
QgsPointCloudNodeId node() const
Returns the key's QgsPointCloudNodeId.
bool operator==(const QgsPointCloudCacheKey &other) const
QString uri() const
Returns the key's uri.
The QgsPointCloudEditingIndex class is a QgsPointCloudIndex that is used as an editing buffer when ed...
int span() const
Returns the number of points in one direction in a single node.
double zMax() const
Returns z max.
bool isModified() const
Returns true if there are uncommitted changes, false otherwise.
bool writeStatistics(QgsPointCloudStatistics &stats)
Writes the statistics object stats into the backing file, if possible.
QgsBox3D rootNodeBounds() const
Returns bounding box of root node in CRS coords.
void storeNodeDataToCache(QgsPointCloudBlock *data, const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request)
Stores existing data to the cache for the specified node and request.
QString error() const
Returns the error that occurred during the loading of the index.
QString subsetString() const
Returns the string used to define a subset of the point cloud.
double zMin() const
Returns z min.
QgsVector3D offset() const
Returns offset of data from CRS.
QgsVector3D scale() const
Returns scale of data relative to CRS.
QgsPointCloudBlockRequest * asyncNodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns a handle responsible for loading a node data block.
bool setSubsetString(const QString &subset)
Sets the string used to define a subset of the point cloud.
bool isValid() const
Returns whether index is loaded and valid.
QgsRectangle extent() const
Returns extent of the data.
QgsCoordinateReferenceSystem crs() const
Returns the coordinate reference system of the point cloud index.
qint64 pointCount() const
Returns the number of points in the point cloud.
QgsPointCloudStatistics metadataStatistics() const
Returns the object containing the statistics metadata extracted from the dataset.
std::unique_ptr< QgsPointCloudBlock > nodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns node data block.
bool commitChanges(QString *errorMessage=nullptr)
Tries to store pending changes to the data provider.
QVariantMap extraMetadata() const
Returns extra metadata that's not accessible through the other methods in an implementation-specific ...
bool updateNodeData(const QHash< QgsPointCloudNodeId, QByteArray > &data)
Tries to update the data for the specified nodes.
QgsPointCloudNodeId root() const
Returns root node of the index.
QgsPointCloudIndex(QgsAbstractPointCloudIndex *index=nullptr)
Construct wrapper, takes ownership of index.
QgsPointCloudNode getNode(const QgsPointCloudNodeId &id) const
Returns object for a given node.
void load(const QString &fileName)
Loads the index from the file.
bool hasNode(const QgsPointCloudNodeId &id) const
Returns whether the octree contain given node.
QgsPointCloudBlock * getNodeDataFromCache(const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request)
Fetches the requested node data from the cache for the specified node and request.
QVariantMap originalMetadata() const
Returns the original metadata map.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
Qgis::PointCloudAccessType accessType() const
Returns the access type of the data If the access type is Remote, data will be fetched from an HTTP s...
Represents a indexed point cloud node's position in octree.
int d() const
Returns d.
int y() const
Returns y.
int x() const
Returns x.
QgsPointCloudNodeId()
Constructs invalid node.
static QgsPointCloudNodeId fromString(const QString &str)
Creates node from string.
int z() const
Returns z.
QString toString() const
Encode node to string.
QgsPointCloudNodeId parentNode() const
Returns the parent of the node.
Keeps metadata for indexed point cloud node.
float error() const
Returns node's error in map units (used to determine in whether the node has enough detail for the cu...
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
Point cloud data request.
Class used to store statistics of a point cloud dataset.
double minimum(const QString &attribute) const
Returns the minimum value for the attribute attribute If no matching statistic is available then NaN ...
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
uint qHash(QgsPointCloudNodeId id)
Hash function for indexed nodes.