QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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 "feature.h"
20#include "labelposition.h"
21#include "layer.h"
22#include "pal.h"
23#include "problem.h"
25#include "qgsfillsymbol.h"
27#include "qgslabelingresults.h"
28#include "qgslogger.h"
29#include "qgsmaplayer.h"
30#include "qgsrendercontext.h"
31#include "qgsruntimeprofiler.h"
32#include "qgssymbol.h"
33#include "qgstextlabelfeature.h"
35
36#include <QString>
37#include <QUuid>
38
39#include "moc_qgslabelingengine.cpp"
40
41using namespace Qt::StringLiterals;
42
43// helper function for checking for job cancellation within PAL
44static bool _palIsCanceled( void *ctx )
45{
46 return ( reinterpret_cast< QgsRenderContext * >( ctx ) )->renderingStopped();
47}
48
50
56class QgsLabelSorter
57{
58 public:
59
60 explicit QgsLabelSorter( const QStringList &layerRenderingOrderIds )
61 : mLayerRenderingOrderIds( layerRenderingOrderIds )
62 {}
63
64 bool operator()( pal::LabelPosition *lp1, pal::LabelPosition *lp2 ) const
65 {
66 QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
67 QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
68
69 if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
70 return lf1->zIndex() < lf2->zIndex();
71
72 //equal z-index, so fallback to respecting layer render order
73 int layer1Pos = mLayerRenderingOrderIds.indexOf( lf1->provider()->layerId() );
74 int layer2Pos = mLayerRenderingOrderIds.indexOf( lf2->provider()->layerId() );
75 if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
76 return layer1Pos > layer2Pos; //higher positions are rendered first
77
78 //same layer, so render larger labels first
79 return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
80 }
81
82 private:
83
84 const QStringList mLayerRenderingOrderIds;
85};
86
88
89//
90// QgsLabelingEngine
91//
92
96
98{
99 qDeleteAll( mProviders );
100 qDeleteAll( mSubProviders );
101}
102
104{
106 mLayerRenderingOrderIds = mMapSettings.layerIds();
107 if ( mResults )
108 mResults->setMapSettings( mapSettings );
109}
110
112{
113 const QList<const QgsAbstractLabelingEngineRule *> rules = mMapSettings.labelingEngineSettings().rules();
114 bool res = true;
115 for ( const QgsAbstractLabelingEngineRule *rule : rules )
116 {
117 if ( !rule->active() || !rule->isAvailable() )
118 continue;
119
120 std::unique_ptr< QgsAbstractLabelingEngineRule > ruleClone( rule->clone() );
121 res = ruleClone->prepare( context ) && res;
122 mEngineRules.emplace_back( std::move( ruleClone ) );
123 }
124 return res;
125}
126
127QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
128{
129 QList< QgsMapLayer * > layers;
130
131 // try to return layers sorted in the desired z order for rendering
132 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
133 std::sort( providersByZ.begin(), providersByZ.end(),
134 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
135 {
136 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
137 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
138
139 if ( providerA && providerB )
140 {
141 return providerA->settings().zIndex < providerB->settings().zIndex ;
142 }
143 return false;
144 } );
145
146 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
147 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
148 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
149 {
150 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
151 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
152
153 if ( providerA && providerB )
154 {
155 return providerA->settings().zIndex < providerB->settings().zIndex ;
156 }
157 return false;
158 } );
159
160 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
161 {
162 if ( provider->layer() && !layers.contains( provider->layer() ) )
163 layers << provider->layer();
164 }
165 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
166 {
167 if ( provider->layer() && !layers.contains( provider->layer() ) )
168 layers << provider->layer();
169 }
170 return layers;
171}
172
174{
175 QStringList layers;
176
177 // try to return layers sorted in the desired z order for rendering
178 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
179 std::sort( providersByZ.begin(), providersByZ.end(),
180 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
181 {
182 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
183 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
184
185 if ( providerA && providerB )
186 {
187 return providerA->settings().zIndex < providerB->settings().zIndex ;
188 }
189 return false;
190 } );
191
192 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
193 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
194 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
195 {
196 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
197 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
198
199 if ( providerA && providerB )
200 {
201 return providerA->settings().zIndex < providerB->settings().zIndex ;
202 }
203 return false;
204 } );
205
206 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
207 {
208 if ( !layers.contains( provider->layerId() ) )
209 layers << provider->layerId();
210 }
211 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
212 {
213 if ( !layers.contains( provider->layerId() ) )
214 layers << provider->layerId();
215 }
216 return layers;
217}
218
220{
221 provider->setEngine( this );
222 mProviders << provider;
223 const QString id = QUuid::createUuid().toString( QUuid::WithoutBraces );
224 mProvidersById.insert( id, provider );
225 return id;
226}
227
229{
230 return mProvidersById.value( id );
231}
232
234{
235 int idx = mProviders.indexOf( provider );
236 if ( idx >= 0 )
237 {
238 mProvidersById.remove( mProvidersById.key( provider ) );
239 delete mProviders.takeAt( idx );
240 }
241}
242
244{
245 QgsAbstractLabelProvider::Flags flags = provider->flags();
246
247 // create the pal layer
248 pal::Layer *l = p.addLayer( provider,
249 provider->name(),
250 provider->placement(),
251 provider->priority(),
252 true,
253 flags.testFlag( QgsAbstractLabelProvider::DrawLabels ) );
254
255 // set whether adjacent lines should be merged
257
258 // set obstacle type
259 l->setObstacleType( provider->obstacleType() );
260
261 // set whether location of centroid must be inside of polygons
263
264 // set how to show upside-down labels
265 l->setUpsidedownLabels( provider->upsidedownLabels() );
266
267 const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
268
269 for ( QgsLabelFeature *feature : features )
270 {
271 try
272 {
273 l->registerFeature( feature );
274 }
275 catch ( std::exception &e )
276 {
277 Q_UNUSED( e )
278 QgsDebugMsgLevel( u"Ignoring feature %1 due PAL exception:"_s.arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
279 continue;
280 }
281 }
282
283 // any sub-providers?
284 const auto subproviders = provider->subProviders();
285 for ( QgsAbstractLabelProvider *subProvider : subproviders )
286 {
287 mSubProviders << subProvider;
288 processProvider( subProvider, context, p );
289 }
290}
291
293{
294 std::unique_ptr< QgsScopedRuntimeProfile > registeringProfile;
296 {
297 registeringProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Registering labels" ), u"rendering"_s );
298 }
299
300 QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
301
302 if ( feedback )
303 feedback->emit labelRegistrationAboutToBegin();
304
305 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
306
307 mPal = std::make_unique< pal::Pal >();
308
309 mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ) );
310 mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ), 2 ) );
311
312 mPal->setShowPartialLabels( settings.testFlag( Qgis::LabelingFlag::UsePartialCandidates ) );
313 mPal->setPlacementVersion( settings.placementVersion() );
314
315 QList< QgsAbstractLabelingEngineRule * > rules;
316 rules.reserve( static_cast< int >( mEngineRules.size() ) );
317 for ( auto &it : mEngineRules )
318 {
319 rules.append( it.get() );
320 }
321 mPal->setRules( rules );
322
323 // for each provider: get labels and register them in PAL
324 const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
325 int index = 0;
326 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
327 {
328 if ( feedback )
329 {
330 feedback->emit providerRegistrationAboutToBegin( provider );
331 feedback->setProgress( index * step );
332 }
333 index++;
334 std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
335 if ( provider->layerExpressionContextScope() )
336 {
337 layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
338 }
339 processProvider( provider, context, *mPal );
340 if ( feedback )
341 feedback->emit providerRegistrationFinished( provider );
342 }
343 if ( feedback )
344 feedback->emit labelRegistrationFinished();
345}
346
348{
349 Q_ASSERT( mPal.get() );
350
351 // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
352 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
353
354 QPainter *painter = context.painter();
355
356 QgsRectangle r1 = mMapSettings.visibleExtent();
357 r1.grow( mMapSettings.extentBuffer() );
358 QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
359
360 QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
361 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
362
363 // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
364 QgsGeometry mapBoundaryGeom = !mMapSettings.labelBoundaryGeometry().isNull() ? mMapSettings.labelBoundaryGeometry() : QgsGeometry::fromQPolygonF( visiblePoly );
365
366 // label blocking regions work by "chopping away" those regions from the permissible labeling area
367 const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
368 for ( const QgsLabelBlockingRegion &region : blockingRegions )
369 {
370 mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
371 }
372
373 if ( settings.flags() & Qgis::LabelingFlag::DrawCandidates )
374 {
375 // draw map boundary
376 QgsFeature f;
377 f.setGeometry( mapBoundaryGeom );
378 QVariantMap properties;
379 properties.insert( u"style"_s, u"no"_s );
380 properties.insert( u"style_border"_s, u"solid"_s );
381 properties.insert( u"color_border"_s, u"#0000ff"_s );
382 properties.insert( u"width_border"_s, u"0.3"_s );
383 properties.insert( u"joinstyle"_s, u"miter"_s );
384 std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
385 boundarySymbol->startRender( context );
386 boundarySymbol->renderFeature( f, context );
387 boundarySymbol->stopRender( context );
388 }
389
390 if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
391 {
392 //PAL features are prerotated, so extent also needs to be unrotated
393 extentGeom.rotate( -mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
394 // yes - this is rotated in the opposite direction... phew, this is confusing!
395 mapBoundaryGeom.rotate( mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
396 }
397
398 QgsRectangle extent = extentGeom.boundingBox();
399
400 mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
401
402 QElapsedTimer t;
403 t.start();
404
405 // do the labeling itself
406 try
407 {
408 mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
409 }
410 catch ( std::exception &e )
411 {
412 Q_UNUSED( e )
413 QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
414 return;
415 }
416
417 if ( context.renderingStopped() )
418 {
419 return; // it has been canceled
420 }
421
422#if 1 // XXX strk
423 // features are pre-rotated but not scaled/translated,
424 // so we only disable rotation here. Ideally, they'd be
425 // also pre-scaled/translated, as suggested here:
426 // https://github.com/qgis/QGIS/issues/20071
427 QgsMapToPixel xform = mMapSettings.mapToPixel();
428 xform.setMapRotation( 0, 0, 0 );
429#else
430 const QgsMapToPixel &xform = mMapSettings->mapToPixel();
431#endif
432
433 // draw rectangles with all candidates
434 // this is done before actual solution of the problem
435 // before number of candidates gets reduced
436 // TODO mCandidates.clear();
438 {
439 painter->setBrush( Qt::NoBrush );
440 for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
441 {
442 for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
443 {
444 pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
445
446 drawLabelCandidateRect( lp, context, &xform );
447 }
448 }
449 }
450
451 // find the solution
452 mLabels = mPal->solveProblem( mProblem.get(), context,
456
457 // sort labels
458 std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mLayerRenderingOrderIds ) );
459
460 QgsDebugMsgLevel( u"LABELING work: %1 ms ... labels# %2"_s.arg( t.elapsed() ).arg( mLabels.size() ), 4 );
461}
462
463void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
464{
465 QElapsedTimer t;
466 t.start();
467
468 std::unique_ptr< QgsScopedRuntimeProfile > drawingProfile;
470 {
471 drawingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering labels" ), u"rendering"_s );
472 }
473
474 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
475
477 QPainter *painter = context.painter();
478
479 // prepare for rendering
480 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
481 {
482 if ( !layerId.isEmpty() && provider->layerId() != layerId )
483 continue;
484
485 // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
486 // only contains generic scopes
487 QgsExpressionContextScopePopper popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
488
489 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
490 provider->startRender( context );
491 }
492
494 auto symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
495
496 // draw label backgrounds
497 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
498 {
499 if ( context.renderingStopped() )
500 break;
501
502 QgsLabelFeature *lf = label->getFeaturePart()->feature();
503 if ( !lf )
504 {
505 continue;
506 }
507
508 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
509 continue;
510
511 context.expressionContext().setFeature( lf->feature() );
512 context.expressionContext().setFields( lf->feature().fields() );
513
514 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
515
516 if ( lf->symbol() )
517 {
518 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
519 }
520 lf->provider()->drawLabelBackground( context, label );
521 }
522
524 {
525 // features are pre-rotated but not scaled/translated,
526 // so we only disable rotation here. Ideally, they'd be
527 // also pre-scaled/translated, as suggested here:
528 // https://github.com/qgis/QGIS/issues/20071
529 QgsMapToPixel xform = context.mapToPixel();
530 xform.setMapRotation( 0, 0, 0 );
531
532 std::function<void( pal::LabelPosition * )> drawLabelRect;
533 drawLabelRect = [&xform, painter, &drawLabelRect]( pal::LabelPosition * label )
534 {
535 QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
536
537 QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
538 QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
539 painter->save();
540 painter->setRenderHint( QPainter::Antialiasing, false );
541 painter->translate( QPointF( outPt.x(), outPt.y() ) );
542 painter->rotate( -label->getAlpha() * 180 / M_PI );
543
544 if ( label->conflictsWithObstacle() )
545 {
546 painter->setBrush( QColor( 255, 0, 0, 100 ) );
547 painter->setPen( QColor( 255, 0, 0, 150 ) );
548 }
549 else
550 {
551 painter->setBrush( QColor( 0, 255, 0, 100 ) );
552 painter->setPen( QColor( 0, 255, 0, 150 ) );
553 }
554
555 painter->drawRect( rect );
556 painter->restore();
557
558 if ( pal::LabelPosition *nextPart = label->nextPart() )
559 drawLabelRect( nextPart );
560 };
561
562 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
563 {
564 drawLabelRect( label );
565 }
566
568 {
569 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
570 {
571 drawLabelRect( label );
572 }
573 }
574 }
575 else
576 {
578 {
579 // features are pre-rotated but not scaled/translated,
580 // so we only disable rotation here. Ideally, they'd be
581 // also pre-scaled/translated, as suggested here:
582 // https://github.com/qgis/QGIS/issues/20071
583 QgsMapToPixel xform = context.mapToPixel();
584 xform.setMapRotation( 0, 0, 0 );
585
586 std::function<void( pal::LabelPosition * )> drawLabelMetricsRecursive;
587 drawLabelMetricsRecursive = [&xform, &context, &drawLabelMetricsRecursive]( pal::LabelPosition * label )
588 {
589 QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
590 QgsLabelingEngine::drawLabelMetrics( label, xform, context, outPt );
591 if ( pal::LabelPosition *nextPart = label->nextPart() )
592 drawLabelMetricsRecursive( nextPart );
593 };
594
595 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
596 {
597 drawLabelMetricsRecursive( label );
598 }
599 }
600
601 // draw the labels
602 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
603 {
604 if ( context.renderingStopped() )
605 break;
606
607 QgsLabelFeature *lf = label->getFeaturePart()->feature();
608 if ( !lf )
609 {
610 continue;
611 }
612
613 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
614 continue;
615
616 context.expressionContext().setFeature( lf->feature() );
617 context.expressionContext().setFields( lf->feature().fields() );
618
619 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
620 if ( lf->symbol() )
621 {
622 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
623 }
624 lf->provider()->drawLabel( context, label );
625 // finished with symbol -- we can't keep it around after this, it may be deleted
626 lf->setSymbol( nullptr );
627 }
628
629 // draw unplaced labels. These are always rendered on top
631 {
632 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
633 {
634 if ( context.renderingStopped() )
635 break;
636 QgsLabelFeature *lf = label->getFeaturePart()->feature();
637 if ( !lf )
638 {
639 continue;
640 }
641
642 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
643 continue;
644
645 context.expressionContext().setFeature( lf->feature() );
646 context.expressionContext().setFields( lf->feature().fields() );
647
648 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
649 if ( lf->symbol() )
650 {
651 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
652 }
653 lf->provider()->drawUnplacedLabel( context, label );
654 // finished with symbol -- we can't keep it around after this, it may be deleted
655 lf->setSymbol( nullptr );
656 }
657 }
658 }
659
660 symbolScopePopper.reset();
661
662 // cleanup
663 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
664 {
665 if ( !layerId.isEmpty() && provider->layerId() != layerId )
666 continue;
667
668 provider->stopRender( context );
669 }
670
671 // Reset composition mode for further drawing operations
672 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
673
674 QgsDebugMsgLevel( u"LABELING draw: %1 ms"_s.arg( t.elapsed() ), 4 );
675}
676
678{
679 mUnlabeled.clear();
680 mLabels.clear();
681 mProblem.reset();
682 mPal.reset();
683}
684
689
690void QgsLabelingEngine::drawLabelCandidateRect( pal::LabelPosition *lp, QgsRenderContext &context, const QgsMapToPixel *xform, QList<QgsLabelCandidate> *candidates )
691{
692 QPainter *painter = context.painter();
693 if ( !painter )
694 return;
695
696 QgsPointXY outPt = xform->transform( lp->getX(), lp->getY() );
697
698 painter->save();
699
700 QgsPointXY outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
701 QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
702 painter->translate( QPointF( outPt.x(), outPt.y() ) );
703 painter->rotate( -lp->getAlpha() * 180 / M_PI );
704
705 if ( lp->conflictsWithObstacle() )
706 {
707 painter->setPen( QColor( 255, 0, 0, 64 ) );
708 }
709 else
710 {
711 painter->setPen( QColor( 0, 0, 0, 64 ) );
712 }
713 painter->drawRect( rect );
714 painter->restore();
715
716 // save the rect
717 rect.moveTo( outPt.x(), outPt.y() );
718 if ( candidates )
719 candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
720
721 // show all parts of the multipart label
722 if ( lp->nextPart() )
723 drawLabelCandidateRect( lp->nextPart(), context, xform, candidates );
724}
725
726void QgsLabelingEngine::drawLabelMetrics( pal::LabelPosition *label, const QgsMapToPixel &xform, QgsRenderContext &context, const QPointF &renderPoint )
727{
728 QPainter *painter = context.painter();
729 if ( !painter )
730 return;
731
732 QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
733 QRectF rect( 0, 0, outPt2.x() - renderPoint.x(), outPt2.y() - renderPoint.y() );
734 painter->save();
735 painter->setRenderHint( QPainter::Antialiasing, false );
736 painter->translate( QPointF( renderPoint.x(), renderPoint.y() ) );
737 painter->rotate( -label->getAlpha() * 180 / M_PI );
738
739 painter->setBrush( Qt::NoBrush );
740 painter->setPen( QColor( 255, 0, 0, 220 ) );
741
742 painter->drawRect( rect );
743
744 painter->setPen( QColor( 0, 0, 0, 60 ) );
745 const QgsMargins &margins = label->getFeaturePart()->feature()->visualMargin();
746 if ( margins.top() > 0 )
747 {
748 const double topMargin = margins.top() / context.mapToPixel().mapUnitsPerPixel();
749 painter->drawLine( QPointF( rect.left(), rect.top() - topMargin ), QPointF( rect.right(), rect.top() - topMargin ) );
750 }
751 if ( margins.bottom() > 0 )
752 {
753 const double bottomMargin = margins.top() / context.mapToPixel().mapUnitsPerPixel();
754 painter->drawLine( QPointF( rect.left(), rect.bottom() + bottomMargin ), QPointF( rect.right(), rect.bottom() + bottomMargin ) );
755 }
756
757 const QRectF outerBounds = label->getFeaturePart()->feature()->outerBounds();
758 if ( !outerBounds.isNull() )
759 {
760 const QRectF mapOuterBounds = QRectF( label->getX() + outerBounds.left(),
761 label->getY() + outerBounds.top(),
762 outerBounds.width(), outerBounds.height() );
763
764 QgsPointXY outerBoundsPt1 = xform.transform( mapOuterBounds.left(), mapOuterBounds.top() );
765 QgsPointXY outerBoundsPt2 = xform.transform( mapOuterBounds.right(), mapOuterBounds.bottom() );
766
767 const QRectF outerBoundsPixel( outerBoundsPt1.x() - renderPoint.x(),
768 outerBoundsPt1.y() - renderPoint.y(),
769 outerBoundsPt2.x() - outerBoundsPt1.x(),
770 outerBoundsPt2.y() - outerBoundsPt1.y() );
771
772 QPen pen( QColor( 255, 0, 255, 140 ) );
773 pen.setCosmetic( true );
774 pen.setWidth( 1 );
775 painter->setPen( pen );
776 painter->drawRect( outerBoundsPixel );
777 }
778
779 if ( QgsTextLabelFeature *textFeature = dynamic_cast< QgsTextLabelFeature * >( label->getFeaturePart()->feature() ) )
780 {
781 const QgsTextDocumentMetrics &metrics = textFeature->documentMetrics();
782 const QgsTextDocument &document = textFeature->document();
783 const int blockCount = document.size();
784
785 double prevBlockBaseline = rect.bottom() - rect.top();
786 const double verticalAlignOffset = -metrics.blockVerticalMargin( document.size() - 1 );
787
788 // draw block baselines
789 for ( int blockIndex = 0; blockIndex < blockCount; ++blockIndex )
790 {
791 const double blockBaseLine = metrics.baselineOffset( blockIndex, Qgis::TextLayoutMode::Labeling );
792
793 const QgsTextBlock &block = document.at( blockIndex );
794 const int fragmentCount = block.size();
795 double left = metrics.blockLeftMargin( blockIndex );
796 for ( int fragmentIndex = 0; fragmentIndex < fragmentCount; ++fragmentIndex )
797 {
798 const double fragmentVerticalOffset = metrics.fragmentVerticalOffset( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
799 const double right = left + metrics.fragmentHorizontalAdvance( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
800
801 if ( fragmentIndex > 0 )
802 {
803 QPen pen( QColor( 0, 0, 255, 220 ) );
804 pen.setStyle( Qt::PenStyle::DashLine );
805
806 painter->setPen( pen );
807
808 painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ),
809 QPointF( rect.left() + left, rect.top() + prevBlockBaseline + verticalAlignOffset ) );
810
811 }
812
813 painter->setPen( QColor( 0, 0, 255, 220 ) );
814 painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ),
815 QPointF( rect.left() + right, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ) );
816 left = right;
817 }
818 prevBlockBaseline = blockBaseLine;
819 }
820 }
821
822 painter->restore();
823}
824
825
826//
827// QgsDefaultLabelingEngine
828//
829
835
837{
838 registerLabels( context );
839 if ( context.renderingStopped() )
840 {
841 cleanup();
842 return; // it has been canceled
843 }
844
845 solve( context );
846 if ( context.renderingStopped() )
847 {
848 cleanup();
849 return;
850 }
851
852 drawLabels( context );
853 cleanup();
854}
855
856
857//
858// QgsStagedRenderLabelingEngine
859//
860
866
868{
869 registerLabels( context );
870 if ( context.renderingStopped() )
871 {
872 cleanup();
873 return; // it has been canceled
874 }
875
876 solve( context );
877 if ( context.renderingStopped() )
878 {
879 cleanup();
880 return;
881 }
882}
883
884
886{
887 drawLabels( context, layerId );
888}
889
894
895
897
899{
900 return mLayer ? mLayer->provider() : nullptr;
901
902}
903
905 : mLayerId( layer ? layer->id() : QString() )
906 , mLayer( layer )
908{
909 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
910 {
911 mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
912 if ( const QgsFeatureRenderer *renderer = vl->renderer() )
913 mLayerReferenceScale = renderer->referenceScale();
914 }
915}
916
921
926
928{
929 const auto subproviders = subProviders();
930 for ( QgsAbstractLabelProvider *subProvider : subproviders )
931 {
932 subProvider->startRender( context );
933 }
934}
935
937{
938 const auto subproviders = subProviders();
939 for ( QgsAbstractLabelProvider *subProvider : subproviders )
940 {
941 subProvider->stopRender( context );
942 }
943}
944
946{
947 return mLayerExpressionContextScope.get();
948}
949
950//
951// QgsLabelingUtils
952//
953
954QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
955{
956 QStringList predefinedOrderString;
957 const auto constPositions = positions;
958 for ( Qgis::LabelPredefinedPointPosition position : constPositions )
959 {
960 switch ( position )
961 {
963 predefinedOrderString << u"TL"_s;
964 break;
966 predefinedOrderString << u"TSL"_s;
967 break;
969 predefinedOrderString << u"T"_s;
970 break;
972 predefinedOrderString << u"TSR"_s;
973 break;
975 predefinedOrderString << u"TR"_s;
976 break;
978 predefinedOrderString << u"L"_s;
979 break;
981 predefinedOrderString << u"R"_s;
982 break;
984 predefinedOrderString << u"BL"_s;
985 break;
987 predefinedOrderString << u"BSL"_s;
988 break;
990 predefinedOrderString << u"B"_s;
991 break;
993 predefinedOrderString << u"BSR"_s;
994 break;
996 predefinedOrderString << u"BR"_s;
997 break;
999 predefinedOrderString << u"O"_s;
1000 break;
1001 }
1002 }
1003 return predefinedOrderString.join( ',' );
1004}
1005
1006QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
1007{
1008 QVector<Qgis::LabelPredefinedPointPosition> result;
1009 const QStringList predefinedOrderList = positionString.split( ',' );
1010 result.reserve( predefinedOrderList.size() );
1011 for ( const QString &position : predefinedOrderList )
1012 {
1013 QString cleaned = position.trimmed().toUpper();
1014 if ( cleaned == "TL"_L1 )
1016 else if ( cleaned == "TSL"_L1 )
1018 else if ( cleaned == "T"_L1 )
1020 else if ( cleaned == "TSR"_L1 )
1022 else if ( cleaned == "TR"_L1 )
1024 else if ( cleaned == "L"_L1 )
1026 else if ( cleaned == "R"_L1 )
1028 else if ( cleaned == "BL"_L1 )
1030 else if ( cleaned == "BSL"_L1 )
1032 else if ( cleaned == "B"_L1 )
1034 else if ( cleaned == "BSR"_L1 )
1036 else if ( cleaned == "BR"_L1 )
1038 else if ( cleaned == "O"_L1 )
1040 }
1041 return result;
1042}
1043
1045{
1046 QStringList parts;
1048 parts << u"OL"_s;
1050 parts << u"AL"_s;
1052 parts << u"BL"_s;
1054 parts << u"LO"_s;
1055 return parts.join( ',' );
1056}
1057
1059{
1061 const QStringList flagList = string.split( ',' );
1062 bool foundLineOrientationFlag = false;
1063 for ( const QString &flag : flagList )
1064 {
1065 QString cleaned = flag.trimmed().toUpper();
1066 if ( cleaned == "OL"_L1 )
1068 else if ( cleaned == "AL"_L1 )
1070 else if ( cleaned == "BL"_L1 )
1072 else if ( cleaned == "LO"_L1 )
1073 foundLineOrientationFlag = true;
1074 }
1075 if ( !foundLineOrientationFlag )
1077 return flags;
1078}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1336
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
Definition qgis.h:1337
@ OnLine
Labels can be placed directly over a line feature.
Definition qgis.h:1334
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1335
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
Definition qgis.h:1348
@ Labeling
Labeling-specific layout mode.
Definition qgis.h:2962
@ DrawCandidates
Whether to draw rectangles of generated candidates (good for debugging).
Definition qgis.h:2905
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
Definition qgis.h:2907
@ DrawLabelMetrics
Whether to render label metric guides (for debugging).
Definition qgis.h:2908
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
Definition qgis.h:2906
@ UseAllLabels
Whether to draw all labels even if there would be collisions.
Definition qgis.h:2900
@ DrawLabelRectOnly
Whether to only draw the label rect and not the actual label text (used for unit tests).
Definition qgis.h:2904
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
Definition qgis.h:2901
@ Millimeters
Millimeters.
Definition qgis.h:5256
@ RecordProfile
Enable run-time profiling while rendering.
Definition qgis.h:2826
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
Definition qgis.h:1260
@ OverPoint
Label directly centered over point.
Definition qgis.h:1273
@ MiddleLeft
Label on left of point.
Definition qgis.h:1266
@ TopRight
Label on top-right of point.
Definition qgis.h:1265
@ MiddleRight
Label on right of point.
Definition qgis.h:1267
@ TopSlightlyRight
Label on top of point, slightly right of center.
Definition qgis.h:1264
@ TopMiddle
Label directly above point.
Definition qgis.h:1263
@ BottomSlightlyLeft
Label below point, slightly left of center.
Definition qgis.h:1269
@ BottomRight
Label on bottom right of point.
Definition qgis.h:1272
@ BottomLeft
Label on bottom-left of point.
Definition qgis.h:1268
@ BottomSlightlyRight
Label below point, slightly right of center.
Definition qgis.h:1271
@ TopLeft
Label on top-left of point.
Definition qgis.h:1261
@ BottomMiddle
Label directly below point.
Definition qgis.h:1270
@ TopSlightlyLeft
Label on top of point, slightly left of center.
Definition qgis.h:1262
An abstract interface class for label providers.
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.
QString mLayerId
Associated layer's ID, if applicable.
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.
QgsWeakMapLayerPointer mLayer
Weak pointer to source layer.
QString providerId() const
Returns provider ID - useful in case there is more than one label provider within a layer (e....
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.
QString mProviderId
Associated provider ID (one layer may have multiple providers, e.g. in rule-based labeling).
Abstract base class for labeling engine rules.
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.
Abstract base class for all 2D vector feature renderers.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:70
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
static std::unique_ptr< 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.
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.
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).
Represents a label candidate.
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 QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
QRectF outerBounds() const
Returns the extreme outer bounds of the label feature, including any surrounding content like borders...
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.
std::unique_ptr< pal::Pal > mPal
const QgsLabelingEngineSettings & engineSettings() const
Gets associated labeling engine settings.
std::unique_ptr< QgsLabelingResults > mResults
Resulting labeling layout.
QgsMapSettings mMapSettings
Associated map settings instance.
bool prepare(QgsRenderContext &context)
Prepares the engine for rendering in the specified context.
void solve(QgsRenderContext &context)
Solves the label problem.
QList< pal::LabelPosition * > mUnlabeled
std::vector< std::unique_ptr< QgsAbstractLabelingEngineRule > > mEngineRules
std::unique_ptr< pal::Problem > mProblem
QString addProvider(QgsAbstractLabelProvider *provider)
Adds a provider of label features.
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
List of labeling engine rules (owned by the labeling engine).
static void drawLabelCandidateRect(pal::LabelPosition *lp, QgsRenderContext &context, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=nullptr)
Draws label candidate rectangles.
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)
QgsAbstractLabelProvider * providerById(const QString &id)
Returns the provider with matching id, where id corresponds to the value returned by the addProvider(...
QgsLabelingEngine()
Construct the labeling engine with default settings.
QHash< QString, QgsAbstractLabelProvider * > mProvidersById
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider's initialization failed. Provider instance is deleted.
static void drawLabelMetrics(pal::LabelPosition *label, const QgsMapToPixel &xform, QgsRenderContext &context, const QPointF &renderPoint)
Draws label metrics.
QList< QgsAbstractLabelProvider * > mProviders
List of providers (the are owned by the labeling engine).
virtual ~QgsLabelingEngine()
Clean up everything (especially the registered providers).
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:83
Contains configuration for rendering maps.
Perform transforms between map coordinates and device coordinates.
void setMapRotation(double degrees, double cx, double cy)
Sets map rotation in degrees (clockwise).
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Defines the four margins of a rectangle.
Definition qgsmargins.h:40
double top() const
Returns the top margin.
Definition qgsmargins.h:80
double bottom() const
Returns the bottom margin.
Definition qgsmargins.h:92
Represents a 2D point.
Definition qgspointxy.h:62
double y
Definition qgspointxy.h:66
double x
Definition qgspointxy.h:65
QPointF toQPointF() const
Converts a point to a QPointF.
Definition qgspointxy.h:167
A rectangle specified with double values.
void grow(double delta)
Grows the rectangle in place by the specified amount.
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.
const QgsMapToPixel & mapToPixel() const
Returns the context's map to pixel transform, which transforms between map coordinates and device coo...
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 block of text consisting of one or more QgsTextFragment objects.
int size() const
Returns the number of fragments in the block.
Contains pre-calculated metrics of a QgsTextDocument.
double fragmentVerticalOffset(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the vertical offset from a text block's baseline which should be applied to the fragment at t...
double baselineOffset(int blockIndex, Qgis::TextLayoutMode mode) const
Returns the offset from the top of the document to the text baseline for the given block index.
double blockLeftMargin(int blockIndex) const
Returns the margin for the left side of the specified block index.
double fragmentHorizontalAdvance(int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode) const
Returns the horizontal advance of the fragment at the specified block and fragment index.
double blockVerticalMargin(int blockIndex) const
Returns the vertical margin for the specified block index.
Represents a document consisting of one or more QgsTextBlock objects.
const QgsTextBlock & at(int index) const
Returns the block at the specified index.
int size() const
Returns the number of blocks in the document.
Adds extra information to QgsLabelFeature for text labels.
Represents a vector layer which manages a vector based dataset.
QgsLabelFeature * feature()
Returns the parent feature.
Definition feature.h:89
LabelPosition is a candidate feature label position.
double getAlpha() const
Returns the angle to rotate text (in radians).
double getHeight() const
double cost() const
Returns the candidate label position's geographical cost.
bool conflictsWithObstacle() const
Returns whether the position is marked as conflicting with an obstacle feature.
double getWidth() const
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
double getX(int i=0) const
Returns the down-left x coordinate.
double getY(int i=0) const
Returns the down-left y coordinate.
LabelPosition * nextPart() const
Returns the next part of this label position (i.e.
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:84
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:87
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
add a new layer
Definition pal.cpp:94
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6900
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63