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