QGIS API Documentation 3.99.0-Master (a8f284845db)
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
18#include "qgspointcloudindex.h"
19
20#include "qgsbox3d.h"
22#include "qgslogger.h"
25
26#include <QDir>
27#include <QFile>
28#include <QFileInfo>
29#include <QJsonArray>
30#include <QJsonDocument>
31#include <QJsonObject>
32#include <QString>
33#include <QTime>
34#include <QtDebug>
35#include <qglobal.h>
36
37using namespace Qt::StringLiterals;
38
40 : mX( 0 )
41 , mY( 0 )
42 , mZ( 0 )
43{}
44
45QgsPointCloudNodeId::QgsPointCloudNodeId( int _d, int _x, int _y, int _z )
46 : mD( _d )
47 , mX( _x )
48 , mY( _y )
49 , mZ( _z )
50{}
51
53{
54 return QgsPointCloudNodeId( mD - 1, mX / 2, mY / 2, mZ / 2 );
55}
56
58{
59 QStringList lst = str.split( '-' );
60 if ( lst.count() != 4 )
61 return QgsPointCloudNodeId();
62 return QgsPointCloudNodeId( lst[0].toInt(), lst[1].toInt(), lst[2].toInt(), lst[3].toInt() );
63}
64
66{
67 return u"%1-%2-%3-%4"_s.arg( mD ).arg( mX ).arg( mY ).arg( mZ );
68}
69
71{
72 return mD;
73}
74
76{
77 return mX;
78}
79
81{
82 return mY;
83}
84
86{
87 return mZ;
88}
89
91{
92 return id.d() + id.x() + id.y() + id.z();
93}
94
96
97//
98// QgsPointCloudCacheKey
99//
100
101QgsPointCloudCacheKey::QgsPointCloudCacheKey( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request, const QString &subset, const QString &uri )
102 : mNode( n )
103 , mUri( uri )
104 , mRequest( request )
105 , mSubsetString( subset )
106{
107}
108
110{
111 return mNode == other.mNode &&
112 mUri == other.mUri &&
113 mRequest == other.mRequest &&
114 mSubsetString == other.mSubsetString;
115}
116
117uint qHash( const QgsPointCloudCacheKey &key )
118{
119 return qHash( key.node() ) ^ qHash( key.request() ) ^ qHash( key.uri() ) ^ qHash( key.subsetString() );
120}
121
122//
123// QgsPointCloudNode
124//
125
127{
128 return mBounds;
129}
130
132{
133 const double d = rootBounds.xMaximum() - rootBounds.xMinimum();
134 const double dLevel = d / pow( 2, id.d() );
135
136 const double xMin = rootBounds.xMinimum() + dLevel * id.x();
137 const double xMax = rootBounds.xMinimum() + dLevel * ( id.x() + 1 );
138 const double yMin = rootBounds.yMinimum() + dLevel * id.y();
139 const double yMax = rootBounds.yMinimum() + dLevel * ( id.y() + 1 );
140 const double zMin = rootBounds.zMinimum() + dLevel * id.z();
141 const double zMax = rootBounds.zMinimum() + dLevel * ( id.z() + 1 );
142
143 return QgsBox3D( xMin, yMin, zMin, xMax, yMax, zMax );
144}
145
146float QgsPointCloudNode::error() const
147{
148 return mError;
149}
150
152
153//
154// QgsAbstractPointCloudIndex
155//
156
158QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsAbstractPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
159
161
163
165{
166 QMutexLocker locker( &mHierarchyMutex );
167 return mHierarchy.contains( n );
168}
169
171{
172 Q_ASSERT( hasNode( id ) );
173
174 qint64 pointCount;
175 {
176 QMutexLocker locker( &mHierarchyMutex );
177 pointCount = mHierarchy.value( id, -1 );
178 }
179
180 QList<QgsPointCloudNodeId> children;
181 {
182 const int d = id.d() + 1;
183 const int x = id.x() * 2;
184 const int y = id.y() * 2;
185 const int z = id.z() * 2;
186
187 for ( int i = 0; i < 8; ++i )
188 {
189 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
190 const QgsPointCloudNodeId n2( d, x + dx, y + dy, z + dz );
191 if ( hasNode( n2 ) )
192 children.append( n2 );
193 }
194 }
195
197 return QgsPointCloudNode( id, pointCount, children, bounds.width() / mSpan, bounds );
198}
199
200bool QgsAbstractPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> & )
201{
202 return false;
203}
204
209
214
219
224
226{
227 return mSpan;
228}
229
231{
232 const QString lastExpression = mFilterExpression;
233 mFilterExpression.setExpression( subset );
234 if ( mFilterExpression.hasParserError() && !subset.isEmpty() )
235 {
236 mFilterExpression.setExpression( lastExpression );
237 return false;
238 }
239
240 // fail if expression references unknown attributes
241 int offset;
242 const QSet<QString> attributes = mFilterExpression.referencedAttributes();
243 for ( const QString &attribute : attributes )
244 {
245 if ( !mAttributes.find( attribute, offset ) )
246 {
247 mFilterExpression.setExpression( lastExpression );
248 return false;
249 }
250 }
251 return true;
252}
253
258
260{
261 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
262 statsMap[ "X" ].minimum = mExtent.xMinimum();
263 statsMap[ "X" ].maximum = mExtent.xMaximum();
264 statsMap[ "Y" ].minimum = mExtent.yMinimum();
265 statsMap[ "Y" ].maximum = mExtent.yMinimum();
266 statsMap[ "Z" ].minimum = mZMin;
267 statsMap[ "Z" ].maximum = mZMax;
268
269 return QgsPointCloudStatistics( pointCount(), statsMap );
270}
271
273{
274 Q_UNUSED( stats );
275 return false;
276}
277
279{
280 // Base QgsAbstractPointCloudIndex fields
281 destination->mUri = mUri;
282 destination->mExtent = mExtent;
283 destination->mZMin = mZMin;
284 destination->mZMax = mZMax;
285 destination->mHierarchy = mHierarchy;
286 destination->mScale = mScale;
287 destination->mOffset = mOffset;
288 destination->mRootBounds = mRootBounds;
289 destination->mAttributes = mAttributes;
290 destination->mSpan = mSpan;
292}
293
295{
296 QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
297
298 QMutexLocker l( &sBlockCacheMutex );
299 QgsPointCloudBlock *cached = sBlockCache.object( key );
300 return cached ? cached->clone() : nullptr;
301}
302
307
308void QgsAbstractPointCloudIndex::storeNodeDataToCacheStatic( QgsPointCloudBlock *data, const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
309{
310 if ( !data )
311 return;
312
313 QgsPointCloudCacheKey key( node, request, expression, uri );
314
315 const int cost = data->pointCount() * data->pointRecordSize();
316
317 QMutexLocker l( &sBlockCacheMutex );
318 QgsDebugMsgLevel( u"(%1/%2): Caching node %3 of %4"_s.arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
319 sBlockCache.insert( key, data->clone(), cost );
320}
321
323{
324 return {};
325}
326
327//
328// QgsPointCloudIndex
329//
330//
331
333{
334 mIndex.reset( index );
335}
336
337QgsPointCloudIndex::operator bool() const
338{
339 return mIndex != nullptr;
340}
341
342void QgsPointCloudIndex::load( const QString &url, const QString &authcfg )
343{
344 Q_ASSERT( mIndex );
345 mIndex->load( url, authcfg );
346}
347
349{
350 return mIndex && mIndex->isValid();
351}
352
354{
355 return mIndex ? mIndex->error() : u"Index is NULL"_s;
356}
357
359{
360 Q_ASSERT( mIndex );
361 return mIndex->accessType();
362}
363
365{
366 Q_ASSERT( mIndex );
367 return mIndex->crs();
368}
369
371{
372 Q_ASSERT( mIndex );
373 return mIndex->pointCount();
374}
375
377{
378 Q_ASSERT( mIndex );
379 return mIndex->originalMetadata();
380}
381
383{
384 Q_ASSERT( mIndex );
385 return mIndex->metadataStatistics();
386}
387
389{
390 Q_ASSERT( mIndex );
391 return mIndex->writeStatistics( stats );
392}
393
395{
396 Q_ASSERT( mIndex );
397 return mIndex->root();
398}
399
401{
402 Q_ASSERT( mIndex );
403 return mIndex->hasNode( id );
404}
405
407{
408 Q_ASSERT( mIndex );
409 return mIndex->getNode( id );
410}
411
413{
414 Q_ASSERT( mIndex );
415 return mIndex->attributes();
416}
417
418std::unique_ptr<QgsPointCloudBlock> QgsPointCloudIndex::nodeData( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request )
419{
420 Q_ASSERT( mIndex );
421 return mIndex->nodeData( n, request );
422}
423
425{
426 Q_ASSERT( mIndex );
427 return mIndex->asyncNodeData( n, request );
428}
429
430bool QgsPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> &data )
431{
432 Q_ASSERT( mIndex );
433 return mIndex->updateNodeData( data );
434}
435
437{
438 Q_ASSERT( mIndex );
439 return mIndex->extent();
440}
441
443{
444 Q_ASSERT( mIndex );
445 return mIndex->zMin();
446}
448{
449 Q_ASSERT( mIndex );
450 return mIndex->zMax();
451}
452
454{
455 Q_ASSERT( mIndex );
456 return mIndex->rootNodeBounds();
457}
458
460{
461 Q_ASSERT( mIndex );
462 return mIndex->scale();
463}
464
466{
467 Q_ASSERT( mIndex );
468 return mIndex->offset();
469}
470
472{
473 Q_ASSERT( mIndex );
474 return mIndex->span();
475}
476
478{
479 Q_ASSERT( mIndex );
480 return mIndex->uri();
481}
482
483bool QgsPointCloudIndex::setSubsetString( const QString &subset )
484{
485 Q_ASSERT( mIndex );
486 return mIndex->setSubsetString( subset );
487}
488
490{
491 Q_ASSERT( mIndex );
492 return mIndex->subsetString();
493}
494
496{
497 Q_ASSERT( mIndex );
498 return mIndex->getNodeDataFromCache( node, request );
499}
500
502{
503 Q_ASSERT( mIndex );
504 mIndex->storeNodeDataToCache( data, node, request );
505}
506
508{
509 Q_ASSERT( mIndex );
510 return mIndex->extraMetadata();
511}
512
513bool QgsPointCloudIndex::commitChanges( QString *errorMessage )
514{
515 Q_ASSERT( mIndex );
516 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
517 return index->commitChanges( errorMessage );
518
519 return false;
520}
521
523{
524 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
525 return index->isModified();
526
527 return false;
528}
529
530QList<QgsPointCloudNodeId> QgsPointCloudIndex::updatedNodes() const
531{
532 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
533 return index->updatedNodes();
534
535 return QList<QgsPointCloudNodeId>();
536}
PointCloudAccessType
The access type of the data, local is for local files and remote for remote files (over HTTP).
Definition qgis.h:6390
Represents an indexed point clouds data in octree.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Sets native attributes of the data.
QString uri() const
Returns the URI used to load the index.
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.
virtual bool setSubsetString(const QString &subset)
Sets the string used to define a subset of the point cloud.
virtual 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:45
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:198
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:205
double width() const
Returns the width of the box.
Definition qgsbox3d.h:280
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:254
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:226
Represents a coordinate reference system (CRS).
A collection of point cloud attributes.
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.
QString subsetString() const
Returns the key's subset string. This is used in the point cloud index as a filter expression.
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.
QgsPointCloudCacheKey(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request, const QString &subset, const QString &uri)
Ctor.
A QgsPointCloudIndex that is used as an editing buffer when editing point cloud data.
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.
void load(const QString &url, const QString &authcfg=QString())
Loads the index from a url.
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.
QList< QgsPointCloudNodeId > updatedNodes() const
Returns a list of node IDs that have been modified.
bool hasNode(const QgsPointCloudNodeId &id) const
Returns whether the octree contain given node.
QString uri() const
Returns the uri used to load the index.
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 an 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 an 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.
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.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
uint qHash(QgsPointCloudNodeId id)
Hash function for indexed nodes.