Skip to content

Commit

Permalink
[feature][api] Allow a custom label sink to be used with map renderer…
Browse files Browse the repository at this point in the history
… jobs
  • Loading branch information
nirvn committed Dec 28, 2021
1 parent 5831c43 commit 4dbcd38
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 10 deletions.
Expand Up @@ -12,7 +12,6 @@




class QgsMapRendererJob : QObject /Abstract/
{
%Docstring(signature="appended")
Expand Down Expand Up @@ -158,6 +157,8 @@ Assign a cache to be used for reading and storing rendered images of individual
Does not take ownership of the object.
%End



int renderingTime() const;
%Docstring
Returns the total time it took to finish the job (in milliseconds).
Expand Down
2 changes: 2 additions & 0 deletions python/core/auto_generated/qgsrendercontext.sip.in
Expand Up @@ -352,6 +352,7 @@ exactly 2mm thick when a map is rendered at 1:1000, or 1mm thick when rendered a
%End



QColor selectionColor() const;
%Docstring
Returns the color to use when rendering selected features.
Expand Down Expand Up @@ -539,6 +540,7 @@ of any faster raster shortcuts.
%End



void setSelectionColor( const QColor &color );
%Docstring
Sets the ``color`` to use when rendering selected features.
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmvectorize.cpp
Expand Up @@ -113,7 +113,7 @@ QVariantMap QgsVectorizeAlgorithmBase::processAlgorithm( const QVariantMap &para
for ( int column = 0; column < iterCols; column++ )
{
const double value = rasterBlock->valueAndNoData( row, column, isNoData );
if ( !isNoData )
if ( !isNoData && !qgsDoubleNear( value, 0.0 ) )
{
const QgsGeometry pixelRectGeometry = createGeometryForPixel( currentX, currentY, mRasterUnitsPerPixelX, mRasterUnitsPerPixelY );

Expand Down
1 change: 1 addition & 0 deletions src/core/maprenderer/qgsmaprendererjob.cpp
Expand Up @@ -495,6 +495,7 @@ std::vector<LayerRenderJob> QgsMapRendererJob::prepareJobs( QPainter *painter, Q
job.context()->expressionContext().appendScope( QgsExpressionContextUtils::layerScope( ml ) );
job.context()->setPainter( painter );
job.context()->setLabelingEngine( labelingEngine2 );
job.context()->setLabelSink( labelSink() );
job.context()->setCoordinateTransform( ct );
job.context()->setExtent( r1 );
if ( !haveExtentInLayerCrs )
Expand Down
20 changes: 19 additions & 1 deletion src/core/maprenderer/qgsmaprendererjob.h
Expand Up @@ -26,7 +26,7 @@
#include <QElapsedTimer>

#include "qgsrendercontext.h"

#include "qgslabelsink.h"
#include "qgsmapsettings.h"
#include "qgsmaskidprovider.h"
#include "qgssettingsentry.h"
Expand Down Expand Up @@ -352,6 +352,22 @@ class CORE_EXPORT QgsMapRendererJob : public QObject SIP_ABSTRACT
*/
void setCache( QgsMapRendererCache *cache );

/**
* Returns the label sink associated to this rendering job.
* \note not available in Python bindings
* \since QGIS 3.24
*/
QgsLabelSink *labelSink() const { return mLabelSink; } SIP_SKIP

/**
* Assigns the label sink which will take over responsability for handling labels
* during the rendering job.
* \note the ownership is not transferred
* \note not available in Python bindings
* \since QGIS 3.24
*/
void setLabelSink( QgsLabelSink *sink ) { mLabelSink = sink; } SIP_SKIP

/**
* Returns the total time it took to finish the job (in milliseconds).
* \see perLayerRenderingTime()
Expand Down Expand Up @@ -559,6 +575,8 @@ class CORE_EXPORT QgsMapRendererJob : public QObject SIP_ABSTRACT
*/
virtual void startPrivate() = 0;

QgsLabelSink *mLabelSink = nullptr;

};


Expand Down
1 change: 1 addition & 0 deletions src/core/maprenderer/qgsmaprenderersequentialjob.cpp
Expand Up @@ -66,6 +66,7 @@ void QgsMapRendererSequentialJob::startPrivate()
mPainter = new QPainter( &mImage );

mInternalJob = new QgsMapRendererCustomPainterJob( mSettings, mPainter );
mInternalJob->setLabelSink( labelSink() );
mInternalJob->setCache( mCache );

connect( mInternalJob, &QgsMapRendererJob::finished, this, &QgsMapRendererSequentialJob::internalFinished );
Expand Down
21 changes: 19 additions & 2 deletions src/core/qgsrendercontext.h
Expand Up @@ -27,6 +27,7 @@
#include "qgscoordinatetransform.h"
#include "qgsexpressioncontext.h"
#include "qgsfeaturefilterprovider.h"
#include "qgslabelsink.h"
#include "qgsmaptopixel.h"
#include "qgsmapunitscale.h"
#include "qgsrectangle.h"
Expand Down Expand Up @@ -365,6 +366,12 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
*/
QgsLabelingEngine *labelingEngine() const { return mLabelingEngine; } SIP_SKIP

/**
* Returns the associated label sink, or NULLPTR if not set.
* \since QGIS 3.24
*/
QgsLabelSink *labelSink() const { return mLabelSink; } SIP_SKIP

/**
* Returns the color to use when rendering selected features.
*
Expand Down Expand Up @@ -531,11 +538,18 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject
void setForceVectorOutput( bool force );

/**
* Assign new labeling engine
* Assigns the labeling engine
* \note not available in Python bindings
*/
void setLabelingEngine( QgsLabelingEngine *engine ) { mLabelingEngine = engine; } SIP_SKIP

/**
* Assigns the label sink which will take over responsability for handling labels.
* \note the ownership is not transferred
* \since QGIS 3.24
*/
void setLabelSink( QgsLabelSink *sink ) { mLabelSink = sink; } SIP_SKIP

/**
* Sets the \a color to use when rendering selected features.
*
Expand Down Expand Up @@ -1024,9 +1038,12 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject

double mSymbologyReferenceScale = -1;

//! Newer labeling engine implementation (can be NULLPTR)
//! Labeling engine implementation (can be NULLPTR)
QgsLabelingEngine *mLabelingEngine = nullptr;

//! Label sink (can be NULLPTR)
QgsLabelSink *mLabelSink = nullptr;

//! Color used for features that are marked as selected
QColor mSelectionColor;

Expand Down
28 changes: 25 additions & 3 deletions src/core/vector/qgsvectorlayerrenderer.cpp
Expand Up @@ -34,6 +34,7 @@
#include "qgspainteffect.h"
#include "qgsfeaturefilterprovider.h"
#include "qgsexception.h"
#include "qgslabelsink.h"
#include "qgslogger.h"
#include "qgssettingsregistrycore.h"
#include "qgsexpressioncontextutils.h"
Expand Down Expand Up @@ -713,14 +714,35 @@ void QgsVectorLayerRenderer::prepareLabeling( QgsVectorLayer *layer, QSet<QStrin
{
if ( layer->labelsEnabled() )
{
mLabelProvider = layer->labeling()->provider( layer );
if ( mLabelProvider )
if ( context.labelSink() )
{
if ( const QgsRuleBasedLabeling *rbl = dynamic_cast<const QgsRuleBasedLabeling *>( layer->labeling() ) )
{
mLabelProvider = new QgsRuleBasedLabelSinkProvider( *rbl, layer, context.labelSink() );
}
else
{
QgsPalLayerSettings settings = layer->labeling()->settings();
mLabelProvider = new QgsLabelSinkProvider( layer, QString(), context.labelSink(), &settings );
}
engine2->addProvider( mLabelProvider );
if ( !mLabelProvider->prepare( context, attributeNames ) )
{
engine2->removeProvider( mLabelProvider );
mLabelProvider = nullptr; // deleted by engine
mLabelProvider = nullptr;
}
}
else
{
mLabelProvider = layer->labeling()->provider( layer );
if ( mLabelProvider )
{
engine2->addProvider( mLabelProvider );
if ( !mLabelProvider->prepare( context, attributeNames ) )
{
engine2->removeProvider( mLabelProvider );
mLabelProvider = nullptr; // deleted by engine
}
}
}
}
Expand Down
63 changes: 61 additions & 2 deletions tests/src/core/testqgsmaprendererjob.cpp
@@ -1,5 +1,5 @@
/***************************************************************************
testqgsvectorfilewriter.cpp
testqgsmaprendererjob.cpp
--------------------------------------
Date : Sun Sep 16 12:22:54 AKDT 2007
Copyright : (C) 2007 by Tim Sutton
Expand All @@ -12,6 +12,7 @@
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgstest.h"
#include <QObject>
#include <QString>
Expand Down Expand Up @@ -45,6 +46,7 @@
#include "qgssinglesymbolrenderer.h"
#include "qgsrasterlayertemporalproperties.h"
#include "qgslinesymbol.h"
#include "qgslabelsink.h"

//qgs unit test utility class
#include "qgsmultirenderchecker.h"
Expand Down Expand Up @@ -91,6 +93,8 @@ class TestQgsMapRendererJob : public QObject

void temporalRender();

void labelSink();

private:
bool imageCheck( const QString &type, const QImage &image, int mismatchCount = 0 );

Expand Down Expand Up @@ -487,7 +491,6 @@ void TestQgsMapRendererJob::testRenderedFeatureHandlers()
QCOMPARE( attributes.at( 2 ), QStringLiteral( "Highway,1" ) );
QCOMPARE( attributes.at( 3 ), QStringLiteral( "Highway,1" ) );
QCOMPARE( attributes.at( 4 ), QStringLiteral( "Jet,95,3,1,1,2" ) );

}

void TestQgsMapRendererJob::stagedRenderer()
Expand Down Expand Up @@ -947,6 +950,62 @@ void TestQgsMapRendererJob::temporalRender()

}

class TestLabelSink : public QgsLabelSink
{
public:
TestLabelSink() {};

void drawLabel( const QString &layerId, QgsRenderContext &context, pal::LabelPosition *label, const QgsPalLayerSettings &settings ) override
{
Q_UNUSED( layerId )
Q_UNUSED( context )
Q_UNUSED( label )
Q_UNUSED( settings )
drawnCount++;
};

int drawnCount = 0;
};

void TestQgsMapRendererJob::labelSink()
{
std::unique_ptr< QgsVectorLayer > pointsLayer = std::make_unique< QgsVectorLayer >( TEST_DATA_DIR + QStringLiteral( "/points.shp" ),
QStringLiteral( "points" ), QStringLiteral( "ogr" ) );
QVERIFY( pointsLayer->isValid() );

QgsPalLayerSettings settings;
settings.fieldName = QStringLiteral( "Class" );
QgsTextFormat format;
format.setFont( QgsFontUtils::getStandardTestFont( QStringLiteral( "Bold" ) ).family() );
format.setSize( 12 );
format.setNamedStyle( QStringLiteral( "Bold" ) );
format.setColor( QColor( 200, 0, 200 ) );
settings.setFormat( format );
settings.zIndex = 1;

pointsLayer->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) );
pointsLayer->setLabelsEnabled( true );

QgsMapSettings mapSettings;
mapSettings.setDestinationCrs( pointsLayer->crs() );
mapSettings.setExtent( pointsLayer->extent() );
mapSettings.setOutputSize( QSize( 512, 512 ) );
mapSettings.setFlag( Qgis::MapSettingsFlag::DrawLabeling, true );
mapSettings.setOutputDpi( 96 );
mapSettings.setLayers( QList< QgsMapLayer * >() << pointsLayer.get() );


QgsMapRendererSequentialJob renderJob( mapSettings );

std::unique_ptr<TestLabelSink> labelSink = std::make_unique<TestLabelSink>();
renderJob.setLabelSink( labelSink.get() );
renderJob.start();
renderJob.waitForFinished();
QImage img = renderJob.renderedImage();
QVERIFY( imageCheck( QStringLiteral( "label_sink" ), img ) );
QCOMPARE( labelSink->drawnCount, 17 );
}

bool TestQgsMapRendererJob::imageCheck( const QString &testName, const QImage &image, int mismatchCount )
{
mReport += "<h2>" + testName + "</h2>\n";
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4dbcd38

Please sign in to comment.