QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgslabelingengine.cpp
Go to the documentation of this file.
1
2/***************************************************************************
3 qgslabelingengine.cpp
4 --------------------------------------
5 Date : September 2015
6 Copyright : (C) 2015 by Martin Dobias
7 Email : wonder dot sk at gmail dot com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17#include "qgslabelingengine.h"
18
19#include "qgslogger.h"
20
21#include "feature.h"
22#include "labelposition.h"
23#include "layer.h"
24#include "pal.h"
25#include "problem.h"
26#include "qgsrendercontext.h"
27#include "qgsmaplayer.h"
28#include "qgssymbol.h"
31#include "qgslabelingresults.h"
32#include "qgsfillsymbol.h"
33
34// helper function for checking for job cancellation within PAL
35static bool _palIsCanceled( void *ctx )
36{
37 return ( reinterpret_cast< QgsRenderContext * >( ctx ) )->renderingStopped();
38}
39
41
47class QgsLabelSorter
48{
49 public:
50
51 explicit QgsLabelSorter( const QStringList &layerRenderingOrderIds )
52 : mLayerRenderingOrderIds( layerRenderingOrderIds )
53 {}
54
55 bool operator()( pal::LabelPosition *lp1, pal::LabelPosition *lp2 ) const
56 {
57 QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
58 QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
59
60 if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
61 return lf1->zIndex() < lf2->zIndex();
62
63 //equal z-index, so fallback to respecting layer render order
64 int layer1Pos = mLayerRenderingOrderIds.indexOf( lf1->provider()->layerId() );
65 int layer2Pos = mLayerRenderingOrderIds.indexOf( lf2->provider()->layerId() );
66 if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
67 return layer1Pos > layer2Pos; //higher positions are rendered first
68
69 //same layer, so render larger labels first
70 return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
71 }
72
73 private:
74
75 const QStringList mLayerRenderingOrderIds;
76};
77
79
80//
81// QgsLabelingEngine
82//
83
85 : mResults( new QgsLabelingResults )
86{}
87
89{
90 qDeleteAll( mProviders );
91 qDeleteAll( mSubProviders );
92}
93
95{
97 mLayerRenderingOrderIds = mMapSettings.layerIds();
98 if ( mResults )
99 mResults->setMapSettings( mapSettings );
100}
101
102QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
103{
104 QList< QgsMapLayer * > layers;
105
106 // try to return layers sorted in the desired z order for rendering
107 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
108 std::sort( providersByZ.begin(), providersByZ.end(),
109 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
110 {
111 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
112 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
113
114 if ( providerA && providerB )
115 {
116 return providerA->settings().zIndex < providerB->settings().zIndex ;
117 }
118 return false;
119 } );
120
121 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
122 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
123 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
124 {
125 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
126 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
127
128 if ( providerA && providerB )
129 {
130 return providerA->settings().zIndex < providerB->settings().zIndex ;
131 }
132 return false;
133 } );
134
135 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
136 {
137 if ( provider->layer() && !layers.contains( provider->layer() ) )
138 layers << provider->layer();
139 }
140 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
141 {
142 if ( provider->layer() && !layers.contains( provider->layer() ) )
143 layers << provider->layer();
144 }
145 return layers;
146}
147
149{
150 QStringList layers;
151
152 // try to return layers sorted in the desired z order for rendering
153 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
154 std::sort( providersByZ.begin(), providersByZ.end(),
155 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
156 {
157 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
158 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
159
160 if ( providerA && providerB )
161 {
162 return providerA->settings().zIndex < providerB->settings().zIndex ;
163 }
164 return false;
165 } );
166
167 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
168 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
169 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
170 {
171 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
172 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
173
174 if ( providerA && providerB )
175 {
176 return providerA->settings().zIndex < providerB->settings().zIndex ;
177 }
178 return false;
179 } );
180
181 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
182 {
183 if ( !layers.contains( provider->layerId() ) )
184 layers << provider->layerId();
185 }
186 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
187 {
188 if ( !layers.contains( provider->layerId() ) )
189 layers << provider->layerId();
190 }
191 return layers;
192}
193
195{
196 provider->setEngine( this );
197 mProviders << provider;
198}
199
201{
202 int idx = mProviders.indexOf( provider );
203 if ( idx >= 0 )
204 {
205 delete mProviders.takeAt( idx );
206 }
207}
208
210{
211 QgsAbstractLabelProvider::Flags flags = provider->flags();
212
213 // create the pal layer
214 pal::Layer *l = p.addLayer( provider,
215 provider->name(),
216 provider->placement(),
217 provider->priority(),
218 true,
219 flags.testFlag( QgsAbstractLabelProvider::DrawLabels ) );
220
221 // set whether adjacent lines should be merged
223
224 // set obstacle type
225 l->setObstacleType( provider->obstacleType() );
226
227 // set whether location of centroid must be inside of polygons
229
230 // set how to show upside-down labels
231 l->setUpsidedownLabels( provider->upsidedownLabels() );
232
233 const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
234
235 for ( QgsLabelFeature *feature : features )
236 {
237 try
238 {
239 l->registerFeature( feature );
240 }
241 catch ( std::exception &e )
242 {
243 Q_UNUSED( e )
244 QgsDebugMsgLevel( QStringLiteral( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
245 continue;
246 }
247 }
248
249 // any sub-providers?
250 const auto subproviders = provider->subProviders();
251 for ( QgsAbstractLabelProvider *subProvider : subproviders )
252 {
253 mSubProviders << subProvider;
254 processProvider( subProvider, context, p );
255 }
256}
257
259{
260 QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
261
262 if ( feedback )
263 feedback->emit labelRegistrationAboutToBegin();
264
266
267 mPal = std::make_unique< pal::Pal >();
268
269 mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ) );
270 mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ), 2 ) );
271
272 mPal->setShowPartialLabels( settings.testFlag( Qgis::LabelingFlag::UsePartialCandidates ) );
273 mPal->setPlacementVersion( settings.placementVersion() );
274
275 // for each provider: get labels and register them in PAL
276 const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
277 int index = 0;
278 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
279 {
280 if ( feedback )
281 {
282 feedback->emit providerRegistrationAboutToBegin( provider );
283 feedback->setProgress( index * step );
284 }
285 index++;
286 std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
287 if ( provider->layerExpressionContextScope() )
288 {
289 layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
290 }
291 processProvider( provider, context, *mPal );
292 if ( feedback )
293 feedback->emit providerRegistrationFinished( provider );
294 }
295 if ( feedback )
296 feedback->emit labelRegistrationFinished();
297}
298
300{
301 Q_ASSERT( mPal.get() );
302
303 // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
305
306 QPainter *painter = context.painter();
307
310 QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
311
312 QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
313 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
314
315 // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
317
318 // label blocking regions work by "chopping away" those regions from the permissible labeling area
319 const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
320 for ( const QgsLabelBlockingRegion &region : blockingRegions )
321 {
322 mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
323 }
324
325 if ( settings.flags() & Qgis::LabelingFlag::DrawCandidates )
326 {
327 // draw map boundary
328 QgsFeature f;
329 f.setGeometry( mapBoundaryGeom );
330 QVariantMap properties;
331 properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
332 properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
333 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "#0000ff" ) );
334 properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
335 properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
336 std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
337 boundarySymbol->startRender( context );
338 boundarySymbol->renderFeature( f, context );
339 boundarySymbol->stopRender( context );
340 }
341
342 if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
343 {
344 //PAL features are prerotated, so extent also needs to be unrotated
346 // yes - this is rotated in the opposite direction... phew, this is confusing!
348 }
349
350 QgsRectangle extent = extentGeom.boundingBox();
351
352 mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
353
354 QElapsedTimer t;
355 t.start();
356
357 // do the labeling itself
358 try
359 {
360 mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
361 }
362 catch ( std::exception &e )
363 {
364 Q_UNUSED( e )
365 QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
366 return;
367 }
368
369 if ( context.renderingStopped() )
370 {
371 return; // it has been canceled
372 }
373
374#if 1 // XXX strk
375 // features are pre-rotated but not scaled/translated,
376 // so we only disable rotation here. Ideally, they'd be
377 // also pre-scaled/translated, as suggested here:
378 // https://github.com/qgis/QGIS/issues/20071
380 xform.setMapRotation( 0, 0, 0 );
381#else
382 const QgsMapToPixel &xform = mMapSettings->mapToPixel();
383#endif
384
385 // draw rectangles with all candidates
386 // this is done before actual solution of the problem
387 // before number of candidates gets reduced
388 // TODO mCandidates.clear();
390 {
391 painter->setBrush( Qt::NoBrush );
392 for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
393 {
394 for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
395 {
396 pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
397
398 QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
399 }
400 }
401 }
402
403 // find the solution
404 mLabels = mPal->solveProblem( mProblem.get(), context,
408
409 // sort labels
410 std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mLayerRenderingOrderIds ) );
411
412 QgsDebugMsgLevel( QStringLiteral( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( mLabels.size() ), 4 );
413}
414
415void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
416{
417 QElapsedTimer t;
418 t.start();
419
421
423 QPainter *painter = context.painter();
424
425 // prepare for rendering
426 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
427 {
428 if ( !layerId.isEmpty() && provider->layerId() != layerId )
429 continue;
430
431 // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
432 // only contains generic scopes
433 QgsExpressionContextScopePopper popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
434
435 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
436 provider->startRender( context );
437 }
438
440 std::unique_ptr< QgsExpressionContextScopePopper > symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
441
442 // draw label backgrounds
443 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
444 {
445 if ( context.renderingStopped() )
446 break;
447
448 QgsLabelFeature *lf = label->getFeaturePart()->feature();
449 if ( !lf )
450 {
451 continue;
452 }
453
454 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
455 continue;
456
457 context.expressionContext().setFeature( lf->feature() );
458 context.expressionContext().setFields( lf->feature().fields() );
459
460 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
461
462 if ( lf->symbol() )
463 {
464 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
465 }
466 lf->provider()->drawLabelBackground( context, label );
467 }
468
469 // draw the labels
470 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
471 {
472 if ( context.renderingStopped() )
473 break;
474
475 QgsLabelFeature *lf = label->getFeaturePart()->feature();
476 if ( !lf )
477 {
478 continue;
479 }
480
481 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
482 continue;
483
484 context.expressionContext().setFeature( lf->feature() );
485 context.expressionContext().setFields( lf->feature().fields() );
486
487 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
488 if ( lf->symbol() )
489 {
490 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
491 }
492 lf->provider()->drawLabel( context, label );
493 // finished with symbol -- we can't keep it around after this, it may be deleted
494 lf->setSymbol( nullptr );
495 }
496
497 // draw unplaced labels. These are always rendered on top
499 {
500 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
501 {
502 if ( context.renderingStopped() )
503 break;
504 QgsLabelFeature *lf = label->getFeaturePart()->feature();
505 if ( !lf )
506 {
507 continue;
508 }
509
510 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
511 continue;
512
513 context.expressionContext().setFeature( lf->feature() );
514 context.expressionContext().setFields( lf->feature().fields() );
515
516 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
517 if ( lf->symbol() )
518 {
519 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
520 }
521 lf->provider()->drawUnplacedLabel( context, label );
522 // finished with symbol -- we can't keep it around after this, it may be deleted
523 lf->setSymbol( nullptr );
524 }
525 }
526
527 symbolScopePopper.reset();
528
529 // cleanup
530 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
531 {
532 if ( !layerId.isEmpty() && provider->layerId() != layerId )
533 continue;
534
535 provider->stopRender( context );
536 }
537
538 // Reset composition mode for further drawing operations
539 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
540
541 QgsDebugMsgLevel( QStringLiteral( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
542}
543
545{
546 mUnlabeled.clear();
547 mLabels.clear();
548 mProblem.reset();
549 mPal.reset();
550}
551
553{
554 return mResults.release();
555}
556
557
558//
559// QgsDefaultLabelingEngine
560//
561
564{
565
566}
567
569{
570 registerLabels( context );
571 if ( context.renderingStopped() )
572 {
573 cleanup();
574 return; // it has been canceled
575 }
576
577 solve( context );
578 if ( context.renderingStopped() )
579 {
580 cleanup();
581 return;
582 }
583
584 drawLabels( context );
585 cleanup();
586}
587
588
589//
590// QgsStagedRenderLabelingEngine
591//
592
595{
596
597}
598
600{
601 registerLabels( context );
602 if ( context.renderingStopped() )
603 {
604 cleanup();
605 return; // it has been canceled
606 }
607
608 solve( context );
609 if ( context.renderingStopped() )
610 {
611 cleanup();
612 return;
613 }
614}
615
616
618{
619 drawLabels( context, layerId );
620}
621
623{
624 cleanup();
625}
626
627
629
631{
632 return mLayer ? mLayer->provider() : nullptr;
633
634}
635
637 : mLayerId( layer ? layer->id() : QString() )
638 , mLayer( layer )
639 , mProviderId( providerId )
640{
641 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
642 {
643 mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
644 if ( const QgsFeatureRenderer *renderer = vl->renderer() )
645 mLayerReferenceScale = renderer->referenceScale();
646 }
647}
648
650{
651
652}
653
655{
656
657}
658
660{
661 const auto subproviders = subProviders();
662 for ( QgsAbstractLabelProvider *subProvider : subproviders )
663 {
664 subProvider->startRender( context );
665 }
666}
667
669{
670 const auto subproviders = subProviders();
671 for ( QgsAbstractLabelProvider *subProvider : subproviders )
672 {
673 subProvider->stopRender( context );
674 }
675}
676
678{
679 return mLayerExpressionContextScope.get();
680}
681
682//
683// QgsLabelingUtils
684//
685
686QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
687{
688 QStringList predefinedOrderString;
689 const auto constPositions = positions;
690 for ( Qgis::LabelPredefinedPointPosition position : constPositions )
691 {
692 switch ( position )
693 {
695 predefinedOrderString << QStringLiteral( "TL" );
696 break;
698 predefinedOrderString << QStringLiteral( "TSL" );
699 break;
701 predefinedOrderString << QStringLiteral( "T" );
702 break;
704 predefinedOrderString << QStringLiteral( "TSR" );
705 break;
707 predefinedOrderString << QStringLiteral( "TR" );
708 break;
710 predefinedOrderString << QStringLiteral( "L" );
711 break;
713 predefinedOrderString << QStringLiteral( "R" );
714 break;
716 predefinedOrderString << QStringLiteral( "BL" );
717 break;
719 predefinedOrderString << QStringLiteral( "BSL" );
720 break;
722 predefinedOrderString << QStringLiteral( "B" );
723 break;
725 predefinedOrderString << QStringLiteral( "BSR" );
726 break;
728 predefinedOrderString << QStringLiteral( "BR" );
729 break;
730 }
731 }
732 return predefinedOrderString.join( ',' );
733}
734
735QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
736{
737 QVector<Qgis::LabelPredefinedPointPosition> result;
738 const QStringList predefinedOrderList = positionString.split( ',' );
739 result.reserve( predefinedOrderList.size() );
740 for ( const QString &position : predefinedOrderList )
741 {
742 QString cleaned = position.trimmed().toUpper();
743 if ( cleaned == QLatin1String( "TL" ) )
745 else if ( cleaned == QLatin1String( "TSL" ) )
747 else if ( cleaned == QLatin1String( "T" ) )
749 else if ( cleaned == QLatin1String( "TSR" ) )
751 else if ( cleaned == QLatin1String( "TR" ) )
753 else if ( cleaned == QLatin1String( "L" ) )
755 else if ( cleaned == QLatin1String( "R" ) )
757 else if ( cleaned == QLatin1String( "BL" ) )
759 else if ( cleaned == QLatin1String( "BSL" ) )
761 else if ( cleaned == QLatin1String( "B" ) )
763 else if ( cleaned == QLatin1String( "BSR" ) )
765 else if ( cleaned == QLatin1String( "BR" ) )
767 }
768 return result;
769}
770
771QString QgsLabelingUtils::encodeLinePlacementFlags( QgsLabeling::LinePlacementFlags flags )
772{
773 QStringList parts;
774 if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
775 parts << QStringLiteral( "OL" );
776 if ( flags & QgsLabeling::LinePlacementFlag::AboveLine )
777 parts << QStringLiteral( "AL" );
778 if ( flags & QgsLabeling::LinePlacementFlag::BelowLine )
779 parts << QStringLiteral( "BL" );
780 if ( !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
781 parts << QStringLiteral( "LO" );
782 return parts.join( ',' );
783}
784
785QgsLabeling::LinePlacementFlags QgsLabelingUtils::decodeLinePlacementFlags( const QString &string )
786{
787 QgsLabeling::LinePlacementFlags flags = QgsLabeling::LinePlacementFlags();
788 const QStringList flagList = string.split( ',' );
789 bool foundLineOrientationFlag = false;
790 for ( const QString &flag : flagList )
791 {
792 QString cleaned = flag.trimmed().toUpper();
793 if ( cleaned == QLatin1String( "OL" ) )
794 flags |= QgsLabeling::LinePlacementFlag::OnLine;
795 else if ( cleaned == QLatin1String( "AL" ) )
796 flags |= QgsLabeling::LinePlacementFlag::AboveLine;
797 else if ( cleaned == QLatin1String( "BL" ) )
798 flags |= QgsLabeling::LinePlacementFlag::BelowLine;
799 else if ( cleaned == QLatin1String( "LO" ) )
800 foundLineOrientationFlag = true;
801 }
802 if ( !foundLineOrientationFlag )
803 flags |= QgsLabeling::LinePlacementFlag::MapOrientation;
804 return flags;
805}
@ DrawCandidates
Whether to draw rectangles of generated candidates (good for debugging)
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
@ UseAllLabels
Whether to draw all labels even if there would be collisions.
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
Definition: qgis.h:753
@ MiddleLeft
Label on left of point.
@ TopRight
Label on top-right of point.
@ MiddleRight
Label on right of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopMiddle
Label directly above point.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ TopLeft
Label on top-left of point.
@ BottomMiddle
Label directly below point.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
The QgsAbstractLabelProvider class is an interface class.
QgsExpressionContextScope * layerExpressionContextScope() const
Returns the expression context scope created from the layer associated with this provider.
virtual QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context)=0
Returns list of label features (they are owned by the provider and thus deleted on its destruction)
virtual void drawUnplacedLabel(QgsRenderContext &context, pal::LabelPosition *label) const
Draw an unplaced label.
virtual void stopRender(QgsRenderContext &context)
To be called after rendering is complete.
virtual QList< QgsAbstractLabelProvider * > subProviders()
Returns list of child providers - useful if the provider needs to put labels into more layers with di...
Qgis::LabelPlacement placement() const
What placement strategy to use for the labels.
void setEngine(const QgsLabelingEngine *engine)
Associate provider with a labeling engine (should be only called internally from QgsLabelingEngine)
virtual void drawLabel(QgsRenderContext &context, pal::LabelPosition *label) const =0
Draw this label at the position determined by the labeling engine.
double priority() const
Default priority of labels (may be overridden by individual labels)
virtual void drawLabelBackground(QgsRenderContext &context, pal::LabelPosition *label) const
Draw the background for the specified label.
QString name() const
Name of the layer (for statistics, debugging etc.) - does not need to be unique.
double layerReferenceScale() const
Returns the symbology reference scale of the layer associated with this provider.
QgsMapLayer * layer() const
Returns the associated layer, or nullptr if no layer is associated with the provider.
virtual void startRender(QgsRenderContext &context)
To be called before rendering of labels begins.
Flags flags() const
Flags associated with the provider.
QgsLabelObstacleSettings::ObstacleType obstacleType() const
How the feature geometries will work as obstacles.
@ MergeConnectedLines
Whether adjacent lines (with the same label text) should be merged.
@ DrawLabels
Whether the labels should be rendered.
@ CentroidMustBeInside
Whether location of centroid must be inside of polygons.
QString layerId() const
Returns ID of associated layer, or empty string if no layer is associated with the provider.
Qgis::UpsideDownLabelHandling upsidedownLabels() const
How to handle labels that would be upside down.
QgsAbstractLabelProvider(QgsMapLayer *layer, const QString &providerId=QString())
Construct the provider with default values.
QgsDefaultLabelingEngine()
Construct the labeling engine with default settings.
void run(QgsRenderContext &context) override
Runs the labeling job.
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * updateSymbolScope(const QgsSymbol *symbol, QgsExpressionContextScope *symbolScope=nullptr)
Updates a symbol scope related to a QgsSymbol to an expression context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsFields fields
Definition: qgsfeature.h:66
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:170
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Label blocking region (in map coordinates and CRS).
The QgsLabelFeature class describes a feature that should be used within the labeling engine.
QSizeF size(double angle=0.0) const
Size of the label (in map units)
QgsAbstractLabelProvider * provider() const
Returns provider of this instance.
void setSymbol(const QgsSymbol *symbol)
Sets the feature symbol associated with this label.
pal::Layer * mLayer
Pointer to PAL layer (assigned when registered to PAL)
QgsFeature feature() const
Returns the original feature associated with this label.
double zIndex() const
Returns the label's z-index.
const QgsSymbol * symbol() const
Returns the feature symbol associated with this label.
QgsFeedback subclass for granular reporting of labeling engine progress.
Stores global configuration for labeling engine.
Qgis::LabelPlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
bool testFlag(Qgis::LabelingFlag f) const
Test whether a particular flag is enabled.
Qgis::LabelingFlags flags() const
Gets flags of the labeling engine.
double maximumPolygonCandidatesPerCmSquared() const
Returns the maximum number of polygon label candidate positions per centimeter squared.
double maximumLineCandidatesPerCm() const
Returns the maximum number of line label candidate positions per centimeter.
The QgsLabelingEngine class provides map labeling functionality.
std::unique_ptr< pal::Pal > mPal
std::unique_ptr< QgsLabelingResults > mResults
Resulting labeling layout.
QgsMapSettings mMapSettings
Associated map settings instance.
void solve(QgsRenderContext &context)
Solves the label problem.
QList< pal::LabelPosition * > mUnlabeled
std::unique_ptr< pal::Problem > mProblem
const QgsMapSettings & mapSettings() const
Gets associated map settings.
QList< pal::LabelPosition * > mLabels
QgsLabelingResults * takeResults()
Returns pointer to recently computed results and pass the ownership of results to the caller.
void cleanup()
Cleans up the engine following a call to registerLabels() or solve().
void setMapSettings(const QgsMapSettings &mapSettings)
Associate map settings instance.
void registerLabels(QgsRenderContext &context)
Runs the label registration step.
QList< QgsAbstractLabelProvider * > mSubProviders
void addProvider(QgsAbstractLabelProvider *provider)
Add provider of label features. Takes ownership of the provider.
void drawLabels(QgsRenderContext &context, const QString &layerId=QString())
Draws labels to the specified render context.
QStringList participatingLayerIds() const
Returns a list of layer IDs for layers with providers in the engine.
QList< QgsMapLayer * > participatingLayers() const
Returns a list of layers with providers in the engine.
void processProvider(QgsAbstractLabelProvider *provider, QgsRenderContext &context, pal::Pal &p)
QgsLabelingEngine()
Construct the labeling engine with default settings.
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider's initialization failed. Provider instance is deleted.
QList< QgsAbstractLabelProvider * > mProviders
List of providers (the are owned by the labeling engine)
virtual ~QgsLabelingEngine()
Clean up everything (especially the registered providers)
Class that stores computed placement from labeling engine.
static QString encodePredefinedPositionOrder(const QVector< Qgis::LabelPredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
static QString encodeLinePlacementFlags(QgsLabeling::LinePlacementFlags flags)
Encodes line placement flags to a string.
static QVector< Qgis::LabelPredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
static QgsLabeling::LinePlacementFlags decodeLinePlacementFlags(const QString &string)
Decodes a string to set of line placement flags.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
The QgsMapSettings class contains configuration for rendering of the map.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
QgsGeometry labelBoundaryGeometry() const
Returns the label boundary geometry, which restricts where in the rendered map labels are permitted t...
QStringList layerIds(bool expandGroupLayers=false) const
Returns the list of layer IDs which will be rendered in the map.
const QgsMapToPixel & mapToPixel() const
QList< QgsLabelBlockingRegion > labelBlockingRegions() const
Returns the list of regions to avoid placing labels within.
double extentBuffer() const
Returns the buffer in map units to use around the visible extent for rendering symbols whose correspo...
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
QPolygonF visiblePolygonWithBuffer() const
Returns the visible area as a polygon (may be rotated) with extent buffer included.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
void setMapRotation(double degrees, double cx, double cy)
Sets map rotation in degrees (clockwise).
static void drawLabelCandidateRect(pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=nullptr)
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:296
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
Contains information about the context of a rendering operation.
double convertToMapUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Scoped object for temporary override of the symbologyReferenceScale property of a QgsRenderContext.
void finalize()
Finalizes and cleans up the engine following the rendering of labels for the last layer to be labeled...
void run(QgsRenderContext &context) override
Runs the labeling job.
QgsStagedRenderLabelingEngine()
Construct the labeling engine with default settings.
void renderLabelsForLayer(QgsRenderContext &context, const QString &layerId)
Renders all the labels which belong only to the layer with matching layerId to the specified render c...
Represents a vector layer which manages a vector based data sets.
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:94
LabelPosition is a candidate feature label position.
Definition: labelposition.h:56
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
A set of features which influence the labeling process.
Definition: layer.h:63
void setUpsidedownLabels(Qgis::UpsideDownLabelHandling ud)
Sets how upside down labels will be handled within the layer.
Definition: layer.h:263
bool registerFeature(QgsLabelFeature *label)
Register a feature in the layer.
Definition: layer.cpp:79
QgsAbstractLabelProvider * provider() const
Returns pointer to the associated provider.
Definition: layer.h:157
void setObstacleType(QgsLabelObstacleSettings::ObstacleType obstacleType)
Sets the obstacle type, which controls how features within the layer act as obstacles for labels.
Definition: layer.h:228
void setMergeConnectedLines(bool merge)
Sets whether connected lines should be merged before labeling.
Definition: layer.h:250
void setCentroidInside(bool forceInside)
Sets whether labels placed at the centroid of features within the layer are forced to be placed insid...
Definition: layer.h:278
Main Pal labeling class.
Definition: pal.h:83
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
add a new layer
Definition: pal.cpp:86
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39