QGIS API Documentation 4.1.0-Master (3b8ef1f72a3)
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//
93// QgsPointCloudCacheKey
94//
95
96QgsPointCloudCacheKey::QgsPointCloudCacheKey( QgsPointCloudNodeId n, const QgsPointCloudRequest &request, const QString &subset, const QString &uri )
97 : mNode( n )
98 , mUri( uri )
99 , mRequest( request )
100 , mSubsetString( subset )
101{}
102
104{
105 return mNode == other.mNode && mUri == other.mUri && mRequest == other.mRequest && mSubsetString == other.mSubsetString;
106}
107
108
109//
110// QgsPointCloudNode
111//
112
114{
115 return mBounds;
116}
117
119{
120 const double d = rootBounds.xMaximum() - rootBounds.xMinimum();
121 const double dLevel = d / pow( 2, id.d() );
122
123 const double xMin = rootBounds.xMinimum() + dLevel * id.x();
124 const double xMax = rootBounds.xMinimum() + dLevel * ( id.x() + 1 );
125 const double yMin = rootBounds.yMinimum() + dLevel * id.y();
126 const double yMax = rootBounds.yMinimum() + dLevel * ( id.y() + 1 );
127 const double zMin = rootBounds.zMinimum() + dLevel * id.z();
128 const double zMax = rootBounds.zMinimum() + dLevel * ( id.z() + 1 );
129
130 return QgsBox3D( xMin, yMin, zMin, xMax, yMax, zMax );
131}
132
133float QgsPointCloudNode::error() const
134{
135 return mError;
136}
137
139
140//
141// QgsAbstractPointCloudIndex
142//
143
145QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsAbstractPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
146
148
150
152{
153 QMutexLocker locker( &mHierarchyMutex );
154 return mHierarchy.contains( n );
155}
156
158{
159 Q_ASSERT( hasNode( id ) );
160
161 qint64 pointCount;
162 {
163 QMutexLocker locker( &mHierarchyMutex );
164 pointCount = mHierarchy.value( id, -1 );
165 }
166
167 QList<QgsPointCloudNodeId> children;
168 {
169 const int d = id.d() + 1;
170 const int x = id.x() * 2;
171 const int y = id.y() * 2;
172 const int z = id.z() * 2;
173
174 for ( int i = 0; i < 8; ++i )
175 {
176 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
177 const QgsPointCloudNodeId n2( d, x + dx, y + dy, z + dz );
178 if ( hasNode( n2 ) )
179 children.append( n2 );
180 }
181 }
182
184 return QgsPointCloudNode( id, pointCount, children, bounds.width() / mSpan, bounds );
185}
186
187bool QgsAbstractPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> & )
188{
189 return false;
190}
191
196
201
206
211
213{
214 return mSpan;
215}
216
218{
219 const QString lastExpression = mFilterExpression;
220 mFilterExpression.setExpression( subset );
221 if ( mFilterExpression.hasParserError() && !subset.isEmpty() )
222 {
223 mFilterExpression.setExpression( lastExpression );
224 return false;
225 }
226
227 // fail if expression references unknown attributes
228 int offset;
229 const QSet<QString> attributes = mFilterExpression.referencedAttributes();
230 for ( const QString &attribute : attributes )
231 {
232 if ( !mAttributes.find( attribute, offset ) )
233 {
234 mFilterExpression.setExpression( lastExpression );
235 return false;
236 }
237 }
238 return true;
239}
240
245
247{
248 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
249 statsMap["X"].minimum = mExtent.xMinimum();
250 statsMap["X"].maximum = mExtent.xMaximum();
251 statsMap["Y"].minimum = mExtent.yMinimum();
252 statsMap["Y"].maximum = mExtent.yMinimum();
253 statsMap["Z"].minimum = mZMin;
254 statsMap["Z"].maximum = mZMax;
255
256 return QgsPointCloudStatistics( pointCount(), statsMap );
257}
258
260{
261 Q_UNUSED( stats );
262 return false;
263}
264
266{
267 // Base QgsAbstractPointCloudIndex fields
268 destination->mUri = mUri;
269 destination->mExtent = mExtent;
270 destination->mZMin = mZMin;
271 destination->mZMax = mZMax;
272 destination->mHierarchy = mHierarchy;
273 destination->mScale = mScale;
274 destination->mOffset = mOffset;
275 destination->mRootBounds = mRootBounds;
276 destination->mAttributes = mAttributes;
277 destination->mSpan = mSpan;
279}
280
282{
283 QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
284
285 QMutexLocker l( &sBlockCacheMutex );
286 QgsPointCloudBlock *cached = sBlockCache.object( key );
287 return cached ? cached->clone() : nullptr;
288}
289
294
296 QgsPointCloudBlock *data, QgsPointCloudNodeId node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri
297)
298{
299 if ( !data )
300 return;
301
302 QgsPointCloudCacheKey key( node, request, expression, uri );
303
304 const int cost = data->pointCount() * data->pointRecordSize();
305
306 QMutexLocker l( &sBlockCacheMutex );
307 QgsDebugMsgLevel( u"(%1/%2): Caching node %3 of %4"_s.arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
308 sBlockCache.insert( key, data->clone(), cost );
309}
310
312{
313 return {};
314}
315
316//
317// QgsPointCloudIndex
318//
319//
320
322{
323 mIndex.reset( index );
324}
325
326QgsPointCloudIndex::operator bool() const
327{
328 return mIndex != nullptr;
329}
330
331void QgsPointCloudIndex::load( const QString &url, const QString &authcfg )
332{
333 Q_ASSERT( mIndex );
334 mIndex->load( url, authcfg );
335}
336
338{
339 return mIndex && mIndex->isValid();
340}
341
343{
344 return mIndex ? mIndex->error() : u"Index is NULL"_s;
345}
346
348{
349 Q_ASSERT( mIndex );
350 return mIndex->accessType();
351}
352
354{
355 Q_ASSERT( mIndex );
356 return mIndex->crs();
357}
358
360{
361 Q_ASSERT( mIndex );
362 return mIndex->pointCount();
363}
364
366{
367 Q_ASSERT( mIndex );
368 return mIndex->originalMetadata();
369}
370
372{
373 Q_ASSERT( mIndex );
374 return mIndex->metadataStatistics();
375}
376
378{
379 Q_ASSERT( mIndex );
380 return mIndex->writeStatistics( stats );
381}
382
384{
385 Q_ASSERT( mIndex );
386 return mIndex->root();
387}
388
390{
391 Q_ASSERT( mIndex );
392 return mIndex->hasNode( id );
393}
394
396{
397 Q_ASSERT( mIndex );
398 return mIndex->getNode( id );
399}
400
402{
403 Q_ASSERT( mIndex );
404 return mIndex->attributes();
405}
406
407std::unique_ptr<QgsPointCloudBlock> QgsPointCloudIndex::nodeData( QgsPointCloudNodeId n, const QgsPointCloudRequest &request )
408{
409 Q_ASSERT( mIndex );
410 return mIndex->nodeData( n, request );
411}
412
414{
415 Q_ASSERT( mIndex );
416 return mIndex->asyncNodeData( n, request );
417}
418
419bool QgsPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> &data )
420{
421 Q_ASSERT( mIndex );
422 return mIndex->updateNodeData( data );
423}
424
426{
427 Q_ASSERT( mIndex );
428 return mIndex->extent();
429}
430
432{
433 Q_ASSERT( mIndex );
434 return mIndex->zMin();
435}
437{
438 Q_ASSERT( mIndex );
439 return mIndex->zMax();
440}
441
443{
444 Q_ASSERT( mIndex );
445 return mIndex->rootNodeBounds();
446}
447
449{
450 Q_ASSERT( mIndex );
451 return mIndex->scale();
452}
453
455{
456 Q_ASSERT( mIndex );
457 return mIndex->offset();
458}
459
461{
462 Q_ASSERT( mIndex );
463 return mIndex->span();
464}
465
467{
468 Q_ASSERT( mIndex );
469 return mIndex->uri();
470}
471
472bool QgsPointCloudIndex::setSubsetString( const QString &subset )
473{
474 Q_ASSERT( mIndex );
475 return mIndex->setSubsetString( subset );
476}
477
479{
480 Q_ASSERT( mIndex );
481 return mIndex->subsetString();
482}
483
485{
486 Q_ASSERT( mIndex );
487 return mIndex->getNodeDataFromCache( node, request );
488}
489
491{
492 Q_ASSERT( mIndex );
493 mIndex->storeNodeDataToCache( data, node, request );
494}
495
497{
498 Q_ASSERT( mIndex );
499 return mIndex->extraMetadata();
500}
501
502bool QgsPointCloudIndex::commitChanges( QString *errorMessage )
503{
504 Q_ASSERT( mIndex );
505 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
506 return index->commitChanges( errorMessage );
507
508 return false;
509}
510
512{
513 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
514 return index->isModified();
515
516 return false;
517}
518
519QList<QgsPointCloudNodeId> QgsPointCloudIndex::updatedNodes() const
520{
521 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
522 return index->updatedNodes();
523
524 return QList<QgsPointCloudNodeId>();
525}
PointCloudAccessType
The access type of the data, local is for local files and remote for remote files (over HTTP).
Definition qgis.h:6531
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.
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.
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