QGIS API Documentation 3.99.0-Master (d270888f95f)
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"
26
27#include <QDir>
28#include <QFile>
29#include <QFileInfo>
30#include <QJsonArray>
31#include <QJsonDocument>
32#include <QJsonObject>
33#include <QString>
34#include <QTime>
35#include <QtDebug>
36#include <qglobal.h>
37#include <qstringliteral.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
60{
61 QStringList lst = str.split( '-' );
62 if ( lst.count() != 4 )
63 return QgsPointCloudNodeId();
64 return QgsPointCloudNodeId( lst[0].toInt(), lst[1].toInt(), lst[2].toInt(), lst[3].toInt() );
65}
66
68{
69 return u"%1-%2-%3-%4"_s.arg( mD ).arg( mX ).arg( mY ).arg( mZ );
70}
71
73{
74 return mD;
75}
76
78{
79 return mX;
80}
81
83{
84 return mY;
85}
86
88{
89 return mZ;
90}
91
93{
94 return id.d() + id.x() + id.y() + id.z();
95}
96
98
99//
100// QgsPointCloudCacheKey
101//
102
103QgsPointCloudCacheKey::QgsPointCloudCacheKey( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request, const QString &subset, const QString &uri )
104 : mNode( n )
105 , mUri( uri )
106 , mRequest( request )
107 , mSubsetString( subset )
108{
109}
110
112{
113 return mNode == other.mNode &&
114 mUri == other.mUri &&
115 mRequest == other.mRequest &&
116 mSubsetString == other.mSubsetString;
117}
118
119uint qHash( const QgsPointCloudCacheKey &key )
120{
121 return qHash( key.node() ) ^ qHash( key.request() ) ^ qHash( key.uri() ) ^ qHash( key.subsetString() );
122}
123
124//
125// QgsPointCloudNode
126//
127
129{
130 return mBounds;
131}
132
134{
135 const double d = rootBounds.xMaximum() - rootBounds.xMinimum();
136 const double dLevel = d / pow( 2, id.d() );
137
138 const double xMin = rootBounds.xMinimum() + dLevel * id.x();
139 const double xMax = rootBounds.xMinimum() + dLevel * ( id.x() + 1 );
140 const double yMin = rootBounds.yMinimum() + dLevel * id.y();
141 const double yMax = rootBounds.yMinimum() + dLevel * ( id.y() + 1 );
142 const double zMin = rootBounds.zMinimum() + dLevel * id.z();
143 const double zMax = rootBounds.zMinimum() + dLevel * ( id.z() + 1 );
144
145 return QgsBox3D( xMin, yMin, zMin, xMax, yMax, zMax );
146}
147
148float QgsPointCloudNode::error() const
149{
150 return mError;
151}
152
154
155//
156// QgsAbstractPointCloudIndex
157//
158
160QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsAbstractPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
161
163
165
167{
168 QMutexLocker locker( &mHierarchyMutex );
169 return mHierarchy.contains( n );
170}
171
173{
174 Q_ASSERT( hasNode( id ) );
175
176 qint64 pointCount;
177 {
178 QMutexLocker locker( &mHierarchyMutex );
179 pointCount = mHierarchy.value( id, -1 );
180 }
181
182 QList<QgsPointCloudNodeId> children;
183 {
184 const int d = id.d() + 1;
185 const int x = id.x() * 2;
186 const int y = id.y() * 2;
187 const int z = id.z() * 2;
188
189 for ( int i = 0; i < 8; ++i )
190 {
191 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
192 const QgsPointCloudNodeId n2( d, x + dx, y + dy, z + dz );
193 if ( hasNode( n2 ) )
194 children.append( n2 );
195 }
196 }
197
199 return QgsPointCloudNode( id, pointCount, children, bounds.width() / mSpan, bounds );
200}
201
202bool QgsAbstractPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> & )
203{
204 return false;
205}
206
211
216
221
226
228{
229 return mSpan;
230}
231
233{
234 const QString lastExpression = mFilterExpression;
235 mFilterExpression.setExpression( subset );
236 if ( mFilterExpression.hasParserError() && !subset.isEmpty() )
237 {
238 mFilterExpression.setExpression( lastExpression );
239 return false;
240 }
241
242 // fail if expression references unknown attributes
243 int offset;
244 const QSet<QString> attributes = mFilterExpression.referencedAttributes();
245 for ( const QString &attribute : attributes )
246 {
247 if ( !mAttributes.find( attribute, offset ) )
248 {
249 mFilterExpression.setExpression( lastExpression );
250 return false;
251 }
252 }
253 return true;
254}
255
260
262{
263 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
264 statsMap[ "X" ].minimum = mExtent.xMinimum();
265 statsMap[ "X" ].maximum = mExtent.xMaximum();
266 statsMap[ "Y" ].minimum = mExtent.yMinimum();
267 statsMap[ "Y" ].maximum = mExtent.yMinimum();
268 statsMap[ "Z" ].minimum = mZMin;
269 statsMap[ "Z" ].maximum = mZMax;
270
271 return QgsPointCloudStatistics( pointCount(), statsMap );
272}
273
275{
276 Q_UNUSED( stats );
277 return false;
278}
279
281{
282 // Base QgsAbstractPointCloudIndex fields
283 destination->mUri = mUri;
284 destination->mExtent = mExtent;
285 destination->mZMin = mZMin;
286 destination->mZMax = mZMax;
287 destination->mHierarchy = mHierarchy;
288 destination->mScale = mScale;
289 destination->mOffset = mOffset;
290 destination->mRootBounds = mRootBounds;
291 destination->mAttributes = mAttributes;
292 destination->mSpan = mSpan;
294}
295
297{
298 QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
299
300 QMutexLocker l( &sBlockCacheMutex );
301 QgsPointCloudBlock *cached = sBlockCache.object( key );
302 return cached ? cached->clone() : nullptr;
303}
304
309
310void QgsAbstractPointCloudIndex::storeNodeDataToCacheStatic( QgsPointCloudBlock *data, const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
311{
312 if ( !data )
313 return;
314
315 QgsPointCloudCacheKey key( node, request, expression, uri );
316
317 const int cost = data->pointCount() * data->pointRecordSize();
318
319 QMutexLocker l( &sBlockCacheMutex );
320 QgsDebugMsgLevel( u"(%1/%2): Caching node %3 of %4"_s.arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
321 sBlockCache.insert( key, data->clone(), cost );
322}
323
325{
326 return {};
327}
328
329//
330// QgsPointCloudIndex
331//
332//
333
335{
336 mIndex.reset( index );
337}
338
339QgsPointCloudIndex::operator bool() const
340{
341 return mIndex != nullptr;
342}
343
344void QgsPointCloudIndex::load( const QString &url, const QString &authcfg )
345{
346 Q_ASSERT( mIndex );
347 mIndex->load( url, authcfg );
348}
349
351{
352 return mIndex && mIndex->isValid();
353}
354
356{
357 return mIndex ? mIndex->error() : u"Index is NULL"_s;
358}
359
361{
362 Q_ASSERT( mIndex );
363 return mIndex->accessType();
364}
365
367{
368 Q_ASSERT( mIndex );
369 return mIndex->crs();
370}
371
373{
374 Q_ASSERT( mIndex );
375 return mIndex->pointCount();
376}
377
379{
380 Q_ASSERT( mIndex );
381 return mIndex->originalMetadata();
382}
383
385{
386 Q_ASSERT( mIndex );
387 return mIndex->metadataStatistics();
388}
389
391{
392 Q_ASSERT( mIndex );
393 return mIndex->writeStatistics( stats );
394}
395
397{
398 Q_ASSERT( mIndex );
399 return mIndex->root();
400}
401
403{
404 Q_ASSERT( mIndex );
405 return mIndex->hasNode( id );
406}
407
409{
410 Q_ASSERT( mIndex );
411 return mIndex->getNode( id );
412}
413
415{
416 Q_ASSERT( mIndex );
417 return mIndex->attributes();
418}
419
420std::unique_ptr<QgsPointCloudBlock> QgsPointCloudIndex::nodeData( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request )
421{
422 Q_ASSERT( mIndex );
423 return mIndex->nodeData( n, request );
424}
425
427{
428 Q_ASSERT( mIndex );
429 return mIndex->asyncNodeData( n, request );
430}
431
432bool QgsPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> &data )
433{
434 Q_ASSERT( mIndex );
435 return mIndex->updateNodeData( data );
436}
437
439{
440 Q_ASSERT( mIndex );
441 return mIndex->extent();
442}
443
445{
446 Q_ASSERT( mIndex );
447 return mIndex->zMin();
448}
450{
451 Q_ASSERT( mIndex );
452 return mIndex->zMax();
453}
454
456{
457 Q_ASSERT( mIndex );
458 return mIndex->rootNodeBounds();
459}
460
462{
463 Q_ASSERT( mIndex );
464 return mIndex->scale();
465}
466
468{
469 Q_ASSERT( mIndex );
470 return mIndex->offset();
471}
472
474{
475 Q_ASSERT( mIndex );
476 return mIndex->span();
477}
478
479
480bool QgsPointCloudIndex::setSubsetString( const QString &subset )
481{
482 Q_ASSERT( mIndex );
483 return mIndex->setSubsetString( subset );
484}
485
487{
488 Q_ASSERT( mIndex );
489 return mIndex->subsetString();
490}
491
493{
494 Q_ASSERT( mIndex );
495 return mIndex->getNodeDataFromCache( node, request );
496}
497
499{
500 Q_ASSERT( mIndex );
501 mIndex->storeNodeDataToCache( data, node, request );
502}
503
505{
506 Q_ASSERT( mIndex );
507 return mIndex->extraMetadata();
508}
509
510bool QgsPointCloudIndex::commitChanges( QString *errorMessage )
511{
512 Q_ASSERT( mIndex );
513 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
514 return index->commitChanges( errorMessage );
515
516 return false;
517}
518
520{
521 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
522 return index->isModified();
523
524 return false;
525}
526
527QList<QgsPointCloudNodeId> QgsPointCloudIndex::updatedNodes() const
528{
529 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
530 return index->updatedNodes();
531
532 return QList<QgsPointCloudNodeId>();
533}
PointCloudAccessType
The access type of the data, local is for local files and remote for remote files (over HTTP).
Definition qgis.h:6340
Represents an 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.
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.
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.