QGIS API Documentation 4.3.0-Master (40e84817713)
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 explicit QgsLabelSorter( const QStringList &layerRenderingOrderIds )
60 : mLayerRenderingOrderIds( layerRenderingOrderIds )
61 {}
62
63 bool operator()( pal::LabelPosition *lp1, pal::LabelPosition *lp2 ) const
64 {
65 QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
66 QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
67
68 if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
69 return lf1->zIndex() < lf2->zIndex();
70
71 //equal z-index, so fallback to respecting layer render order
72 int layer1Pos = mLayerRenderingOrderIds.indexOf( lf1->provider()->layerId() );
73 int layer2Pos = mLayerRenderingOrderIds.indexOf( lf2->provider()->layerId() );
74 if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
75 return layer1Pos > layer2Pos; //higher positions are rendered first
76
77 //same layer, so render larger labels first
78 return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
79 }
80
81 private:
82 const QStringList mLayerRenderingOrderIds;
83};
84
86
87//
88// QgsLabelingEngine
89//
90
92{
93 if ( !mapSettings.labelingEngineSettings().flags().testFlag( Qgis::LabelingFlag::DisableSearchTree ) )
94 {
95 mResults = std::make_unique< QgsLabelingResults >();
96 }
97
99}
100
102{
103 qDeleteAll( mProviders );
104 qDeleteAll( mSubProviders );
105}
106
108{
110 mLayerRenderingOrderIds = mMapSettings.layerIds();
111 if ( mResults )
112 mResults->setMapSettings( mapSettings );
113}
114
116{
117 const QList<const QgsAbstractLabelingEngineRule *> rules = mMapSettings.labelingEngineSettings().rules();
118 bool res = true;
119 for ( const QgsAbstractLabelingEngineRule *rule : rules )
120 {
121 if ( !rule->active() || !rule->isAvailable() )
122 continue;
123
124 std::unique_ptr< QgsAbstractLabelingEngineRule > ruleClone( rule->clone() );
125 res = ruleClone->prepare( context ) && res;
126 mEngineRules.emplace_back( std::move( ruleClone ) );
127 }
128 return res;
129}
130
131QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
132{
133 QList< QgsMapLayer * > layers;
134
135 // try to return layers sorted in the desired z order for rendering
136 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
137 std::sort( providersByZ.begin(), providersByZ.end(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
138 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
139 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
140
141 if ( providerA && providerB )
142 {
143 return providerA->settings().zIndex < providerB->settings().zIndex;
144 }
145 return false;
146 } );
147
148 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
149 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
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(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
180 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
181 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
182
183 if ( providerA && providerB )
184 {
185 return providerA->settings().zIndex < providerB->settings().zIndex;
186 }
187 return false;
188 } );
189
190 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
191 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
192 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
193 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
194
195 if ( providerA && providerB )
196 {
197 return providerA->settings().zIndex < providerB->settings().zIndex;
198 }
199 return false;
200 } );
201
202 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
203 {
204 if ( !layers.contains( provider->layerId() ) )
205 layers << provider->layerId();
206 }
207 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
208 {
209 if ( !layers.contains( provider->layerId() ) )
210 layers << provider->layerId();
211 }
212 return layers;
213}
214
216{
217 provider->setEngine( this );
218 mProviders << provider;
219 const QString id = QUuid::createUuid().toString( QUuid::WithoutBraces );
220 mProvidersById.insert( id, provider );
221 return id;
222}
223
225{
226 return mProvidersById.value( id );
227}
228
230{
231 int idx = mProviders.indexOf( provider );
232 if ( idx >= 0 )
233 {
234 mProvidersById.remove( mProvidersById.key( provider ) );
235 delete mProviders.takeAt( idx );
236 }
237}
238
240{
241 QgsAbstractLabelProvider::Flags flags = provider->flags();
242
243 // create the pal layer
244 pal::Layer *l = p.addLayer( provider, provider->name(), provider->placement(), provider->priority(), true, flags.testFlag( QgsAbstractLabelProvider::DrawLabels ) );
245
246 // set whether adjacent lines should be merged
248
249 // set obstacle type
250 l->setObstacleType( provider->obstacleType() );
251
252 // set whether location of centroid must be inside of polygons
254
255 // set how to show upside-down labels
256 l->setUpsidedownLabels( provider->upsidedownLabels() );
257
258 const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
259
260 for ( QgsLabelFeature *feature : features )
261 {
262 try
263 {
264 l->registerFeature( feature );
265 }
266 catch ( std::exception &e )
267 {
268 Q_UNUSED( e )
269 QgsDebugMsgLevel( u"Ignoring feature %1 due PAL exception:"_s.arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
270 continue;
271 }
272 }
273
274 // any sub-providers?
275 const auto subproviders = provider->subProviders();
276 for ( QgsAbstractLabelProvider *subProvider : subproviders )
277 {
278 mSubProviders << subProvider;
279 processProvider( subProvider, context, p );
280 }
281}
282
284{
285 std::unique_ptr< QgsScopedRuntimeProfile > registeringProfile;
287 {
288 registeringProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Registering labels" ), u"rendering"_s );
289 }
290
291 QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
292
293 if ( feedback )
294 feedback->emit labelRegistrationAboutToBegin();
295
296 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
297
298 mPal = std::make_unique< pal::Pal >( settings.flags() );
299
300 mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ) );
301 mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ), 2 ) );
302
303 mPal->setShowPartialLabels( settings.testFlag( Qgis::LabelingFlag::UsePartialCandidates ) );
304 mPal->setPlacementVersion( settings.placementVersion() );
305
306 QList< QgsAbstractLabelingEngineRule * > rules;
307 rules.reserve( static_cast< int >( mEngineRules.size() ) );
308 for ( auto &it : mEngineRules )
309 {
310 rules.append( it.get() );
311 }
312 mPal->setRules( rules );
313
314 // for each provider: get labels and register them in PAL
315 const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
316 int index = 0;
317 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
318 {
319 if ( feedback )
320 {
321 feedback->emit providerRegistrationAboutToBegin( provider );
322 feedback->setProgress( index * step );
323 }
324 index++;
325 std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
326 if ( provider->layerExpressionContextScope() )
327 {
328 layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
329 }
330 processProvider( provider, context, *mPal );
331 if ( feedback )
332 feedback->emit providerRegistrationFinished( provider );
333 }
334 if ( feedback )
335 feedback->emit labelRegistrationFinished();
336}
337
339{
340 Q_ASSERT( mPal.get() );
341
342 // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
343 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
344
345 QPainter *painter = context.painter();
346
347 QgsRectangle r1 = mMapSettings.visibleExtent();
348 r1.grow( mMapSettings.extentBuffer() );
349 QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
350
351 QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
352 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
353
354 // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
355 QgsGeometry mapBoundaryGeom = !mMapSettings.labelBoundaryGeometry().isNull() ? mMapSettings.labelBoundaryGeometry() : QgsGeometry::fromQPolygonF( visiblePoly );
356
357 // label blocking regions work by "chopping away" those regions from the permissible labeling area
358 const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
359 for ( const QgsLabelBlockingRegion &region : blockingRegions )
360 {
361 mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry, QgsGeometryParameters(), context.feedback() );
362 }
363
364 if ( settings.flags() & Qgis::LabelingFlag::DrawCandidates )
365 {
366 // draw map boundary
367 QgsFeature f;
368 f.setGeometry( mapBoundaryGeom );
369 QVariantMap properties;
370 properties.insert( u"style"_s, u"no"_s );
371 properties.insert( u"style_border"_s, u"solid"_s );
372 properties.insert( u"color_border"_s, u"#0000ff"_s );
373 properties.insert( u"width_border"_s, u"0.3"_s );
374 properties.insert( u"joinstyle"_s, u"miter"_s );
375 std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
376 boundarySymbol->startRender( context );
377 boundarySymbol->renderFeature( f, context );
378 boundarySymbol->stopRender( context );
379 }
380
381 if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
382 {
383 //PAL features are prerotated, so extent also needs to be unrotated
384 extentGeom.rotate( -mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
385 // yes - this is rotated in the opposite direction... phew, this is confusing!
386 mapBoundaryGeom.rotate( mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
387 }
388
389 QgsRectangle extent = extentGeom.boundingBox();
390
391 mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
392
393 QElapsedTimer t;
394 t.start();
395
396 // do the labeling itself
397 try
398 {
399 mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
400 }
401 catch ( std::exception &e )
402 {
403 Q_UNUSED( e )
404 QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
405 return;
406 }
407
408 if ( context.renderingStopped() )
409 {
410 return; // it has been canceled
411 }
412
413#if 1 // XXX strk
414 // features are pre-rotated but not scaled/translated,
415 // so we only disable rotation here. Ideally, they'd be
416 // also pre-scaled/translated, as suggested here:
417 // https://github.com/qgis/QGIS/issues/20071
418 QgsMapToPixel xform = mMapSettings.mapToPixel();
419 xform.setMapRotation( 0, 0, 0 );
420#else
421 const QgsMapToPixel &xform = mMapSettings->mapToPixel();
422#endif
423
424 // draw rectangles with all candidates
425 // this is done before actual solution of the problem
426 // before number of candidates gets reduced
427 // TODO mCandidates.clear();
429 {
430 painter->setBrush( Qt::NoBrush );
431 for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
432 {
433 for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
434 {
435 pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
436
437 drawLabelCandidateRect( lp, context, &xform );
438 }
439 }
440 }
441
442 // find the solution
443 mLabels
445
446 // sort labels
447 std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mLayerRenderingOrderIds ) );
448
449 QgsDebugMsgLevel( u"LABELING work: %1 ms ... labels# %2"_s.arg( t.elapsed() ).arg( mLabels.size() ), 4 );
450}
451
452void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
453{
454 QElapsedTimer t;
455 t.start();
456
457 std::unique_ptr< QgsScopedRuntimeProfile > drawingProfile;
459 {
460 drawingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering labels" ), u"rendering"_s );
461 }
462
463 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
464
466 QPainter *painter = context.painter();
467
468 // prepare for rendering
469 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
470 {
471 if ( !layerId.isEmpty() && provider->layerId() != layerId )
472 continue;
473
474 // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
475 // only contains generic scopes
477 popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
478
479 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
480 provider->startRender( context );
481 }
482
484 auto symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
485
486 // draw label backgrounds
487 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
488 {
489 if ( context.renderingStopped() )
490 break;
491
492 QgsLabelFeature *lf = label->getFeaturePart()->feature();
493 if ( !lf )
494 {
495 continue;
496 }
497
498 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
499 continue;
500
501 context.expressionContext().setFeature( lf->feature() );
502 context.expressionContext().setFields( lf->feature().fields() );
503
504 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
505
506 if ( lf->symbol() )
507 {
508 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
509 }
510 lf->provider()->drawLabelBackground( context, label );
511 }
512
514 {
515 // features are pre-rotated but not scaled/translated,
516 // so we only disable rotation here. Ideally, they'd be
517 // also pre-scaled/translated, as suggested here:
518 // https://github.com/qgis/QGIS/issues/20071
519 QgsMapToPixel xform = context.mapToPixel();
520 xform.setMapRotation( 0, 0, 0 );
521
522 std::function<void( pal::LabelPosition * )> drawLabelRect;
523 drawLabelRect = [&xform, painter, &drawLabelRect]( pal::LabelPosition *label ) {
524 QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
525
526 QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
527 QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
528 painter->save();
529 painter->setRenderHint( QPainter::Antialiasing, false );
530 painter->translate( QPointF( outPt.x(), outPt.y() ) );
531 painter->rotate( -label->getAlpha() * 180 / M_PI );
532
533 if ( label->conflictsWithObstacle() )
534 {
535 painter->setBrush( QColor( 255, 0, 0, 100 ) );
536 painter->setPen( QColor( 255, 0, 0, 150 ) );
537 }
538 else
539 {
540 painter->setBrush( QColor( 0, 255, 0, 100 ) );
541 painter->setPen( QColor( 0, 255, 0, 150 ) );
542 }
543
544 painter->drawRect( rect );
545 painter->restore();
546
547 if ( pal::LabelPosition *nextPart = label->nextPart() )
548 drawLabelRect( nextPart );
549 };
550
551 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
552 {
553 drawLabelRect( label );
554 }
555
557 {
558 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
559 {
560 drawLabelRect( label );
561 }
562 }
563 }
564 else
565 {
567 {
568 // features are pre-rotated but not scaled/translated,
569 // so we only disable rotation here. Ideally, they'd be
570 // also pre-scaled/translated, as suggested here:
571 // https://github.com/qgis/QGIS/issues/20071
572 QgsMapToPixel xform = context.mapToPixel();
573 xform.setMapRotation( 0, 0, 0 );
574
575 std::function<void( pal::LabelPosition * )> drawLabelMetricsRecursive;
576 drawLabelMetricsRecursive = [&xform, &context, &drawLabelMetricsRecursive]( pal::LabelPosition *label ) {
577 QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
578 QgsLabelingEngine::drawLabelMetrics( label, xform, context, outPt );
579 if ( pal::LabelPosition *nextPart = label->nextPart() )
580 drawLabelMetricsRecursive( nextPart );
581 };
582
583 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
584 {
585 drawLabelMetricsRecursive( label );
586 }
587 }
588
589 // draw the labels
590 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
591 {
592 if ( context.renderingStopped() )
593 break;
594
595 QgsLabelFeature *lf = label->getFeaturePart()->feature();
596 if ( !lf )
597 {
598 continue;
599 }
600
601 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
602 continue;
603
604 context.expressionContext().setFeature( lf->feature() );
605 context.expressionContext().setFields( lf->feature().fields() );
606
607 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
608 if ( lf->symbol() )
609 {
610 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
611 }
612 lf->provider()->drawLabel( context, label );
613 // finished with symbol -- we can't keep it around after this, it may be deleted
614 lf->setSymbol( nullptr );
615 }
616
617 // draw unplaced labels. These are always rendered on top
619 {
620 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
621 {
622 if ( context.renderingStopped() )
623 break;
624 QgsLabelFeature *lf = label->getFeaturePart()->feature();
625 if ( !lf )
626 {
627 continue;
628 }
629
630 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
631 continue;
632
633 context.expressionContext().setFeature( lf->feature() );
634 context.expressionContext().setFields( lf->feature().fields() );
635
636 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
637 if ( lf->symbol() )
638 {
639 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
640 }
641 lf->provider()->drawUnplacedLabel( context, label );
642 // finished with symbol -- we can't keep it around after this, it may be deleted
643 lf->setSymbol( nullptr );
644 }
645 }
646 }
647
648 symbolScopePopper.reset();
649
650 // cleanup
651 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
652 {
653 if ( !layerId.isEmpty() && provider->layerId() != layerId )
654 continue;
655
656 provider->stopRender( context );
657 }
658
659 // Reset composition mode for further drawing operations
660 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
661
662 QgsDebugMsgLevel( u"LABELING draw: %1 ms"_s.arg( t.elapsed() ), 4 );
663}
664
666{
667 mUnlabeled.clear();
668 mLabels.clear();
669 mProblem.reset();
670 mPal.reset();
671}
672
677
678void QgsLabelingEngine::drawLabelCandidateRect( pal::LabelPosition *lp, QgsRenderContext &context, const QgsMapToPixel *xform, QList<QgsLabelCandidate> *candidates )
679{
680 QPainter *painter = context.painter();
681 if ( !painter )
682 return;
683
684 QgsPointXY outPt = xform->transform( lp->getX(), lp->getY() );
685
686 painter->save();
687
688 QgsPointXY outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
689 QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
690 painter->translate( QPointF( outPt.x(), outPt.y() ) );
691 painter->rotate( -lp->getAlpha() * 180 / M_PI );
692
693 if ( lp->conflictsWithObstacle() )
694 {
695 painter->setPen( QColor( 255, 0, 0, 64 ) );
696 }
697 else
698 {
699 painter->setPen( QColor( 0, 0, 0, 64 ) );
700 }
701 painter->drawRect( rect );
702 painter->restore();
703
704 // save the rect
705 rect.moveTo( outPt.x(), outPt.y() );
706 if ( candidates )
707 candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
708
709 // show all parts of the multipart label
710 if ( lp->nextPart() )
711 drawLabelCandidateRect( lp->nextPart(), context, xform, candidates );
712}
713
714void QgsLabelingEngine::drawLabelMetrics( pal::LabelPosition *label, const QgsMapToPixel &xform, QgsRenderContext &context, const QPointF &renderPoint )
715{
716 QPainter *painter = context.painter();
717 if ( !painter )
718 return;
719
720 QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
721 QRectF rect( 0, 0, outPt2.x() - renderPoint.x(), outPt2.y() - renderPoint.y() );
722 painter->save();
723 painter->setRenderHint( QPainter::Antialiasing, false );
724 painter->translate( QPointF( renderPoint.x(), renderPoint.y() ) );
725 painter->rotate( -label->getAlpha() * 180 / M_PI );
726
727 painter->setBrush( Qt::NoBrush );
728 painter->setPen( QColor( 255, 0, 0, 220 ) );
729
730 painter->drawRect( rect );
731
732 painter->setPen( QColor( 0, 0, 0, 60 ) );
733 const QgsMargins &margins = label->getFeaturePart()->feature()->visualMargin();
734 if ( margins.top() > 0 )
735 {
736 const double topMargin = margins.top() / context.mapToPixel().mapUnitsPerPixel();
737 painter->drawLine( QPointF( rect.left(), rect.top() - topMargin ), QPointF( rect.right(), rect.top() - topMargin ) );
738 }
739 if ( margins.bottom() > 0 )
740 {
741 const double bottomMargin = margins.top() / context.mapToPixel().mapUnitsPerPixel();
742 painter->drawLine( QPointF( rect.left(), rect.bottom() + bottomMargin ), QPointF( rect.right(), rect.bottom() + bottomMargin ) );
743 }
744
745 const QRectF outerBounds = label->getFeaturePart()->feature()->outerBounds();
746 if ( !outerBounds.isNull() )
747 {
748 const QRectF mapOuterBounds = QRectF( label->getX() + outerBounds.left(), label->getY() + outerBounds.top(), outerBounds.width(), outerBounds.height() );
749
750 QgsPointXY outerBoundsPt1 = xform.transform( mapOuterBounds.left(), mapOuterBounds.top() );
751 QgsPointXY outerBoundsPt2 = xform.transform( mapOuterBounds.right(), mapOuterBounds.bottom() );
752
753 const QRectF outerBoundsPixel( outerBoundsPt1.x() - renderPoint.x(), outerBoundsPt1.y() - renderPoint.y(), outerBoundsPt2.x() - outerBoundsPt1.x(), outerBoundsPt2.y() - outerBoundsPt1.y() );
754
755 QPen pen( QColor( 255, 0, 255, 140 ) );
756 pen.setCosmetic( true );
757 pen.setWidth( 1 );
758 painter->setPen( pen );
759 painter->drawRect( outerBoundsPixel );
760 }
761
762 if ( QgsTextLabelFeature *textFeature = dynamic_cast< QgsTextLabelFeature * >( label->getFeaturePart()->feature() ) )
763 {
764 const QgsTextDocumentMetrics &metrics = textFeature->documentMetrics();
765 const QgsTextDocument &document = textFeature->document();
766 const int blockCount = document.size();
767
768 double prevBlockBaseline = rect.bottom() - rect.top();
769 const double verticalAlignOffset = -metrics.blockVerticalMargin( document.size() - 1 );
770
771 // draw block baselines
772 for ( int blockIndex = 0; blockIndex < blockCount; ++blockIndex )
773 {
774 const double blockBaseLine = metrics.baselineOffset( blockIndex, Qgis::TextLayoutMode::Labeling );
775
776 const QgsTextBlock &block = document.at( blockIndex );
777 const int fragmentCount = block.size();
778 double left = metrics.blockLeftMargin( blockIndex );
779 for ( int fragmentIndex = 0; fragmentIndex < fragmentCount; ++fragmentIndex )
780 {
781 const double fragmentVerticalOffset = metrics.fragmentVerticalOffset( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
782 const double right = left + metrics.fragmentHorizontalAdvance( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
783
784 if ( fragmentIndex > 0 )
785 {
786 QPen pen( QColor( 0, 0, 255, 220 ) );
787 pen.setStyle( Qt::PenStyle::DashLine );
788
789 painter->setPen( pen );
790
791 painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ), QPointF( rect.left() + left, rect.top() + prevBlockBaseline + verticalAlignOffset ) );
792 }
793
794 painter->setPen( QColor( 0, 0, 255, 220 ) );
795 painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ), QPointF( rect.left() + right, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ) );
796 left = right;
797 }
798 prevBlockBaseline = blockBaseLine;
799 }
800 }
801
802 painter->restore();
803}
804
805
806//
807// QgsDefaultLabelingEngine
808//
809
813
815{
816 registerLabels( context );
817 if ( context.renderingStopped() )
818 {
819 cleanup();
820 return; // it has been canceled
821 }
822
823 solve( context );
824 if ( context.renderingStopped() )
825 {
826 cleanup();
827 return;
828 }
829
830 drawLabels( context );
831 cleanup();
832}
833
834
835//
836// QgsStagedRenderLabelingEngine
837//
838
842
844{
845 registerLabels( context );
846 if ( context.renderingStopped() )
847 {
848 cleanup();
849 return; // it has been canceled
850 }
851
852 solve( context );
853 if ( context.renderingStopped() )
854 {
855 cleanup();
856 return;
857 }
858}
859
860
862{
863 drawLabels( context, layerId );
864}
865
870
871
873
875{
876 return mLayer ? mLayer->provider() : nullptr;
877}
878
880 : mLayerId( layer ? layer->id() : QString() )
881 , mLayer( layer )
883{
884 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
885 {
886 mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
887 if ( const QgsFeatureRenderer *renderer = vl->renderer() )
888 mLayerReferenceScale = renderer->referenceScale();
889 }
890}
891
894
897
899{
900 const auto subproviders = subProviders();
901 for ( QgsAbstractLabelProvider *subProvider : subproviders )
902 {
903 subProvider->startRender( context );
904 }
905}
906
908{
909 const auto subproviders = subProviders();
910 for ( QgsAbstractLabelProvider *subProvider : subproviders )
911 {
912 subProvider->stopRender( context );
913 }
914}
915
917{
918 return mLayerExpressionContextScope.get();
919}
920
921//
922// QgsLabelingUtils
923//
924
925QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
926{
927 QStringList predefinedOrderString;
928 const auto constPositions = positions;
929 for ( Qgis::LabelPredefinedPointPosition position : constPositions )
930 {
931 switch ( position )
932 {
934 predefinedOrderString << u"TL"_s;
935 break;
937 predefinedOrderString << u"TSL"_s;
938 break;
940 predefinedOrderString << u"T"_s;
941 break;
943 predefinedOrderString << u"TSR"_s;
944 break;
946 predefinedOrderString << u"TR"_s;
947 break;
949 predefinedOrderString << u"L"_s;
950 break;
952 predefinedOrderString << u"R"_s;
953 break;
955 predefinedOrderString << u"BL"_s;
956 break;
958 predefinedOrderString << u"BSL"_s;
959 break;
961 predefinedOrderString << u"B"_s;
962 break;
964 predefinedOrderString << u"BSR"_s;
965 break;
967 predefinedOrderString << u"BR"_s;
968 break;
970 predefinedOrderString << u"O"_s;
971 break;
972 }
973 }
974 return predefinedOrderString.join( ',' );
975}
976
977QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
978{
979 QVector<Qgis::LabelPredefinedPointPosition> result;
980 const QStringList predefinedOrderList = positionString.split( ',' );
981 result.reserve( predefinedOrderList.size() );
982 for ( const QString &position : predefinedOrderList )
983 {
984 QString cleaned = position.trimmed().toUpper();
985 if ( cleaned == "TL"_L1 )
987 else if ( cleaned == "TSL"_L1 )
989 else if ( cleaned == "T"_L1 )
991 else if ( cleaned == "TSR"_L1 )
993 else if ( cleaned == "TR"_L1 )
995 else if ( cleaned == "L"_L1 )
997 else if ( cleaned == "R"_L1 )
999 else if ( cleaned == "BL"_L1 )
1001 else if ( cleaned == "BSL"_L1 )
1003 else if ( cleaned == "B"_L1 )
1005 else if ( cleaned == "BSR"_L1 )
1007 else if ( cleaned == "BR"_L1 )
1009 else if ( cleaned == "O"_L1 )
1011 }
1012 return result;
1013}
1014
1016{
1017 QStringList parts;
1019 parts << u"OL"_s;
1021 parts << u"AL"_s;
1023 parts << u"BL"_s;
1025 parts << u"LO"_s;
1026 return parts.join( ',' );
1027}
1028
1030{
1032 const QStringList flagList = string.split( ',' );
1033 bool foundLineOrientationFlag = false;
1034 for ( const QString &flag : flagList )
1035 {
1036 QString cleaned = flag.trimmed().toUpper();
1037 if ( cleaned == "OL"_L1 )
1039 else if ( cleaned == "AL"_L1 )
1041 else if ( cleaned == "BL"_L1 )
1043 else if ( cleaned == "LO"_L1 )
1044 foundLineOrientationFlag = true;
1045 }
1046 if ( !foundLineOrientationFlag )
1048 return flags;
1049}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1398
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
Definition qgis.h:1399
@ OnLine
Labels can be placed directly over a line feature.
Definition qgis.h:1396
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1397
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
Definition qgis.h:1410
@ Labeling
Labeling-specific layout mode.
Definition qgis.h:3107
@ DrawCandidates
Whether to draw rectangles of generated candidates (good for debugging).
Definition qgis.h:3046
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
Definition qgis.h:3048
@ DrawLabelMetrics
Whether to render label metric guides (for debugging).
Definition qgis.h:3049
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
Definition qgis.h:3047
@ DisableSearchTree
Disable the creation of the label search tree.
Definition qgis.h:3053
@ UseAllLabels
Whether to draw all labels even if there would be collisions.
Definition qgis.h:3041
@ DrawLabelRectOnly
Whether to only draw the label rect and not the actual label text (used for unit tests).
Definition qgis.h:3045
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
Definition qgis.h:3042
@ Millimeters
Millimeters.
Definition qgis.h:5656
@ RecordProfile
Enable run-time profiling while rendering.
Definition qgis.h:2963
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
Definition qgis.h:1322
@ OverPoint
Label directly centered over point.
Definition qgis.h:1335
@ MiddleLeft
Label on left of point.
Definition qgis.h:1328
@ TopRight
Label on top-right of point.
Definition qgis.h:1327
@ MiddleRight
Label on right of point.
Definition qgis.h:1329
@ TopSlightlyRight
Label on top of point, slightly right of center.
Definition qgis.h:1326
@ TopMiddle
Label directly above point.
Definition qgis.h:1325
@ BottomSlightlyLeft
Label below point, slightly left of center.
Definition qgis.h:1331
@ BottomRight
Label on bottom right of point.
Definition qgis.h:1334
@ BottomLeft
Label on bottom-left of point.
Definition qgis.h:1330
@ BottomSlightlyRight
Label below point, slightly right of center.
Definition qgis.h:1333
@ TopLeft
Label on top-left of point.
Definition qgis.h:1323
@ BottomMiddle
Label directly below point.
Definition qgis.h:1332
@ TopSlightlyLeft
Label on top of point, slightly left of center.
Definition qgis.h:1324
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(const QgsMapSettings &mapSettings)
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:65
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:65
static std::unique_ptr< QgsFillSymbol > createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Encapsulates parameters under which a geometry operation is performed.
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
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.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points making up this geometry that do not make up other.
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.
QgsLabelingEngine(const QgsMapSettings &mapSettings)
Construct the labeling engine with default settings.
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(...
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:76
double bottom() const
Returns the bottom margin.
Definition qgsmargins.h:88
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:168
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(const QgsMapSettings &mapSettings)
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:87
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:262
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:227
void setMergeConnectedLines(bool merge)
Sets whether connected lines should be merged before labeling.
Definition layer.h:249
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:277
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:95
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7462
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80