mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-07 00:03:52 -05:00
Create QgsMapHitTestTask, a QgsTask which executes a legend hit
test in a background thread in a thread-safe way These legend hit tests can be very expensive to calculate, so background execution is desirable...
This commit is contained in:
parent
11f5a6947a
commit
f261855490
@ -76,6 +76,53 @@ Tests whether a given legend key is visible for a specified layer.
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class QgsMapHitTestTask : QgsTask
|
||||||
|
{
|
||||||
|
%Docstring(signature="appended")
|
||||||
|
Executes a QgsMapHitTest in a background thread.
|
||||||
|
|
||||||
|
.. versionadded:: 3.32
|
||||||
|
%End
|
||||||
|
|
||||||
|
%TypeHeaderCode
|
||||||
|
#include "qgsmaphittest.h"
|
||||||
|
%End
|
||||||
|
public:
|
||||||
|
|
||||||
|
QgsMapHitTestTask( const QgsMapSettings &settings, const QgsGeometry &polygon = QgsGeometry(), const QgsMapHitTest::LayerFilterExpression &layerFilterExpression = QgsMapHitTest::LayerFilterExpression() );
|
||||||
|
%Docstring
|
||||||
|
Constructor for QgsMapHitTestTask, filtering by a visible geometry.
|
||||||
|
|
||||||
|
:param settings: Map settings used to evaluate symbols
|
||||||
|
:param polygon: Polygon geometry to refine the hit test
|
||||||
|
:param layerFilterExpression: Expression string for each layer id to evaluate in order to refine the symbol selection
|
||||||
|
%End
|
||||||
|
|
||||||
|
QgsMapHitTestTask( const QgsMapSettings &settings, const QgsMapHitTest::LayerFilterExpression &layerFilterExpression );
|
||||||
|
%Docstring
|
||||||
|
Constructor for QgsMapHitTestTask, filtering by expressions.
|
||||||
|
|
||||||
|
:param settings: Map settings used to evaluate symbols
|
||||||
|
:param layerFilterExpression: Expression string for each layer id to evaluate in order to refine the symbol selection
|
||||||
|
%End
|
||||||
|
|
||||||
|
QMap<QString, QSet<QString>> results() const;
|
||||||
|
%Docstring
|
||||||
|
Returns the hit test results, which are a map of layer ID to
|
||||||
|
visible symbol legend keys.
|
||||||
|
%End
|
||||||
|
|
||||||
|
virtual void cancel();
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual bool run();
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
* This file has been generated automatically from *
|
* This file has been generated automatically from *
|
||||||
* *
|
* *
|
||||||
|
|||||||
@ -267,3 +267,131 @@ void QgsMapHitTest::runHitTestFeatureSource( QgsAbstractFeatureSource *source,
|
|||||||
r->stopRender( context );
|
r->stopRender( context );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// QgsMapHitTestTask
|
||||||
|
//
|
||||||
|
|
||||||
|
QgsMapHitTestTask::QgsMapHitTestTask( const QgsMapSettings &settings, const QgsGeometry &polygon, const QgsMapHitTest::LayerFilterExpression &layerFilterExpression )
|
||||||
|
: QgsTask( tr( "Updating Legend" ), QgsTask::Flag::CanCancel | QgsTask::Flag::CancelWithoutPrompt | QgsTask::Flag::Silent )
|
||||||
|
, mSettings( settings )
|
||||||
|
, mLayerFilterExpression( layerFilterExpression )
|
||||||
|
, mPolygon( polygon )
|
||||||
|
, mOnlyExpressions( false )
|
||||||
|
{
|
||||||
|
prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
QgsMapHitTestTask::QgsMapHitTestTask( const QgsMapSettings &settings, const QgsMapHitTest::LayerFilterExpression &layerFilterExpression )
|
||||||
|
: QgsTask( tr( "Updating Legend" ), QgsTask::Flag::CanCancel | QgsTask::Flag::CancelWithoutPrompt | QgsTask::Flag::Silent )
|
||||||
|
, mSettings( settings )
|
||||||
|
, mLayerFilterExpression( layerFilterExpression )
|
||||||
|
, mOnlyExpressions( true )
|
||||||
|
{
|
||||||
|
prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
QMap<QString, QSet<QString> > QgsMapHitTestTask::results() const
|
||||||
|
{
|
||||||
|
return mResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMapHitTestTask::prepare()
|
||||||
|
{
|
||||||
|
const QList< QgsMapLayer * > layers = mSettings.layers( true );
|
||||||
|
for ( QgsMapLayer *layer : layers )
|
||||||
|
{
|
||||||
|
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
|
||||||
|
if ( !vl || !vl->renderer() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QgsMapLayerStyleOverride styleOverride( vl );
|
||||||
|
if ( mSettings.layerStyleOverrides().contains( vl->id() ) )
|
||||||
|
styleOverride.setOverrideStyle( mSettings.layerStyleOverrides().value( vl->id() ) );
|
||||||
|
|
||||||
|
if ( !mOnlyExpressions )
|
||||||
|
{
|
||||||
|
if ( !vl->isInScaleRange( mSettings.scale() ) )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PreparedLayerData layerData;
|
||||||
|
layerData.source = std::make_unique< QgsVectorLayerFeatureSource >( vl );
|
||||||
|
layerData.layerId = vl->id();
|
||||||
|
layerData.crs = vl->crs();
|
||||||
|
layerData.fields = vl->fields();
|
||||||
|
layerData.renderer.reset( vl->renderer()->clone() );
|
||||||
|
layerData.transform = mSettings.layerTransform( vl );
|
||||||
|
layerData.extent = mSettings.outputExtentToLayerExtent( vl, mSettings.visibleExtent() );
|
||||||
|
layerData.layerScope.reset( QgsExpressionContextUtils::layerScope( vl ) );
|
||||||
|
|
||||||
|
mPreparedData.emplace_back( std::move( layerData ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QgsMapHitTestTask::cancel()
|
||||||
|
{
|
||||||
|
if ( mFeedback )
|
||||||
|
mFeedback->cancel();
|
||||||
|
|
||||||
|
QgsTask::cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QgsMapHitTestTask::run()
|
||||||
|
{
|
||||||
|
mFeedback = std::make_unique< QgsFeedback >();
|
||||||
|
connect( mFeedback.get(), &QgsFeedback::progressChanged, this, &QgsTask::progressChanged );
|
||||||
|
|
||||||
|
std::unique_ptr< QgsMapHitTest > hitTest;
|
||||||
|
if ( !mOnlyExpressions )
|
||||||
|
hitTest = std::make_unique< QgsMapHitTest >( mSettings, mPolygon, mLayerFilterExpression );
|
||||||
|
else
|
||||||
|
hitTest = std::make_unique< QgsMapHitTest >( mSettings, mLayerFilterExpression );
|
||||||
|
|
||||||
|
// TODO: do we need this temp image?
|
||||||
|
QImage tmpImage( mSettings.outputSize(), mSettings.outputImageFormat() );
|
||||||
|
tmpImage.setDotsPerMeterX( mSettings.outputDpi() * 25.4 );
|
||||||
|
tmpImage.setDotsPerMeterY( mSettings.outputDpi() * 25.4 );
|
||||||
|
QPainter painter( &tmpImage );
|
||||||
|
|
||||||
|
QgsRenderContext context = QgsRenderContext::fromMapSettings( mSettings );
|
||||||
|
context.setPainter( &painter ); // we are not going to draw anything, but we still need a working painter
|
||||||
|
|
||||||
|
int layerIdx = 0;
|
||||||
|
const int totalCount = mPreparedData.size();
|
||||||
|
for ( auto &layerData : mPreparedData )
|
||||||
|
{
|
||||||
|
mFeedback->setProgress( static_cast< double >( layerIdx ) / totalCount * 100.0 );
|
||||||
|
if ( mFeedback->isCanceled() )
|
||||||
|
break;
|
||||||
|
|
||||||
|
QgsMapHitTest::SymbolSet &usedSymbols = hitTest->mHitTest[layerData.layerId];
|
||||||
|
QgsMapHitTest::SymbolSet &usedSymbolsRuleKey = hitTest->mHitTestRuleKey[layerData.layerId];
|
||||||
|
|
||||||
|
context.setCoordinateTransform( layerData.transform );
|
||||||
|
context.setExtent( layerData.extent );
|
||||||
|
|
||||||
|
QgsExpressionContextScope *layerScope = layerData.layerScope.release();
|
||||||
|
QgsExpressionContextScopePopper scopePopper( context.expressionContext(), layerScope );
|
||||||
|
|
||||||
|
hitTest->runHitTestFeatureSource( layerData.source.get(),
|
||||||
|
layerData.layerId,
|
||||||
|
layerData.crs,
|
||||||
|
layerData.fields,
|
||||||
|
layerData.renderer.get(),
|
||||||
|
usedSymbols,
|
||||||
|
usedSymbolsRuleKey,
|
||||||
|
context,
|
||||||
|
mFeedback.get() );
|
||||||
|
layerIdx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
mResults = hitTest->mHitTestRuleKey;
|
||||||
|
|
||||||
|
mFeedback.reset();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,8 @@
|
|||||||
#include "qgis_sip.h"
|
#include "qgis_sip.h"
|
||||||
#include "qgsmapsettings.h"
|
#include "qgsmapsettings.h"
|
||||||
#include "qgsgeometry.h"
|
#include "qgsgeometry.h"
|
||||||
|
#include "qgstaskmanager.h"
|
||||||
|
#include "qgscoordinatetransform.h"
|
||||||
|
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
|
||||||
@ -125,6 +127,77 @@ class CORE_EXPORT QgsMapHitTest
|
|||||||
|
|
||||||
//! Whether to use only expressions during the filtering
|
//! Whether to use only expressions during the filtering
|
||||||
bool mOnlyExpressions;
|
bool mOnlyExpressions;
|
||||||
|
|
||||||
|
friend class QgsMapHitTestTask;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \ingroup core
|
||||||
|
* \brief Executes a QgsMapHitTest in a background thread.
|
||||||
|
*
|
||||||
|
* \since QGIS 3.32
|
||||||
|
*/
|
||||||
|
class CORE_EXPORT QgsMapHitTestTask : public QgsTask
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for QgsMapHitTestTask, filtering by a visible geometry.
|
||||||
|
*
|
||||||
|
* \param settings Map settings used to evaluate symbols
|
||||||
|
* \param polygon Polygon geometry to refine the hit test
|
||||||
|
* \param layerFilterExpression Expression string for each layer id to evaluate in order to refine the symbol selection
|
||||||
|
*/
|
||||||
|
QgsMapHitTestTask( const QgsMapSettings &settings, const QgsGeometry &polygon = QgsGeometry(), const QgsMapHitTest::LayerFilterExpression &layerFilterExpression = QgsMapHitTest::LayerFilterExpression() );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for QgsMapHitTestTask, filtering by expressions.
|
||||||
|
*
|
||||||
|
* \param settings Map settings used to evaluate symbols
|
||||||
|
* \param layerFilterExpression Expression string for each layer id to evaluate in order to refine the symbol selection
|
||||||
|
*/
|
||||||
|
QgsMapHitTestTask( const QgsMapSettings &settings, const QgsMapHitTest::LayerFilterExpression &layerFilterExpression );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hit test results, which are a map of layer ID to
|
||||||
|
* visible symbol legend keys.
|
||||||
|
*/
|
||||||
|
QMap<QString, QSet<QString>> results() const;
|
||||||
|
|
||||||
|
void cancel() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
bool run() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void prepare();
|
||||||
|
|
||||||
|
struct PreparedLayerData
|
||||||
|
{
|
||||||
|
std::unique_ptr< QgsAbstractFeatureSource > source;
|
||||||
|
QString layerId;
|
||||||
|
QgsCoordinateReferenceSystem crs;
|
||||||
|
QgsFields fields;
|
||||||
|
std::unique_ptr< QgsFeatureRenderer > renderer;
|
||||||
|
QgsRectangle extent;
|
||||||
|
QgsCoordinateTransform transform;
|
||||||
|
std::unique_ptr< QgsExpressionContextScope > layerScope;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector< PreparedLayerData > mPreparedData;
|
||||||
|
|
||||||
|
QgsMapSettings mSettings;
|
||||||
|
QgsMapHitTest::LayerFilterExpression mLayerFilterExpression;
|
||||||
|
QgsGeometry mPolygon;
|
||||||
|
bool mOnlyExpressions = false;
|
||||||
|
QMap<QString, QSet<QString>> mResults;
|
||||||
|
|
||||||
|
std::unique_ptr< QgsFeedback > mFeedback;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // QGSMAPHITTEST_H
|
#endif // QGSMAPHITTEST_H
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user