mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-11 00:09:04 -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
|
%End
|
||||||
virtual ~QgsPointCloudBlock();
|
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;
|
const char *data() const;
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns raw pointer to data
|
Returns raw pointer to data
|
||||||
|
|||||||
@ -34,6 +34,12 @@ Ctor
|
|||||||
%End
|
%End
|
||||||
virtual ~QgsPointCloudBlock();
|
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;
|
const char *data() const;
|
||||||
%Docstring
|
%Docstring
|
||||||
Returns raw pointer to data
|
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 )
|
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 );
|
const bool found = fetchNodeHierarchy( n );
|
||||||
if ( !found )
|
if ( !found )
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -164,7 +169,9 @@ std::unique_ptr<QgsPointCloudBlock> QgsCopcPointCloudIndex::nodeData( const Inde
|
|||||||
}
|
}
|
||||||
QgsRectangle filterRect = request.filterRect();
|
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 )
|
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 )
|
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();
|
mHierarchyMutex.lock();
|
||||||
const bool found = mHierarchy.contains( n );
|
const bool found = mHierarchy.contains( n );
|
||||||
mHierarchyMutex.unlock();
|
mHierarchyMutex.unlock();
|
||||||
@ -323,25 +328,25 @@ std::unique_ptr<QgsPointCloudBlock> QgsEptPointCloudIndex::nodeData( const Index
|
|||||||
requestAttributes.extend( attributes(), filterExpression.referencedAttributes() );
|
requestAttributes.extend( attributes(), filterExpression.referencedAttributes() );
|
||||||
QgsRectangle filterRect = request.filterRect();
|
QgsRectangle filterRect = request.filterRect();
|
||||||
|
|
||||||
|
std::unique_ptr<QgsPointCloudBlock> decoded;
|
||||||
if ( mDataType == QLatin1String( "binary" ) )
|
if ( mDataType == QLatin1String( "binary" ) )
|
||||||
{
|
{
|
||||||
const QString filename = QStringLiteral( "%1/ept-data/%2.bin" ).arg( mDirectory, n.toString() );
|
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" ) )
|
else if ( mDataType == QLatin1String( "zstandard" ) )
|
||||||
{
|
{
|
||||||
const QString filename = QStringLiteral( "%1/ept-data/%2.zst" ).arg( mDirectory, n.toString() );
|
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" ) )
|
else if ( mDataType == QLatin1String( "laszip" ) )
|
||||||
{
|
{
|
||||||
const QString filename = QStringLiteral( "%1/ept-data/%2.laz" ).arg( mDirectory, n.toString() );
|
const QString filename = QStringLiteral( "%1/ept-data/%2.laz" ).arg( mDirectory, n.toString() );
|
||||||
return QgsLazDecoder::decompressLaz( filename, requestAttributes, filterExpression, filterRect );
|
decoded = QgsLazDecoder::decompressLaz( filename, requestAttributes, filterExpression, filterRect );
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return nullptr; // unsupported
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storeNodeDataToCache( decoded.get(), n, request );
|
||||||
|
return decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
QgsPointCloudBlockRequest *QgsEptPointCloudIndex::asyncNodeData( const IndexedPointCloudNode &n, const QgsPointCloudRequest &request )
|
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
|
const char *QgsPointCloudBlock::data() const
|
||||||
{
|
{
|
||||||
return mStorage.data();
|
return mStorage.data();
|
||||||
|
|||||||
@ -45,6 +45,12 @@ class CORE_EXPORT QgsPointCloudBlock
|
|||||||
//! Dtor
|
//! Dtor
|
||||||
virtual ~QgsPointCloudBlock() = default;
|
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
|
//! Returns raw pointer to data
|
||||||
const char *data() const;
|
const char *data() const;
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "qgstiledownloadmanager.h"
|
#include "qgstiledownloadmanager.h"
|
||||||
#include "qgspointcloudstatistics.h"
|
#include "qgspointcloudstatistics.h"
|
||||||
|
#include "qgslogger.h"
|
||||||
|
|
||||||
IndexedPointCloudNode::IndexedPointCloudNode():
|
IndexedPointCloudNode::IndexedPointCloudNode():
|
||||||
mD( -1 ),
|
mD( -1 ),
|
||||||
@ -87,6 +88,31 @@ uint qHash( IndexedPointCloudNode id )
|
|||||||
|
|
||||||
///@cond PRIVATE
|
///@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
|
// QgsPointCloudDataBounds
|
||||||
//
|
//
|
||||||
@ -153,6 +179,9 @@ QgsDoubleRange QgsPointCloudDataBounds::zRange( const QgsVector3D &offset, const
|
|||||||
// QgsPointCloudIndex
|
// QgsPointCloudIndex
|
||||||
//
|
//
|
||||||
|
|
||||||
|
QMutex QgsPointCloudIndex::sBlockCacheMutex;
|
||||||
|
QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> QgsPointCloudIndex::sBlockCache( 200'000'000 ); // 200MB of cached points
|
||||||
|
|
||||||
QgsPointCloudIndex::QgsPointCloudIndex() = default;
|
QgsPointCloudIndex::QgsPointCloudIndex() = default;
|
||||||
|
|
||||||
QgsPointCloudIndex::~QgsPointCloudIndex() = default;
|
QgsPointCloudIndex::~QgsPointCloudIndex() = default;
|
||||||
@ -364,3 +393,28 @@ void QgsPointCloudIndex::copyCommonProperties( QgsPointCloudIndex *destination )
|
|||||||
destination->mSpan = mSpan;
|
destination->mSpan = mSpan;
|
||||||
destination->mFilterExpression = mFilterExpression;
|
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 <QVector>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#include <QCache>
|
||||||
|
|
||||||
#include "qgis_core.h"
|
#include "qgis_core.h"
|
||||||
#include "qgsrectangle.h"
|
#include "qgsrectangle.h"
|
||||||
@ -34,6 +35,7 @@
|
|||||||
#include "qgsrange.h"
|
#include "qgsrange.h"
|
||||||
#include "qgspointcloudattribute.h"
|
#include "qgspointcloudattribute.h"
|
||||||
#include "qgspointcloudexpression.h"
|
#include "qgspointcloudexpression.h"
|
||||||
|
#include "qgspointcloudrequest.h"
|
||||||
|
|
||||||
#define SIP_NO_FILE
|
#define SIP_NO_FILE
|
||||||
|
|
||||||
@ -105,6 +107,46 @@ Q_DECLARE_TYPEINFO( IndexedPointCloudNode, Q_PRIMITIVE_TYPE );
|
|||||||
//! Hash function for indexed nodes
|
//! Hash function for indexed nodes
|
||||||
CORE_EXPORT uint qHash( IndexedPointCloudNode id );
|
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
|
* \ingroup core
|
||||||
*
|
*
|
||||||
@ -330,6 +372,24 @@ class CORE_EXPORT QgsPointCloudIndex: public QObject
|
|||||||
*/
|
*/
|
||||||
void copyCommonProperties( QgsPointCloudIndex *destination ) const;
|
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
|
protected: //TODO private
|
||||||
//! Sets native attributes of the data
|
//! Sets native attributes of the data
|
||||||
void setAttributes( const QgsPointCloudAttributeCollection &attributes );
|
void setAttributes( const QgsPointCloudAttributeCollection &attributes );
|
||||||
@ -348,6 +408,8 @@ class CORE_EXPORT QgsPointCloudIndex: public QObject
|
|||||||
|
|
||||||
QString mError;
|
QString mError;
|
||||||
QString mUri;
|
QString mUri;
|
||||||
|
static QMutex sBlockCacheMutex;
|
||||||
|
static QCache<QgsPointCloudCacheKey, QgsPointCloudBlock> sBlockCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGSPOINTCLOUDINDEX_H
|
#endif // QGSPOINTCLOUDINDEX_H
|
||||||
|
|||||||
@ -21,6 +21,12 @@
|
|||||||
|
|
||||||
QgsPointCloudRequest::QgsPointCloudRequest() = default;
|
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
|
QgsPointCloudAttributeCollection QgsPointCloudRequest::attributes() const
|
||||||
{
|
{
|
||||||
return mAttributes;
|
return mAttributes;
|
||||||
@ -30,3 +36,8 @@ void QgsPointCloudRequest::setAttributes( const QgsPointCloudAttributeCollection
|
|||||||
{
|
{
|
||||||
mAttributes = attributes;
|
mAttributes = attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint qHash( const QgsPointCloudRequest &request )
|
||||||
|
{
|
||||||
|
return qHash( request.filterRect() ) ^ qHash( request.attributes().toFields() );
|
||||||
|
}
|
||||||
|
|||||||
@ -44,6 +44,9 @@ class CORE_EXPORT QgsPointCloudRequest
|
|||||||
//! Ctor
|
//! Ctor
|
||||||
QgsPointCloudRequest();
|
QgsPointCloudRequest();
|
||||||
|
|
||||||
|
//! Equality operator
|
||||||
|
bool operator==( const QgsPointCloudRequest &other ) const;
|
||||||
|
|
||||||
//! Returns attributes
|
//! Returns attributes
|
||||||
QgsPointCloudAttributeCollection attributes() const;
|
QgsPointCloudAttributeCollection attributes() const;
|
||||||
|
|
||||||
@ -66,4 +69,7 @@ class CORE_EXPORT QgsPointCloudRequest
|
|||||||
QgsRectangle mFilterRect;
|
QgsRectangle mFilterRect;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Hash function for QgsPointCloudRequest
|
||||||
|
uint qHash( const QgsPointCloudRequest &request );
|
||||||
|
|
||||||
#endif // QGSPOINTCLOUDREQUEST_H
|
#endif // QGSPOINTCLOUDREQUEST_H
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user