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