[api] Add labeling flag to collect unplaced labels without rendering them

This commit is contained in:
Nyall Dawson 2021-06-08 10:38:15 +10:00
parent 8e2fb49718
commit c95fed55f1
5 changed files with 60 additions and 16 deletions

View File

@ -29,6 +29,7 @@ Stores global configuration for labeling engine
DrawLabelRectOnly,
DrawCandidates,
DrawUnplacedLabels,
CollectUnplacedLabels,
};
typedef QFlags<QgsLabelingEngineSettings::Flag> Flags;

View File

@ -392,7 +392,9 @@ void QgsLabelingEngine::solve( QgsRenderContext &context )
}
// find the solution
mLabels = mPal->solveProblem( mProblem.get(), settings.testFlag( QgsLabelingEngineSettings::UseAllLabels ), settings.testFlag( QgsLabelingEngineSettings::DrawUnplacedLabels ) ? &mUnlabeled : nullptr );
mLabels = mPal->solveProblem( mProblem.get(),
settings.testFlag( QgsLabelingEngineSettings::UseAllLabels ),
settings.testFlag( QgsLabelingEngineSettings::DrawUnplacedLabels ) || settings.testFlag( QgsLabelingEngineSettings::CollectUnplacedLabels ) ? &mUnlabeled : nullptr );
// sort labels
std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mMapSettings ) );
@ -476,7 +478,7 @@ void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &la
}
// draw unplaced labels. These are always rendered on top
if ( settings.testFlag( QgsLabelingEngineSettings::DrawUnplacedLabels ) )
if ( settings.testFlag( QgsLabelingEngineSettings::DrawUnplacedLabels ) || settings.testFlag( QgsLabelingEngineSettings::CollectUnplacedLabels ) )
{
for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
{

View File

@ -40,6 +40,7 @@ class CORE_EXPORT QgsLabelingEngineSettings
DrawLabelRectOnly = 1 << 4, //!< Whether to only draw the label rect and not the actual label text (used for unit tests)
DrawCandidates = 1 << 5, //!< Whether to draw rectangles of generated candidates (good for debugging)
DrawUnplacedLabels = 1 << 6, //!< Whether to render unplaced labels as an indicator/warning for users
CollectUnplacedLabels = 1 << 7, //!< Whether unplaced labels should be collected in the labeling results (regardless of whether they are being rendered). Since QGIS 3.20
};
Q_DECLARE_FLAGS( Flags, Flag )

View File

@ -466,20 +466,23 @@ void QgsVectorLayerLabelProvider::drawLabel( QgsRenderContext &context, pal::Lab
void QgsVectorLayerLabelProvider::drawUnplacedLabel( QgsRenderContext &context, LabelPosition *label ) const
{
if ( !mSettings.drawLabels || mSettings.unplacedVisibility() == Qgis::UnplacedLabelVisibility::NeverShow )
return;
QgsTextLabelFeature *lf = dynamic_cast<QgsTextLabelFeature *>( label->getFeaturePart()->feature() );
QgsPalLayerSettings tmpLyr( mSettings );
QgsTextFormat format = tmpLyr.format();
format.setColor( mEngine->engineSettings().unplacedLabelColor() );
tmpLyr.setFormat( format );
drawLabelPrivate( label, context, tmpLyr, QgsTextRenderer::Text );
QgsTextFormat format = mSettings.format();
if ( mSettings.drawLabels
&& mSettings.unplacedVisibility() != Qgis::UnplacedLabelVisibility::NeverShow
&& mEngine->engineSettings().flags() & QgsLabelingEngineSettings::DrawUnplacedLabels )
{
QgsPalLayerSettings tmpLyr( mSettings );
format = tmpLyr.format();
format.setColor( mEngine->engineSettings().unplacedLabelColor() );
tmpLyr.setFormat( format );
drawLabelPrivate( label, context, tmpLyr, QgsTextRenderer::Text );
}
// add to the results
QString labeltext = label->getFeaturePart()->feature()->labelText();
mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, labeltext, tmpLyr.format().font(), false, lf->hasFixedPosition(), mProviderId, true );
mEngine->results()->mLabelSearchTree->insertLabel( label, label->getFeaturePart()->featureId(), mLayerId, labeltext, format.font(), false, lf->hasFixedPosition(), mProviderId, true );
}
void QgsVectorLayerLabelProvider::drawLabelPrivate( pal::LabelPosition *label, QgsRenderContext &context, QgsPalLayerSettings &tmpLyr, QgsTextRenderer::TextPart drawType, double dpiRatio ) const

View File

@ -1914,7 +1914,7 @@ void TestQgsLabelingEngine::labelingResults()
settings.fieldName = QStringLiteral( "\"id\"" );
settings.isExpression = true;
settings.placement = QgsPalLayerSettings::OverPoint;
settings.priority = 10;
std::unique_ptr< QgsVectorLayer> vl2( new QgsVectorLayer( QStringLiteral( "Point?crs=epsg:4326&field=id:integer" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) ) );
vl2->setRenderer( new QgsNullSymbolRenderer() );
@ -1931,6 +1931,8 @@ void TestQgsLabelingEngine::labelingResults()
QVERIFY( vl2->dataProvider()->addFeature( f ) );
vl2->updateExtents();
std::unique_ptr< QgsVectorLayer> vl3( vl2->clone() );
vl2->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) ); // TODO: this should not be necessary!
vl2->setLabelsEnabled( true );
@ -1944,7 +1946,7 @@ void TestQgsLabelingEngine::labelingResults()
mapSettings.setOutputSize( size );
mapSettings.setExtent( QgsRectangle( -4137976.6, 6557092.6, 1585557.4, 9656515.0 ) );
// mapSettings.setRotation( 60 );
mapSettings.setLayers( QList<QgsMapLayer *>() << vl2.get() );
mapSettings.setLayers( QList<QgsMapLayer *>() << vl2.get() << vl3.get() );
mapSettings.setOutputDpi( 96 );
QgsLabelingEngineSettings engineSettings = mapSettings.labelingEngineSettings();
@ -1965,11 +1967,11 @@ void TestQgsLabelingEngine::labelingResults()
QCOMPARE( labels.count(), 3 );
std::sort( labels.begin(), labels.end(), []( const QgsLabelPosition & a, const QgsLabelPosition & b )
{
return a.labelText.compare( b.labelText );
return a.labelText.compare( b.labelText ) < 0;
} );
QCOMPARE( labels.at( 0 ).labelText, QStringLiteral( "1" ) );
QCOMPARE( labels.at( 1 ).labelText, QStringLiteral( "8888" ) );
QCOMPARE( labels.at( 2 ).labelText, QStringLiteral( "33333" ) );
QCOMPARE( labels.at( 1 ).labelText, QStringLiteral( "33333" ) );
QCOMPARE( labels.at( 2 ).labelText, QStringLiteral( "8888" ) );
labels = results->labelsAtPosition( QgsPointXY( -654732, 7003282 ) );
QCOMPARE( labels.count(), 1 );
@ -2015,6 +2017,41 @@ void TestQgsLabelingEngine::labelingResults()
labels = results->labelsAtPosition( QgsPointXY( -2463392, 6708478 ) );
QCOMPARE( labels.count(), 0 );
// with unplaced labels -- all vl3 labels will be unplaced, because they are conflicting with those in vl2
settings.priority = 1;
vl3->setLabeling( new QgsVectorLayerSimpleLabeling( settings ) );
vl3->setLabelsEnabled( true );
engineSettings.setFlag( QgsLabelingEngineSettings::CollectUnplacedLabels, true );
mapSettings.setLabelingEngineSettings( engineSettings );
QgsMapRendererSequentialJob jobB( mapSettings );
jobB.start();
jobB.waitForFinished();
results.reset( jobB.takeLabelingResults() );
QVERIFY( results );
labels = results->allLabels();
QCOMPARE( labels.count(), 6 );
std::sort( labels.begin(), labels.end(), []( const QgsLabelPosition & a, const QgsLabelPosition & b )
{
return a.isUnplaced == b.isUnplaced ? a.labelText.compare( b.labelText ) < 0 : a.isUnplaced < b.isUnplaced;
} );
QCOMPARE( labels.at( 0 ).labelText, QStringLiteral( "1" ) );
QVERIFY( !labels.at( 0 ).isUnplaced );
QCOMPARE( labels.at( 1 ).labelText, QStringLiteral( "33333" ) );
QVERIFY( !labels.at( 1 ).isUnplaced );
QCOMPARE( labels.at( 2 ).labelText, QStringLiteral( "8888" ) );
QVERIFY( !labels.at( 2 ).isUnplaced );
QCOMPARE( labels.at( 3 ).labelText, QStringLiteral( "1" ) );
QVERIFY( labels.at( 3 ).isUnplaced );
QCOMPARE( labels.at( 4 ).labelText, QStringLiteral( "33333" ) );
QVERIFY( labels.at( 4 ).isUnplaced );
QCOMPARE( labels.at( 5 ).labelText, QStringLiteral( "8888" ) );
QVERIFY( labels.at( 5 ).isUnplaced );
mapSettings.setLayers( {vl2.get() } );
// with rotation
mapSettings.setRotation( 60 );
QgsMapRendererSequentialJob job2( mapSettings );