mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-07 00:03:52 -05:00
Add a static QCache to QgsPointCloudIndex for storing decoded data blocks
This commit is contained in:
parent
2d7d2dde06
commit
1285a8dfe2
@ -34,6 +34,12 @@ Ctor
|
||||
%End
|
||||
virtual ~QgsPointCloudBlock();
|
||||
|
||||
QgsPointCloudBlock *clone() const;
|
||||
%Docstring
|
||||
Clones the QgsPointCloudBlock returning a new copy.
|
||||
Caller takes ownership of the returned object.
|
||||
%End
|
||||
|
||||
const char *data() const;
|
||||
%Docstring
|
||||
Returns raw pointer to data
|
||||
|
||||
@ -34,6 +34,12 @@ Ctor
|
||||
%End
|
||||
virtual ~QgsPointCloudBlock();
|
||||
|
||||
QgsPointCloudBlock *clone() const;
|
||||
%Docstring
|
||||
Clones the QgsPointCloudBlock returning a new copy.
|
||||
Caller takes ownership of the returned object.
|
||||
%End
|
||||
|
||||
const char *data() const;
|
||||
%Docstring
|
||||
Returns raw pointer to data
|
||||
|
||||
@ -138,6 +138,11 @@ bool QgsCopcPointCloudIndex::loadSchema( QgsLazInfo &lazInfo )
|
||||
|
||||
std::unique_ptr<QgsPointCloudBlock> QgsCopcPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
|
||||
{
|
||||
if ( QgsPointCloudBlock *cached = getNodeDataFromCache( n, request ) )
|
||||
{
|
||||
return std::unique_ptr<QgsPointCloudBlock>( cached );
|
||||
}
|
||||
|
||||
const bool found = fetchNodeHierarchy( n );
|
||||
if ( !found )
|
||||
return nullptr;
|
||||
@ -164,7 +169,9 @@ std::unique_ptr<QgsPointCloudBlock> QgsCopcPointCloudIndex::nodeData( const Inde
|
||||
}
|
||||
QgsRectangle filterRect = request.filterRect();
|
||||
|
||||
return QgsLazDecoder::decompressCopc( rawBlockData, *mLazInfo.get(), pointCount, requestAttributes, filterExpression, filterRect );
|
||||
std::unique_ptr<QgsPointCloudBlock> decoded = QgsLazDecoder::decompressCopc( rawBlockData, *mLazInfo.get(), pointCount, requestAttributes, filterExpression, filterRect );
|
||||
storeNodeDataToCache( decoded.get(), n, request );
|
||||
return decoded;
|
||||
}
|
||||
|
||||
QgsPointCloudBlockRequest *QgsCopcPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
|
||||
|
||||
@ -309,6 +309,11 @@ bool QgsEptPointCloudIndex::loadSchema( const QByteArray &dataJson )
|
||||
|
||||
std::unique_ptr<QgsPointCloudBlock> QgsEptPointCloudIndex::nodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
|
||||
{
|
||||
if ( QgsPointCloudBlock *cached = getNodeDataFromCache( n, request ) )
|
||||
{
|
||||
return std::unique_ptr<QgsPointCloudBlock>( cached );
|
||||
}
|
||||
|
||||
mHierarchyMutex.lock();
|
||||
const bool found = mHierarchy.contains( n );
|
||||
mHierarchyMutex.unlock();
|
||||
@ -323,25 +328,25 @@ std::unique_ptr<QgsPointCloudBlock> QgsEptPointCloudIndex::nodeData( const Index
|
||||
requestAttributes.extend( attributes(), filterExpression.referencedAttributes() );
|
||||
QgsRectangle filterRect = request.filterRect();
|
||||
|
||||
std::unique_ptr<QgsPointCloudBlock> decoded;
|
||||
if ( mDataType == QLatin1String( "binary" ) )
|
||||
{
|
||||
const QString filename = QStringLiteral( "%1/ept-data/%2.bin" ).arg( mDirectory, n.toString() );
|
||||
return QgsEptDecoder::decompressBinary( filename, attributes(), requestAttributes, scale(), offset(), filterExpression, filterRect );
|
||||
decoded = QgsEptDecoder::decompressBinary( filename, attributes(), requestAttributes, scale(), offset(), filterExpression, filterRect );
|
||||
}
|
||||
else if ( mDataType == QLatin1String( "zstandard" ) )
|
||||
{
|
||||
const QString filename = QStringLiteral( "%1/ept-data/%2.zst" ).arg( mDirectory, n.toString() );
|
||||
return QgsEptDecoder::decompressZStandard( filename, attributes(), request.attributes(), scale(), offset(), filterExpression, filterRect );
|
||||
decoded = QgsEptDecoder::decompressZStandard( filename, attributes(), request.attributes(), scale(), offset(), filterExpression, filterRect );
|
||||
}
|
||||
else if ( mDataType == QLatin1String( "laszip" ) )
|
||||
{
|
||||
const QString filename = QStringLiteral( "%1/ept-data/%2.laz" ).arg( mDirectory, n.toString() );
|
||||
return QgsLazDecoder::decompressLaz( filename, requestAttributes, filterExpression, filterRect );
|
||||
}
|
||||
else
|
||||
{
|
||||
return nullptr; // unsupported
|
||||
decoded = QgsLazDecoder::decompressLaz( filename, requestAttributes, filterExpression, filterRect );
|
||||
}
|
||||
|
||||
storeNodeDataToCache( decoded.get(), n, request );
|
||||
return decoded;
|
||||
}
|
||||
|
||||
QgsPointCloudBlockRequest *QgsEptPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
|
||||
|
||||
@ -34,6 +34,11 @@ QgsPointCloudBlock::QgsPointCloudBlock(
|
||||
{
|
||||
}
|
||||
|
||||
QgsPointCloudBlock *QgsPointCloudBlock::clone() const
|
||||
{
|
||||
return new QgsPointCloudBlock( *this );
|
||||
}
|
||||
|
||||
const char *QgsPointCloudBlock::data() const
|
||||
{
|
||||
return mStorage.data();
|
||||
|
||||
@ -45,6 +45,12 @@ class CORE_EXPORT QgsPointCloudBlock
|
||||
//! Dtor
|
||||
virtual ~QgsPointCloudBlock() = default;
|
||||
|
||||
/**
|
||||
* Clones the QgsPointCloudBlock returning a new copy.
|
||||
* Caller takes ownership of the returned object.
|
||||
*/
|
||||
QgsPointCloudBlock *clone() const;
|
||||
|
||||
//! Returns raw pointer to data
|
||||
const char *data() const;
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
|
||||
#include "qgstiledownloadmanager.h"
|
||||
#include "qgspointcloudstatistics.h"
|
||||
#include "qgslogger.h"
|
||||
|
||||
IndexedPointCloudNode::IndexedPointCloudNode():
|
||||
mD( -1 ),
|
||||
@ -87,6 +88,31 @@ uint qHash( IndexedPointCloudNode id )
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
//
|
||||
// QgsPointCloudCacheKey
|
||||
//
|
||||
|
||||
QgsPointCloudCacheKey::QgsPointCloudCacheKey( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
|
||||
: mNode( n )
|
||||
, mUri( uri )
|
||||
, mRequest( request )
|
||||
, mFilterExpression( expression )
|
||||
{
|
||||
}
|
||||
|
||||
bool QgsPointCloudCacheKey::operator==( const QgsPointCloudCacheKey &other ) const
|
||||
{
|
||||
return mNode == other.mNode &&
|
||||
mUri == other.mUri &&
|
||||
mRequest == other.mRequest &&
|
||||
mFilterExpression == other.mFilterExpression;
|
||||
}
|
||||
|
||||
uint qHash( const QgsPointCloudCacheKey &key )
|
||||
{
|
||||
return qHash( key.node() ) ^ qHash( key.request() ) ^ qHash( key.uri() ) ^ qHash( key.filterExpression() );
|
||||
}
|
||||
|
||||
//
|
||||
// QgsPointCloudDataBounds
|
||||
//
|
||||
@ -153,6 +179,9 @@ QgsDoubleRange QgsPointCloudDataBounds::zRange( const QgsVector3D &offset, const
|
||||
// QgsPointCloudIndex
|
||||
//
|
||||
|
||||
QMutex QgsPointCloudIndex::sBlockCacheMutex;
|
||||
QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
|
||||
|
||||
QgsPointCloudIndex::QgsPointCloudIndex() = default;
|
||||
|
||||
QgsPointCloudIndex::~QgsPointCloudIndex() = default;
|
||||
@ -364,3 +393,28 @@ void QgsPointCloudIndex::copyCommonProperties( QgsPointCloudIndex *destination )
|
||||
destination->mSpan = mSpan;
|
||||
destination->mFilterExpression = mFilterExpression;
|
||||
}
|
||||
|
||||
QgsPointCloudBlock *QgsPointCloudIndex::getNodeDataFromCache( const IndexedPointCloudNode &node, const QgsPointCloudRequest &request )
|
||||
{
|
||||
QgsPointCloudCacheKey key( node, request, mFilterExpression, mUri );
|
||||
|
||||
QMutexLocker l( &sBlockCacheMutex );
|
||||
QgsPointCloudBlock *cached = sBlockCache.object( key );
|
||||
return cached ? cached->clone() : nullptr;
|
||||
}
|
||||
|
||||
void QgsPointCloudIndex::storeNodeDataToCache( QgsPointCloudBlock *data, const IndexedPointCloudNode &node, const QgsPointCloudRequest &request )
|
||||
{
|
||||
storeNodeDataToCacheStatic( data, node, request, mFilterExpression, mUri );
|
||||
}
|
||||
|
||||
void QgsPointCloudIndex::storeNodeDataToCacheStatic( QgsPointCloudBlock *data, const IndexedPointCloudNode &node, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri )
|
||||
{
|
||||
QgsPointCloudCacheKey key( node, request, expression, uri );
|
||||
|
||||
const int cost = data->pointCount() * data->pointRecordSize();
|
||||
|
||||
QMutexLocker l( &sBlockCacheMutex );
|
||||
QgsDebugMsgLevel( QStringLiteral( "(%1/%2): Caching node %3 of %4" ).arg( sBlockCache.totalCost() ).arg( sBlockCache.maxCost() ).arg( key.node().toString() ).arg( key.uri() ), 4 );
|
||||
sBlockCache.insert( key, data->clone(), cost );
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
#include <QVector>
|
||||
#include <QList>
|
||||
#include <QMutex>
|
||||
#include <QCache>
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgsrectangle.h"
|
||||
@ -34,6 +35,7 @@
|
||||
#include "qgsrange.h"
|
||||
#include "qgspointcloudattribute.h"
|
||||
#include "qgspointcloudexpression.h"
|
||||
#include "qgspointcloudrequest.h"
|
||||
|
||||
#define SIP_NO_FILE
|
||||
|
||||
@ -105,6 +107,46 @@ Q_DECLARE_TYPEINFO( IndexedPointCloudNode, Q_PRIMITIVE_TYPE );
|
||||
//! Hash function for indexed nodes
|
||||
CORE_EXPORT uint qHash( IndexedPointCloudNode id );
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
*
|
||||
* \brief Container class for QgsPointCloudBlock cache keys
|
||||
*
|
||||
* \note The API is considered EXPERIMENTAL and can be changed without a notice
|
||||
*
|
||||
* \since QGIS 3.36
|
||||
*/
|
||||
class CORE_EXPORT QgsPointCloudCacheKey
|
||||
{
|
||||
public:
|
||||
//! Ctor
|
||||
QgsPointCloudCacheKey( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request, const QgsPointCloudExpression &expression, const QString &uri );
|
||||
|
||||
//! Compares keys
|
||||
bool operator==( const QgsPointCloudCacheKey &other ) const;
|
||||
|
||||
//! Returns the key's IndexedPointCloudNode
|
||||
IndexedPointCloudNode node() const { return mNode; }
|
||||
|
||||
//! Returns the key's uri
|
||||
QString uri() const { return mUri; }
|
||||
|
||||
//! Returns the key's QgsPointCloudRequest
|
||||
QgsPointCloudRequest request() const { return mRequest; }
|
||||
|
||||
//! Returns the key's QgsPointCloudExpression
|
||||
QgsPointCloudExpression filterExpression() const { return mFilterExpression; }
|
||||
|
||||
private:
|
||||
IndexedPointCloudNode mNode;
|
||||
QString mUri;
|
||||
QgsPointCloudRequest mRequest;
|
||||
QgsPointCloudExpression mFilterExpression;
|
||||
};
|
||||
|
||||
//! Hash function for QgsPointCloudCacheKey
|
||||
uint qHash( const QgsPointCloudCacheKey &key );
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
*
|
||||
@ -330,6 +372,24 @@ class CORE_EXPORT QgsPointCloudIndex: public QObject
|
||||
*/
|
||||
void copyCommonProperties( QgsPointCloudIndex *destination ) const;
|
||||
|
||||
/**
|
||||
* Fetches the requested node data from the cache for the specified \a node and \a request.
|
||||
* If not found in the cache, nullptr is returned.
|
||||
* Caller takes ownership of the returned object.
|
||||
*/
|
||||
QgsPointCloudBlock *getNodeDataFromCache( const IndexedPointCloudNode &node, const QgsPointCloudRequest &request );
|
||||
|
||||
/**
|
||||
* Stores existing \a data to the cache for the specified \a node and \a request. Ownership is not transferred, block gets cloned in the cache.
|
||||
*/
|
||||
void storeNodeDataToCache( QgsPointCloudBlock *data, const IndexedPointCloudNode &node, const QgsPointCloudRequest &request );
|
||||
|
||||
/**
|
||||
* Stores existing \a data to the cache for the specified \a node, \a request, \a expression and \a uri. Ownership is not transferred, block gets cloned in the cache.
|
||||
*/
|
||||
static void storeNodeDataToCacheStatic( QgsPointCloudBlock *data, const IndexedPointCloudNode &node, const QgsPointCloudRequest &request,
|
||||
const QgsPointCloudExpression &expression, const QString &uri );
|
||||
|
||||
protected: //TODO private
|
||||
//! Sets native attributes of the data
|
||||
void setAttributes( const QgsPointCloudAttributeCollection &attributes );
|
||||
@ -348,6 +408,8 @@ class CORE_EXPORT QgsPointCloudIndex: public QObject
|
||||
|
||||
QString mError;
|
||||
QString mUri;
|
||||
static QMutex sBlockCacheMutex;
|
||||
static QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> sBlockCache;
|
||||
};
|
||||
|
||||
#endif // QGSPOINTCLOUDINDEX_H
|
||||
|
||||
@ -21,6 +21,12 @@
|
||||
|
||||
QgsPointCloudRequest::QgsPointCloudRequest() = default;
|
||||
|
||||
bool QgsPointCloudRequest::operator==( const QgsPointCloudRequest &other ) const
|
||||
{
|
||||
return mFilterRect == other.filterRect() &&
|
||||
mAttributes.toFields() == other.attributes().toFields(); //todo: QgsPointCloudAttributeCollection::operator==
|
||||
}
|
||||
|
||||
QgsPointCloudAttributeCollection QgsPointCloudRequest::attributes() const
|
||||
{
|
||||
return mAttributes;
|
||||
@ -30,3 +36,8 @@ void QgsPointCloudRequest::setAttributes( const QgsPointCloudAttributeCollection
|
||||
{
|
||||
mAttributes = attributes;
|
||||
}
|
||||
|
||||
uint qHash( const QgsPointCloudRequest &request )
|
||||
{
|
||||
return qHash( request.filterRect() ) ^ qHash( request.attributes().toFields() );
|
||||
}
|
||||
|
||||
@ -44,6 +44,9 @@ class CORE_EXPORT QgsPointCloudRequest
|
||||
//! Ctor
|
||||
QgsPointCloudRequest();
|
||||
|
||||
//! Equality operator
|
||||
bool operator==( const QgsPointCloudRequest &other ) const;
|
||||
|
||||
//! Returns attributes
|
||||
QgsPointCloudAttributeCollection attributes() const;
|
||||
|
||||
@ -66,4 +69,7 @@ class CORE_EXPORT QgsPointCloudRequest
|
||||
QgsRectangle mFilterRect;
|
||||
};
|
||||
|
||||
//! Hash function for QgsPointCloudRequest
|
||||
uint qHash( const QgsPointCloudRequest &request );
|
||||
|
||||
#endif // QGSPOINTCLOUDREQUEST_H
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user