QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
109{
110 return mNode == other.mNode && mUri == other.mUri && mRequest == other.mRequest && mSubsetString == other.mSubsetString;
111}
112
113uint qHash( const QgsPointCloudCacheKey &key )
114{
115 return qHash( key.node() ) ^ qHash( key.request() ) ^ qHash( key.uri() ) ^ qHash( key.subsetString() );
116}
117
118//
119// QgsPointCloudNode
120//
121
123{
124 return mBounds;
125}
126
128{
129 const double d = rootBounds.xMaximum() - rootBounds.xMinimum();
130 const double dLevel = d / pow( 2, id.d() );
131
132 const double xMin = rootBounds.xMinimum() + dLevel * id.x();
133 const double xMax = rootBounds.xMinimum() + dLevel * ( id.x() + 1 );
134 const double yMin = rootBounds.yMinimum() + dLevel * id.y();
135 const double yMax = rootBounds.yMinimum() + dLevel * ( id.y() + 1 );
136 const double zMin = rootBounds.zMinimum() + dLevel * id.z();
137 const double zMax = rootBounds.zMinimum() + dLevel * ( id.z() + 1 );
138
139 return QgsBox3D( xMin, yMin, zMin, xMax, yMax, zMax );
140}
141
142float QgsPointCloudNode::error() const
143{
144 return mError;
145}
146
148
149//
150// QgsAbstractPointCloudIndex
151//
152
154QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsAbstractPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
155
157
159
161{
162 QMutexLocker locker( &mHierarchyMutex );
163 return mHierarchy.contains( n );
164}
165
167{
168 Q_ASSERT( hasNode( id ) );
169
170 qint64 pointCount;
171 {
172 QMutexLocker locker( &mHierarchyMutex );
173 pointCount = mHierarchy.value( id, -1 );
174 }
175
176 QList<QgsPointCloudNodeId> children;
177 {
178 const int d = id.d() + 1;
179 const int x = id.x() * 2;
180 const int y = id.y() * 2;
181 const int z = id.z() * 2;
182
183 for ( int i = 0; i < 8; ++i )
184 {
185 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
186 const QgsPointCloudNodeId n2( d, x + dx, y + dy, z + dz );
187 if ( hasNode( n2 ) )
188 children.append( n2 );
189 }
190 }
191
193 return QgsPointCloudNode( id, pointCount, children, bounds.width() / mSpan, bounds );
194}
195
196bool QgsAbstractPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> & )
197{
198 return false;
199}
200
205
210
215
220
222{
223 return mSpan;
224}
225
227{
228 const QString lastExpression = mFilterExpression;
229 mFilterExpression.setExpression( subset );
230 if ( mFilterExpression.hasParserError() && !subset.isEmpty() )
231 {
232 mFilterExpression.setExpression( lastExpression );
233 return false;
234 }
235
236 // fail if expression references unknown attributes
237 int offset;
238 const QSet<QString> attributes = mFilterExpression.referencedAttributes();
239 for ( const QString &attribute : attributes )
240 {
241 if ( !mAttributes.find( attribute, offset ) )
242 {
243 mFilterExpression.setExpression( lastExpression );
244 return false;
245 }
246 }
247 return true;
248}
249
254
256{
257 QMap<QString, QgsPointCloudAttributeStatistics> statsMap;
258 statsMap["X"].minimum = mExtent.xMinimum();
259 statsMap["X"].maximum = mExtent.xMaximum();
260 statsMap["Y"].minimum = mExtent.yMinimum();
261 statsMap["Y"].maximum = mExtent.yMinimum();
262 statsMap["Z"].minimum = mZMin;
263 statsMap["Z"].maximum = mZMax;
264
265 return QgsPointCloudStatistics( pointCount(), statsMap );
266}
267
269{
270 Q_UNUSED( stats );
271 return false;
272}
273
275{
276 // Base QgsAbstractPointCloudIndex fields
277 destination->mUri = mUri;
278 destination->mExtent = mExtent;
279 destination->mZMin = mZMin;
280 destination->mZMax = mZMax;
281 destination->mHierarchy = mHierarchy;
282 destination->mScale = mScale;
283 destination->mOffset = mOffset;
284 destination->mRootBounds = mRootBounds;
285 destination->mAttributes = mAttributes;
286 destination->mSpan = mSpan;
288}
289
291{
292 QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
293
294 QMutexLocker l( &sBlockCacheMutex );
295 QgsPointCloudBlock *cached = sBlockCache.object( key );
296 return cached ? cached->clone() : nullptr;
297}
298
303
305 QgsPointCloudBlock *data, const QgsPointCloudNodeId &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri
306)
307{
308 if ( !data )
309 return;
310
311 QgsPointCloudCacheKey key( node, request, expression, uri );
312
313 const int cost = data->pointCount() * data->pointRecordSize();
314
315 QMutexLocker l( &sBlockCacheMutex );
316 QgsDebugMsgLevel( u"(%1/%2): Caching node %3 of %4"_s.arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
317 sBlockCache.insert( key, data->clone(), cost );
318}
319
321{
322 return {};
323}
324
325//
326// QgsPointCloudIndex
327//
328//
329
331{
332 mIndex.reset( index );
333}
334
335QgsPointCloudIndex::operator bool() const
336{
337 return mIndex != nullptr;
338}
339
340void QgsPointCloudIndex::load( const QString &url, const QString &authcfg )
341{
342 Q_ASSERT( mIndex );
343 mIndex->load( url, authcfg );
344}
345
347{
348 return mIndex && mIndex->isValid();
349}
350
352{
353 return mIndex ? mIndex->error() : u"Index is NULL"_s;
354}
355
357{
358 Q_ASSERT( mIndex );
359 return mIndex->accessType();
360}
361
363{
364 Q_ASSERT( mIndex );
365 return mIndex->crs();
366}
367
369{
370 Q_ASSERT( mIndex );
371 return mIndex->pointCount();
372}
373
375{
376 Q_ASSERT( mIndex );
377 return mIndex->originalMetadata();
378}
379
381{
382 Q_ASSERT( mIndex );
383 return mIndex->metadataStatistics();
384}
385
387{
388 Q_ASSERT( mIndex );
389 return mIndex->writeStatistics( stats );
390}
391
393{
394 Q_ASSERT( mIndex );
395 return mIndex->root();
396}
397
399{
400 Q_ASSERT( mIndex );
401 return mIndex->hasNode( id );
402}
403
405{
406 Q_ASSERT( mIndex );
407 return mIndex->getNode( id );
408}
409
411{
412 Q_ASSERT( mIndex );
413 return mIndex->attributes();
414}
415
416std::unique_ptr<QgsPointCloudBlock> QgsPointCloudIndex::nodeData( const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request )
417{
418 Q_ASSERT( mIndex );
419 return mIndex->nodeData( n, request );
420}
421
423{
424 Q_ASSERT( mIndex );
425 return mIndex->asyncNodeData( n, request );
426}
427
428bool QgsPointCloudIndex::updateNodeData( const QHash<QgsPointCloudNodeId, QByteArray> &data )
429{
430 Q_ASSERT( mIndex );
431 return mIndex->updateNodeData( data );
432}
433
435{
436 Q_ASSERT( mIndex );
437 return mIndex->extent();
438}
439
441{
442 Q_ASSERT( mIndex );
443 return mIndex->zMin();
444}
446{
447 Q_ASSERT( mIndex );
448 return mIndex->zMax();
449}
450
452{
453 Q_ASSERT( mIndex );
454 return mIndex->rootNodeBounds();
455}
456
458{
459 Q_ASSERT( mIndex );
460 return mIndex->scale();
461}
462
464{
465 Q_ASSERT( mIndex );
466 return mIndex->offset();
467}
468
470{
471 Q_ASSERT( mIndex );
472 return mIndex->span();
473}
474
476{
477 Q_ASSERT( mIndex );
478 return mIndex->uri();
479}
480
481bool QgsPointCloudIndex::setSubsetString( const QString &subset )
482{
483 Q_ASSERT( mIndex );
484 return mIndex->setSubsetString( subset );
485}
486
488{
489 Q_ASSERT( mIndex );
490 return mIndex->subsetString();
491}
492
494{
495 Q_ASSERT( mIndex );
496 return mIndex->getNodeDataFromCache( node, request );
497}
498
500{
501 Q_ASSERT( mIndex );
502 mIndex->storeNodeDataToCache( data, node, request );
503}
504
506{
507 Q_ASSERT( mIndex );
508 return mIndex->extraMetadata();
509}
510
511bool QgsPointCloudIndex::commitChanges( QString *errorMessage )
512{
513 Q_ASSERT( mIndex );
514 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
515 return index->commitChanges( errorMessage );
516
517 return false;
518}
519
521{
522 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
523 return index->isModified();
524
525 return false;
526}
527
528QList<QgsPointCloudNodeId> QgsPointCloudIndex::updatedNodes() const
529{
530 if ( QgsPointCloudEditingIndex *index = dynamic_cast<QgsPointCloudEditingIndex *>( mIndex.get() ) )
531 return index->updatedNodes();
532
533 return QList<QgsPointCloudNodeId>();
534}
PointCloudAccessType
The access type of the data, local is for local files and remote for remote files (over HTTP).
Definition qgis.h:6432
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: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.
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.