QGIS API Documentation 4.1.0-Master (376402f9aeb)
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 <algorithm>
21
22#include "qgsbox3d.h"
24#include "qgslogger.h"
27
28#include <QDir>
29#include <QFile>
30#include <QFileInfo>
31#include <QJsonArray>
32#include <QJsonDocument>
33#include <QJsonObject>
34#include <QString>
35#include <QTime>
36#include <QtDebug>
37#include <qglobal.h>
38
39using namespace Qt::StringLiterals;
40
42 : mX( 0 )
43 , mY( 0 )
44 , mZ( 0 )
45{}
46
47QgsPointCloudNodeId::QgsPointCloudNodeId( int _d, int _x, int _y, int _z )
48 : mD( _d )
49 , mX( _x )
50 , mY( _y )
51 , mZ( _z )
52{}
53
55{
56 return QgsPointCloudNodeId( mD - 1, mX / 2, mY / 2, mZ / 2 );
57}
58
59QVector<QgsPointCloudNodeId> QgsPointCloudNodeId::childrenNodes() const
60{
61 if ( !isValid() )
62 return {};
63
64 QVector<QgsPointCloudNodeId> children;
65 children.reserve( 8 );
66 for ( int i = 0; i < 8; ++i )
67 {
68 const int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
69 children.emplace_back( mD + 1, mX * 2 + dx, mY * 2 + dy, mZ * 2 + dz );
70 }
71 return children;
72}
73
75{
76 QStringList lst = str.split( '-' );
77 if ( lst.count() != 4 )
78 return QgsPointCloudNodeId();
79 return QgsPointCloudNodeId( lst[0].toInt(), lst[1].toInt(), lst[2].toInt(), lst[3].toInt() );
80}
81
83{
84 return u"%1-%2-%3-%4"_s.arg( mD ).arg( mX ).arg( mY ).arg( mZ );
85}
86
88{
89 return mD;
90}
91
93{
94 return mX;
95}
96
98{
99 return mY;
100}
101
103{
104 return mZ;
105}
106
108
109//
110// QgsPointCloudCacheKey
111//
112
113QgsPointCloudCacheKey::QgsPointCloudCacheKey( QgsPointCloudNodeId n, const QgsPointCloudRequest &request, const QString &subset, const QString &uri )
114 : mNode( n )
115 , mUri( uri )
116 , mRequest( request )
117 , mSubsetString( subset )
118{}
119
121{
122 return mNode == other.mNode && mUri == other.mUri && mRequest == other.mRequest && mSubsetString == other.mSubsetString;
123}
124
125
126//
127// QgsPointCloudNode
128//
129
131{
132 return mBounds;
133}
134
136{
137 const double d = rootBounds.xMaximum() - rootBounds.xMinimum();
138 const double dLevel = d / pow( 2, id.d() );
139
140 const double xMin = rootBounds.xMinimum() + dLevel * id.x();
141 const double xMax = rootBounds.xMinimum() + dLevel * ( id.x() + 1 );
142 const double yMin = rootBounds.yMinimum() + dLevel * id.y();
143 const double yMax = rootBounds.yMinimum() + dLevel * ( id.y() + 1 );
144 const double zMin = rootBounds.zMinimum() + dLevel * id.z();
145 const double zMax = rootBounds.zMinimum() + dLevel * ( id.z() + 1 );
146
147 return QgsBox3D( xMin, yMin, zMin, xMax, yMax, zMax );
148}
149
150float QgsPointCloudNode::error() const
151{
152 return mError;
153}
154
156
157//
158// QgsAbstractPointCloudIndex
159//
160
162QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsAbstractPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
163
165
167
169{
170 QMutexLocker locker( &mHierarchyMutex );
171 return mHierarchy.contains( n );
172}
173
175{
176 Q_ASSERT( hasNode( id ) );
177
178 qint64 pointCount;
179 {
180 QMutexLocker locker( &mHierarchyMutex );
181 pointCount = mHierarchy.value( id, -1 );
182 }
183
184 QList<QgsPointCloudNodeId> children = id.childrenNodes();
185 children.erase( std::remove_if( children.begin(), children.end(), [this]( const QgsPointCloudNodeId &c ) { return !hasNode( c ); } ), children.end() );
186
188 return QgsPointCloudNode( id, pointCount, children, bounds.width() / mSpan, bounds );
189}
190
191bool QgsAbstractPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> & )
192{
193 return false;
194}
195
200
205
210
215
217{
218 return mSpan;
219}
220
222{
223 const QString lastExpression = mFilterExpression;
224 mFilterExpression.setExpression( subset );
225 if ( mFilterExpression.hasParserError() && !subset.isEmpty() )
226 {
227 mFilterExpression.setExpression( lastExpression );
228 return false;
229 }
230
231 // fail if expression references unknown attributes
232 int offset;
233 const QSet<QString> attributes = mFilterExpression.referencedAttributes();
234 for ( const QString &attribute : attributes )
235 {
236 if ( !mAttributes.find( attribute, offset ) )
237 {
238 mFilterExpression.setExpression( lastExpression );
239 return false;
240 }
241 }
242 return true;
243}
244
249
251{
252 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
253 statsMap["X"].minimum = mExtent.xMinimum();
254 statsMap["X"].maximum = mExtent.xMaximum();
255 statsMap["Y"].minimum = mExtent.yMinimum();
256 statsMap["Y"].maximum = mExtent.yMinimum();
257 statsMap["Z"].minimum = mZMin;
258 statsMap["Z"].maximum = mZMax;
259
260 return QgsPointCloudStatistics( pointCount(), statsMap );
261}
262
264{
265 Q_UNUSED( stats );
266 return false;
267}
268
270{
271 // Base QgsAbstractPointCloudIndex fields
272 destination->mUri = mUri;
273 destination->mExtent = mExtent;
274 destination->mZMin = mZMin;
275 destination->mZMax = mZMax;
276 destination->mHierarchy = mHierarchy;
277 destination->mScale = mScale;
278 destination->mOffset = mOffset;
279 destination->mRootBounds = mRootBounds;
280 destination->mAttributes = mAttributes;
281 destination->mSpan = mSpan;
283}
284
286{
287 QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
288
289 QMutexLocker l( &sBlockCacheMutex );
290 QgsPointCloudBlock *cached = sBlockCache.object( key );
291 return cached ? cached->clone() : nullptr;
292}
293
298
300 QgsPointCloudBlock *data, QgsPointCloudNodeId node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri
301)
302{
303 if ( !data )
304 return;
305
306 QgsPointCloudCacheKey key( node, request, expression, uri );
307
308 const int cost = data->pointCount() * data->pointRecordSize();
309
310 QMutexLocker l( &sBlockCacheMutex );
311 QgsDebugMsgLevel( u"(%1/%2): Caching node %3 of %4"_s.arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
312 sBlockCache.insert( key, data->clone(), cost );
313}
314
316{
317 return {};
318}
319
320//
321// QgsPointCloudIndex
322//
323//
324
326{
327 mIndex.reset( index );
328}
329
330QgsPointCloudIndex::operator bool() const
331{
332 return mIndex != nullptr;
333}
334
335void QgsPointCloudIndex::load( const QString &url, const QString &authcfg )
336{
337 Q_ASSERT( mIndex );
338 mIndex->load( url, authcfg );
339}
340
342{
343 return mIndex && mIndex->isValid();
344}
345
347{
348 return mIndex ? mIndex->error() : u"Index is NULL"_s;
349}
350
352{
353 Q_ASSERT( mIndex );
354 return mIndex->accessType();
355}
356
358{
359 Q_ASSERT( mIndex );
360 return mIndex->crs();
361}
362
364{
365 Q_ASSERT( mIndex );
366 return mIndex->pointCount();
367}
368
370{
371 Q_ASSERT( mIndex );
372 return mIndex->originalMetadata();
373}
374
376{
377 Q_ASSERT( mIndex );
378 return mIndex->metadataStatistics();
379}
380
382{
383 Q_ASSERT( mIndex );
384 return mIndex->writeStatistics( stats );
385}
386
388{
389 Q_ASSERT( mIndex );
390 return mIndex->root();
391}
392
394{
395 Q_ASSERT( mIndex );
396 return mIndex->hasNode( id );
397}
398
400{
401 Q_ASSERT( mIndex );
402 return mIndex->getNode( id );
403}
404
406{
407 Q_ASSERT( mIndex );
408 return mIndex->attributes();
409}
410
411std::unique_ptr<QgsPointCloudBlock> QgsPointCloudIndex::nodeData( QgsPointCloudNodeId n, const QgsPointCloudRequest &request )
412{
413 Q_ASSERT( mIndex );
414 return mIndex->nodeData( n, request );
415}
416
418{
419 Q_ASSERT( mIndex );
420 return mIndex->asyncNodeData( n, request );
421}
422
423bool QgsPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> &data )
424{
425 Q_ASSERT( mIndex );
426 return mIndex->updateNodeData( data );
427}
428
430{
431 Q_ASSERT( mIndex );
432 return mIndex->extent();
433}
434
436{
437 Q_ASSERT( mIndex );
438 return mIndex->zMin();
439}
441{
442 Q_ASSERT( mIndex );
443 return mIndex->zMax();
444}
445
447{
448 Q_ASSERT( mIndex );
449 return mIndex->rootNodeBounds();
450}
451
453{
454 Q_ASSERT( mIndex );
455 return mIndex->scale();
456}
457
459{
460 Q_ASSERT( mIndex );
461 return mIndex->offset();
462}
463
465{
466 Q_ASSERT( mIndex );
467 return mIndex->span();
468}
469
471{
472 Q_ASSERT( mIndex );
473 return mIndex->uri();
474}
475
477{
478 Q_ASSERT( mIndex );
479 return mIndex->needsHierarchyFetching( n );
480}
481
482bool QgsPointCloudIndex::setSubsetString( const QString &subset )
483{
484 Q_ASSERT( mIndex );
485 return mIndex->setSubsetString( subset );
486}
487
489{
490 Q_ASSERT( mIndex );
491 return mIndex->subsetString();
492}
493
495{
496 Q_ASSERT( mIndex );
497 return mIndex->getNodeDataFromCache( node, request );
498}
499
501{
502 Q_ASSERT( mIndex );
503 mIndex->storeNodeDataToCache( data, node, request );
504}
505
507{
508 Q_ASSERT( mIndex );
509 return mIndex->extraMetadata();
510}
511
512bool QgsPointCloudIndex::commitChanges( QString *errorMessage )
513{
514 Q_ASSERT( mIndex );
515 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
516 return index->commitChanges( errorMessage );
517
518 return false;
519}
520
522{
523 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
524 return index->isModified();
525
526 return false;
527}
528
529QList<QgsPointCloudNodeId> QgsPointCloudIndex::updatedNodes() const
530{
531 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
532 return index->updatedNodes();
533
534 return QList<QgsPointCloudNodeId>();
535}
PointCloudAccessType
The access type of the data, local is for local files and remote for remote files (over HTTP).
Definition qgis.h:6588
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 QgsPointCloudNode getNode(QgsPointCloudNodeId id) const
Returns object for a given node.
virtual qint64 pointCount() const =0
Returns the number of points in the point cloud.
QgsPointCloudAttributeCollection mAttributes
QHash< QgsPointCloudNodeId, int > mHierarchy
Data hierarchy.
virtual bool hasNode(QgsPointCloudNodeId n) const
Returns whether the octree contain given node.
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 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.
QgsPointCloudBlock * getNodeDataFromCache(QgsPointCloudNodeId node, const QgsPointCloudRequest &request)
Fetches the requested node data from the cache for the specified node and request.
void storeNodeDataToCache(QgsPointCloudBlock *data, QgsPointCloudNodeId node, const QgsPointCloudRequest &request) const
Stores existing data to the cache for the specified node and request.
QgsPointCloudExpression mFilterExpression
The filter expression to be evaluated when fetching node data.
static void storeNodeDataToCacheStatic(QgsPointCloudBlock *data, 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.
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
int mSpan
All native attributes stored in the file.
double mZMax
Vertical extent of data.
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.
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:205
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:212
double width() const
Returns the width of the box.
Definition qgsbox3d.h:287
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:261
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:233
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.
QgsPointCloudNodeId node() const
Returns the key's QgsPointCloudNodeId.
bool operator==(const QgsPointCloudCacheKey &other) const
QString uri() const
Returns the key's uri.
QgsPointCloudCacheKey(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.
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.
std::unique_ptr< QgsPointCloudBlock > nodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns node data block.
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.
QgsPointCloudNode getNode(QgsPointCloudNodeId id) const
Returns object for a given node.
bool setSubsetString(const QString &subset)
Sets the string used to define a subset of the point cloud.
bool hasNode(QgsPointCloudNodeId id) const
Returns whether the octree contain given node.
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.
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.
void storeNodeDataToCache(QgsPointCloudBlock *data, QgsPointCloudNodeId node, const QgsPointCloudRequest &request)
Stores existing data to the cache for the specified node and request.
QgsPointCloudIndex(QgsAbstractPointCloudIndex *index=nullptr)
Construct wrapper, takes ownership of index.
QgsPointCloudBlockRequest * asyncNodeData(QgsPointCloudNodeId n, const QgsPointCloudRequest &request)
Returns a handle responsible for loading a node data block.
QList< QgsPointCloudNodeId > updatedNodes() const
Returns a list of node IDs that have been modified.
QString uri() const
Returns the uri used to load the index.
bool needsHierarchyFetching(const QgsPointCloudNodeId &node) const
Returns whether calling getNode() for node will trigger a hierarchy page fetch.
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...
QgsPointCloudBlock * getNodeDataFromCache(QgsPointCloudNodeId node, const QgsPointCloudRequest &request)
Fetches the requested node data from the cache for the specified node and request.
Represents an indexed point cloud node's position in octree.
int d() const
Returns d.
int y() const
Returns y.
bool isValid() const
Returns whether node is valid.
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.
QVector< QgsPointCloudNodeId > childrenNodes() const
Returns the node's 8 direct child nodes.
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
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 QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63