QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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#include "qgsruntimeprofiler.h"
34
35// helper function for checking for job cancellation within PAL
36static bool _palIsCanceled( void *ctx )
37{
38 return ( reinterpret_cast< QgsRenderContext * >( ctx ) )->renderingStopped();
39}
40
42
48class QgsLabelSorter
49{
50 public:
51
52 explicit QgsLabelSorter( const QStringList &layerRenderingOrderIds )
53 : mLayerRenderingOrderIds( layerRenderingOrderIds )
54 {}
55
56 bool operator()( pal::LabelPosition *lp1, pal::LabelPosition *lp2 ) const
57 {
58 QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
59 QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
60
61 if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
62 return lf1->zIndex() < lf2->zIndex();
63
64 //equal z-index, so fallback to respecting layer render order
65 int layer1Pos = mLayerRenderingOrderIds.indexOf( lf1->provider()->layerId() );
66 int layer2Pos = mLayerRenderingOrderIds.indexOf( lf2->provider()->layerId() );
67 if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
68 return layer1Pos > layer2Pos; //higher positions are rendered first
69
70 //same layer, so render larger labels first
71 return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
72 }
73
74 private:
75
76 const QStringList mLayerRenderingOrderIds;
77};
78
80
81//
82// QgsLabelingEngine
83//
84
86 : mResults( new QgsLabelingResults )
87{}
88
90{
91 qDeleteAll( mProviders );
92 qDeleteAll( mSubProviders );
93}
94
96{
98 mLayerRenderingOrderIds = mMapSettings.layerIds();
99 if ( mResults )
100 mResults->setMapSettings( mapSettings );
101}
102
103QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
104{
105 QList< QgsMapLayer * > layers;
106
107 // try to return layers sorted in the desired z order for rendering
108 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
109 std::sort( providersByZ.begin(), providersByZ.end(),
110 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
111 {
112 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
113 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
114
115 if ( providerA && providerB )
116 {
117 return providerA->settings().zIndex < providerB->settings().zIndex ;
118 }
119 return false;
120 } );
121
122 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
123 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
124 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
125 {
126 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
127 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
128
129 if ( providerA && providerB )
130 {
131 return providerA->settings().zIndex < providerB->settings().zIndex ;
132 }
133 return false;
134 } );
135
136 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
137 {
138 if ( provider->layer() && !layers.contains( provider->layer() ) )
139 layers << provider->layer();
140 }
141 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
142 {
143 if ( provider->layer() && !layers.contains( provider->layer() ) )
144 layers << provider->layer();
145 }
146 return layers;
147}
148
150{
151 QStringList layers;
152
153 // try to return layers sorted in the desired z order for rendering
154 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
155 std::sort( providersByZ.begin(), providersByZ.end(),
156 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
157 {
158 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
159 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
160
161 if ( providerA && providerB )
162 {
163 return providerA->settings().zIndex < providerB->settings().zIndex ;
164 }
165 return false;
166 } );
167
168 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
169 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
170 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
171 {
172 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
173 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
174
175 if ( providerA && providerB )
176 {
177 return providerA->settings().zIndex < providerB->settings().zIndex ;
178 }
179 return false;
180 } );
181
182 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
183 {
184 if ( !layers.contains( provider->layerId() ) )
185 layers << provider->layerId();
186 }
187 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
188 {
189 if ( !layers.contains( provider->layerId() ) )
190 layers << provider->layerId();
191 }
192 return layers;
193}
194
196{
197 provider->setEngine( this );
198 mProviders << provider;
199}
200
202{
203 int idx = mProviders.indexOf( provider );
204 if ( idx >= 0 )
205 {
206 delete mProviders.takeAt( idx );
207 }
208}
209
211{
212 QgsAbstractLabelProvider::Flags flags = provider->flags();
213
214 // create the pal layer
215 pal::Layer *l = p.addLayer( provider,
216 provider->name(),
217 provider->placement(),
218 provider->priority(),
219 true,
220 flags.testFlag( QgsAbstractLabelProvider::DrawLabels ) );
221
222 // set whether adjacent lines should be merged
224
225 // set obstacle type
226 l->setObstacleType( provider->obstacleType() );
227
228 // set whether location of centroid must be inside of polygons
230
231 // set how to show upside-down labels
232 l->setUpsidedownLabels( provider->upsidedownLabels() );
233
234 const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
235
236 for ( QgsLabelFeature *feature : features )
237 {
238 try
239 {
240 l->registerFeature( feature );
241 }
242 catch ( std::exception &e )
243 {
244 Q_UNUSED( e )
245 QgsDebugMsgLevel( QStringLiteral( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
246 continue;
247 }
248 }
249
250 // any sub-providers?
251 const auto subproviders = provider->subProviders();
252 for ( QgsAbstractLabelProvider *subProvider : subproviders )
253 {
254 mSubProviders << subProvider;
255 processProvider( subProvider, context, p );
256 }
257}
258
260{
261 std::unique_ptr< QgsScopedRuntimeProfile > registeringProfile;
263 {
264 registeringProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Registering labels" ), QStringLiteral( "rendering" ) );
265 }
266
267 QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
268
269 if ( feedback )
270 feedback->emit labelRegistrationAboutToBegin();
271
273
274 mPal = std::make_unique< pal::Pal >();
275
276 mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ) );
277 mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ), 2 ) );
278
279 mPal->setShowPartialLabels( settings.testFlag( Qgis::LabelingFlag::UsePartialCandidates ) );
280 mPal->setPlacementVersion( settings.placementVersion() );
281
282 // for each provider: get labels and register them in PAL
283 const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
284 int index = 0;
285 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
286 {
287 if ( feedback )
288 {
289 feedback->emit providerRegistrationAboutToBegin( provider );
290 feedback->setProgress( index * step );
291 }
292 index++;
293 std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
294 if ( provider->layerExpressionContextScope() )
295 {
296 layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
297 }
298 processProvider( provider, context, *mPal );
299 if ( feedback )
300 feedback->emit providerRegistrationFinished( provider );
301 }
302 if ( feedback )
303 feedback->emit labelRegistrationFinished();
304}
305
307{
308 Q_ASSERT( mPal.get() );
309
310 // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
312
313 QPainter *painter = context.painter();
314
317 QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
318
319 QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
320 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
321
322 // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
324
325 // label blocking regions work by "chopping away" those regions from the permissible labeling area
326 const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
327 for ( const QgsLabelBlockingRegion &region : blockingRegions )
328 {
329 mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
330 }
331
332 if ( settings.flags() & Qgis::LabelingFlag::DrawCandidates )
333 {
334 // draw map boundary
335 QgsFeature f;
336 f.setGeometry( mapBoundaryGeom );
337 QVariantMap properties;
338 properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
339 properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
340 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "#0000ff" ) );
341 properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
342 properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
343 std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
344 boundarySymbol->startRender( context );
345 boundarySymbol->renderFeature( f, context );
346 boundarySymbol->stopRender( context );
347 }
348
349 if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
350 {
351 //PAL features are prerotated, so extent also needs to be unrotated
353 // yes - this is rotated in the opposite direction... phew, this is confusing!
355 }
356
357 QgsRectangle extent = extentGeom.boundingBox();
358
359 mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
360
361 QElapsedTimer t;
362 t.start();
363
364 // do the labeling itself
365 try
366 {
367 mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
368 }
369 catch ( std::exception &e )
370 {
371 Q_UNUSED( e )
372 QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
373 return;
374 }
375
376 if ( context.renderingStopped() )
377 {
378 return; // it has been canceled
379 }
380
381#if 1 // XXX strk
382 // features are pre-rotated but not scaled/translated,
383 // so we only disable rotation here. Ideally, they'd be
384 // also pre-scaled/translated, as suggested here:
385 // https://github.com/qgis/QGIS/issues/20071
387 xform.setMapRotation( 0, 0, 0 );
388#else
389 const QgsMapToPixel &xform = mMapSettings->mapToPixel();
390#endif
391
392 // draw rectangles with all candidates
393 // this is done before actual solution of the problem
394 // before number of candidates gets reduced
395 // TODO mCandidates.clear();
397 {
398 painter->setBrush( Qt::NoBrush );
399 for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
400 {
401 for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
402 {
403 pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
404
405 QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
406 }
407 }
408 }
409
410 // find the solution
411 mLabels = mPal->solveProblem( mProblem.get(), context,
415
416 // sort labels
417 std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mLayerRenderingOrderIds ) );
418
419 QgsDebugMsgLevel( QStringLiteral( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( mLabels.size() ), 4 );
420}
421
422void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
423{
424 QElapsedTimer t;
425 t.start();
426
427 std::unique_ptr< QgsScopedRuntimeProfile > drawingProfile;
429 {
430 drawingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering labels" ), QStringLiteral( "rendering" ) );
431 }
432
434
436 QPainter *painter = context.painter();
437
438 // prepare for rendering
439 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
440 {
441 if ( !layerId.isEmpty() && provider->layerId() != layerId )
442 continue;
443
444 // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
445 // only contains generic scopes
446 QgsExpressionContextScopePopper popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
447
448 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
449 provider->startRender( context );
450 }
451
453 std::unique_ptr< QgsExpressionContextScopePopper > symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
454
455 // draw label backgrounds
456 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
457 {
458 if ( context.renderingStopped() )
459 break;
460
461 QgsLabelFeature *lf = label->getFeaturePart()->feature();
462 if ( !lf )
463 {
464 continue;
465 }
466
467 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
468 continue;
469
470 context.expressionContext().setFeature( lf->feature() );
471 context.expressionContext().setFields( lf->feature().fields() );
472
473 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
474
475 if ( lf->symbol() )
476 {
477 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
478 }
479 lf->provider()->drawLabelBackground( context, label );
480 }
481
482 // draw the labels
483 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
484 {
485 if ( context.renderingStopped() )
486 break;
487
488 QgsLabelFeature *lf = label->getFeaturePart()->feature();
489 if ( !lf )
490 {
491 continue;
492 }
493
494 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
495 continue;
496
497 context.expressionContext().setFeature( lf->feature() );
498 context.expressionContext().setFields( lf->feature().fields() );
499
500 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
501 if ( lf->symbol() )
502 {
503 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
504 }
505 lf->provider()->drawLabel( context, label );
506 // finished with symbol -- we can't keep it around after this, it may be deleted
507 lf->setSymbol( nullptr );
508 }
509
510 // draw unplaced labels. These are always rendered on top
512 {
513 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
514 {
515 if ( context.renderingStopped() )
516 break;
517 QgsLabelFeature *lf = label->getFeaturePart()->feature();
518 if ( !lf )
519 {
520 continue;
521 }
522
523 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
524 continue;
525
526 context.expressionContext().setFeature( lf->feature() );
527 context.expressionContext().setFields( lf->feature().fields() );
528
529 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
530 if ( lf->symbol() )
531 {
532 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
533 }
534 lf->provider()->drawUnplacedLabel( context, label );
535 // finished with symbol -- we can't keep it around after this, it may be deleted
536 lf->setSymbol( nullptr );
537 }
538 }
539
540 symbolScopePopper.reset();
541
542 // cleanup
543 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
544 {
545 if ( !layerId.isEmpty() && provider->layerId() != layerId )
546 continue;
547
548 provider->stopRender( context );
549 }
550
551 // Reset composition mode for further drawing operations
552 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
553
554 QgsDebugMsgLevel( QStringLiteral( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
555}
556
558{
559 mUnlabeled.clear();
560 mLabels.clear();
561 mProblem.reset();
562 mPal.reset();
563}
564
566{
567 return mResults.release();
568}
569
570
571//
572// QgsDefaultLabelingEngine
573//
574
577{
578
579}
580
582{
583 registerLabels( context );
584 if ( context.renderingStopped() )
585 {
586 cleanup();
587 return; // it has been canceled
588 }
589
590 solve( context );
591 if ( context.renderingStopped() )
592 {
593 cleanup();
594 return;
595 }
596
597 drawLabels( context );
598 cleanup();
599}
600
601
602//
603// QgsStagedRenderLabelingEngine
604//
605
608{
609
610}
611
613{
614 registerLabels( context );
615 if ( context.renderingStopped() )
616 {
617 cleanup();
618 return; // it has been canceled
619 }
620
621 solve( context );
622 if ( context.renderingStopped() )
623 {
624 cleanup();
625 return;
626 }
627}
628
629
631{
632 drawLabels( context, layerId );
633}
634
636{
637 cleanup();
638}
639
640
642
644{
645 return mLayer ? mLayer->provider() : nullptr;
646
647}
648
650 : mLayerId( layer ? layer->id() : QString() )
651 , mLayer( layer )
652 , mProviderId( providerId )
653{
654 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
655 {
656 mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
657 if ( const QgsFeatureRenderer *renderer = vl->renderer() )
658 mLayerReferenceScale = renderer->referenceScale();
659 }
660}
661
663{
664
665}
666
668{
669
670}
671
673{
674 const auto subproviders = subProviders();
675 for ( QgsAbstractLabelProvider *subProvider : subproviders )
676 {
677 subProvider->startRender( context );
678 }
679}
680
682{
683 const auto subproviders = subProviders();
684 for ( QgsAbstractLabelProvider *subProvider : subproviders )
685 {
686 subProvider->stopRender( context );
687 }
688}
689
691{
692 return mLayerExpressionContextScope.get();
693}
694
695//
696// QgsLabelingUtils
697//
698
699QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
700{
701 QStringList predefinedOrderString;
702 const auto constPositions = positions;
703 for ( Qgis::LabelPredefinedPointPosition position : constPositions )
704 {
705 switch ( position )
706 {
708 predefinedOrderString << QStringLiteral( "TL" );
709 break;
711 predefinedOrderString << QStringLiteral( "TSL" );
712 break;
714 predefinedOrderString << QStringLiteral( "T" );
715 break;
717 predefinedOrderString << QStringLiteral( "TSR" );
718 break;
720 predefinedOrderString << QStringLiteral( "TR" );
721 break;
723 predefinedOrderString << QStringLiteral( "L" );
724 break;
726 predefinedOrderString << QStringLiteral( "R" );
727 break;
729 predefinedOrderString << QStringLiteral( "BL" );
730 break;
732 predefinedOrderString << QStringLiteral( "BSL" );
733 break;
735 predefinedOrderString << QStringLiteral( "B" );
736 break;
738 predefinedOrderString << QStringLiteral( "BSR" );
739 break;
741 predefinedOrderString << QStringLiteral( "BR" );
742 break;
743 }
744 }
745 return predefinedOrderString.join( ',' );
746}
747
748QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
749{
750 QVector<Qgis::LabelPredefinedPointPosition> result;
751 const QStringList predefinedOrderList = positionString.split( ',' );
752 result.reserve( predefinedOrderList.size() );
753 for ( const QString &position : predefinedOrderList )
754 {
755 QString cleaned = position.trimmed().toUpper();
756 if ( cleaned == QLatin1String( "TL" ) )
758 else if ( cleaned == QLatin1String( "TSL" ) )
760 else if ( cleaned == QLatin1String( "T" ) )
762 else if ( cleaned == QLatin1String( "TSR" ) )
764 else if ( cleaned == QLatin1String( "TR" ) )
766 else if ( cleaned == QLatin1String( "L" ) )
768 else if ( cleaned == QLatin1String( "R" ) )
770 else if ( cleaned == QLatin1String( "BL" ) )
772 else if ( cleaned == QLatin1String( "BSL" ) )
774 else if ( cleaned == QLatin1String( "B" ) )
776 else if ( cleaned == QLatin1String( "BSR" ) )
778 else if ( cleaned == QLatin1String( "BR" ) )
780 }
781 return result;
782}
783
785{
786 QStringList parts;
788 parts << QStringLiteral( "OL" );
790 parts << QStringLiteral( "AL" );
792 parts << QStringLiteral( "BL" );
794 parts << QStringLiteral( "LO" );
795 return parts.join( ',' );
796}
797
799{
801 const QStringList flagList = string.split( ',' );
802 bool foundLineOrientationFlag = false;
803 for ( const QString &flag : flagList )
804 {
805 QString cleaned = flag.trimmed().toUpper();
806 if ( cleaned == QLatin1String( "OL" ) )
808 else if ( cleaned == QLatin1String( "AL" ) )
810 else if ( cleaned == QLatin1String( "BL" ) )
812 else if ( cleaned == QLatin1String( "LO" ) )
813 foundLineOrientationFlag = true;
814 }
815 if ( !foundLineOrientationFlag )
817 return flags;
818}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
Definition: qgis.h:1009
@ 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.
@ Millimeters
Millimeters.
@ RecordProfile
Enable run-time profiling while rendering (since QGIS 3.34)
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
Definition: qgis.h:935
@ 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:167
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:61
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:162
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
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:164
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 QVector< Qgis::LabelPredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
static Qgis::LabelLinePlacementFlags decodeLinePlacementFlags(const QString &string)
Decodes a string to set of line placement flags.
static QString encodeLinePlacementFlags(Qgis::LabelLinePlacementFlags flags)
Encodes line placement flags to a string.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
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
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:262
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:307
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...
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
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:87
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39