QGIS API Documentation 4.1.0-Master (60fea48833c)
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
94
96{
97 qDeleteAll( mProviders );
98 qDeleteAll( mSubProviders );
99}
100
102{
104 mLayerRenderingOrderIds = mMapSettings.layerIds();
105 if ( mResults )
106 mResults->setMapSettings( mapSettings );
107}
108
110{
111 const QList<const QgsAbstractLabelingEngineRule *> rules = mMapSettings.labelingEngineSettings().rules();
112 bool res = true;
113 for ( const QgsAbstractLabelingEngineRule *rule : rules )
114 {
115 if ( !rule->active() || !rule->isAvailable() )
116 continue;
117
118 std::unique_ptr< QgsAbstractLabelingEngineRule > ruleClone( rule->clone() );
119 res = ruleClone->prepare( context ) && res;
120 mEngineRules.emplace_back( std::move( ruleClone ) );
121 }
122 return res;
123}
124
125QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
126{
127 QList< QgsMapLayer * > layers;
128
129 // try to return layers sorted in the desired z order for rendering
130 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
131 std::sort( providersByZ.begin(), providersByZ.end(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
132 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
133 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
134
135 if ( providerA && providerB )
136 {
137 return providerA->settings().zIndex < providerB->settings().zIndex;
138 }
139 return false;
140 } );
141
142 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
143 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
144 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
145 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
146
147 if ( providerA && providerB )
148 {
149 return providerA->settings().zIndex < providerB->settings().zIndex;
150 }
151 return false;
152 } );
153
154 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
155 {
156 if ( provider->layer() && !layers.contains( provider->layer() ) )
157 layers << provider->layer();
158 }
159 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
160 {
161 if ( provider->layer() && !layers.contains( provider->layer() ) )
162 layers << provider->layer();
163 }
164 return layers;
165}
166
168{
169 QStringList layers;
170
171 // try to return layers sorted in the desired z order for rendering
172 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
173 std::sort( providersByZ.begin(), providersByZ.end(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
174 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
175 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
176
177 if ( providerA && providerB )
178 {
179 return providerA->settings().zIndex < providerB->settings().zIndex;
180 }
181 return false;
182 } );
183
184 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
185 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(), []( const QgsAbstractLabelProvider *a, const QgsAbstractLabelProvider *b ) -> bool {
186 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
187 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
188
189 if ( providerA && providerB )
190 {
191 return providerA->settings().zIndex < providerB->settings().zIndex;
192 }
193 return false;
194 } );
195
196 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
197 {
198 if ( !layers.contains( provider->layerId() ) )
199 layers << provider->layerId();
200 }
201 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
202 {
203 if ( !layers.contains( provider->layerId() ) )
204 layers << provider->layerId();
205 }
206 return layers;
207}
208
210{
211 provider->setEngine( this );
212 mProviders << provider;
213 const QString id = QUuid::createUuid().toString( QUuid::WithoutBraces );
214 mProvidersById.insert( id, provider );
215 return id;
216}
217
219{
220 return mProvidersById.value( id );
221}
222
224{
225 int idx = mProviders.indexOf( provider );
226 if ( idx >= 0 )
227 {
228 mProvidersById.remove( mProvidersById.key( provider ) );
229 delete mProviders.takeAt( idx );
230 }
231}
232
234{
235 QgsAbstractLabelProvider::Flags flags = provider->flags();
236
237 // create the pal layer
238 pal::Layer *l = p.addLayer( provider, provider->name(), provider->placement(), provider->priority(), true, flags.testFlag( QgsAbstractLabelProvider::DrawLabels ) );
239
240 // set whether adjacent lines should be merged
242
243 // set obstacle type
244 l->setObstacleType( provider->obstacleType() );
245
246 // set whether location of centroid must be inside of polygons
248
249 // set how to show upside-down labels
250 l->setUpsidedownLabels( provider->upsidedownLabels() );
251
252 const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
253
254 for ( QgsLabelFeature *feature : features )
255 {
256 try
257 {
258 l->registerFeature( feature );
259 }
260 catch ( std::exception &e )
261 {
262 Q_UNUSED( e )
263 QgsDebugMsgLevel( u"Ignoring feature %1 due PAL exception:"_s.arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
264 continue;
265 }
266 }
267
268 // any sub-providers?
269 const auto subproviders = provider->subProviders();
270 for ( QgsAbstractLabelProvider *subProvider : subproviders )
271 {
272 mSubProviders << subProvider;
273 processProvider( subProvider, context, p );
274 }
275}
276
278{
279 std::unique_ptr< QgsScopedRuntimeProfile > registeringProfile;
281 {
282 registeringProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Registering labels" ), u"rendering"_s );
283 }
284
285 QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
286
287 if ( feedback )
288 feedback->emit labelRegistrationAboutToBegin();
289
290 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
291
292 mPal = std::make_unique< pal::Pal >();
293
294 mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ) );
295 mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ), 2 ) );
296
297 mPal->setShowPartialLabels( settings.testFlag( Qgis::LabelingFlag::UsePartialCandidates ) );
298 mPal->setPlacementVersion( settings.placementVersion() );
299
300 QList< QgsAbstractLabelingEngineRule * > rules;
301 rules.reserve( static_cast< int >( mEngineRules.size() ) );
302 for ( auto &it : mEngineRules )
303 {
304 rules.append( it.get() );
305 }
306 mPal->setRules( rules );
307
308 // for each provider: get labels and register them in PAL
309 const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
310 int index = 0;
311 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
312 {
313 if ( feedback )
314 {
315 feedback->emit providerRegistrationAboutToBegin( provider );
316 feedback->setProgress( index * step );
317 }
318 index++;
319 std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
320 if ( provider->layerExpressionContextScope() )
321 {
322 layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
323 }
324 processProvider( provider, context, *mPal );
325 if ( feedback )
326 feedback->emit providerRegistrationFinished( provider );
327 }
328 if ( feedback )
329 feedback->emit labelRegistrationFinished();
330}
331
333{
334 Q_ASSERT( mPal.get() );
335
336 // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
337 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
338
339 QPainter *painter = context.painter();
340
341 QgsRectangle r1 = mMapSettings.visibleExtent();
342 r1.grow( mMapSettings.extentBuffer() );
343 QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
344
345 QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
346 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
347
348 // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
349 QgsGeometry mapBoundaryGeom = !mMapSettings.labelBoundaryGeometry().isNull() ? mMapSettings.labelBoundaryGeometry() : QgsGeometry::fromQPolygonF( visiblePoly );
350
351 // label blocking regions work by "chopping away" those regions from the permissible labeling area
352 const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
353 for ( const QgsLabelBlockingRegion &region : blockingRegions )
354 {
355 mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
356 }
357
358 if ( settings.flags() & Qgis::LabelingFlag::DrawCandidates )
359 {
360 // draw map boundary
361 QgsFeature f;
362 f.setGeometry( mapBoundaryGeom );
363 QVariantMap properties;
364 properties.insert( u"style"_s, u"no"_s );
365 properties.insert( u"style_border"_s, u"solid"_s );
366 properties.insert( u"color_border"_s, u"#0000ff"_s );
367 properties.insert( u"width_border"_s, u"0.3"_s );
368 properties.insert( u"joinstyle"_s, u"miter"_s );
369 std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
370 boundarySymbol->startRender( context );
371 boundarySymbol->renderFeature( f, context );
372 boundarySymbol->stopRender( context );
373 }
374
375 if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
376 {
377 //PAL features are prerotated, so extent also needs to be unrotated
378 extentGeom.rotate( -mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
379 // yes - this is rotated in the opposite direction... phew, this is confusing!
380 mapBoundaryGeom.rotate( mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
381 }
382
383 QgsRectangle extent = extentGeom.boundingBox();
384
385 mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
386
387 QElapsedTimer t;
388 t.start();
389
390 // do the labeling itself
391 try
392 {
393 mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
394 }
395 catch ( std::exception &e )
396 {
397 Q_UNUSED( e )
398 QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
399 return;
400 }
401
402 if ( context.renderingStopped() )
403 {
404 return; // it has been canceled
405 }
406
407#if 1 // XXX strk
408 // features are pre-rotated but not scaled/translated,
409 // so we only disable rotation here. Ideally, they'd be
410 // also pre-scaled/translated, as suggested here:
411 // https://github.com/qgis/QGIS/issues/20071
412 QgsMapToPixel xform = mMapSettings.mapToPixel();
413 xform.setMapRotation( 0, 0, 0 );
414#else
415 const QgsMapToPixel &xform = mMapSettings->mapToPixel();
416#endif
417
418 // draw rectangles with all candidates
419 // this is done before actual solution of the problem
420 // before number of candidates gets reduced
421 // TODO mCandidates.clear();
423 {
424 painter->setBrush( Qt::NoBrush );
425 for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
426 {
427 for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
428 {
429 pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
430
431 drawLabelCandidateRect( lp, context, &xform );
432 }
433 }
434 }
435
436 // find the solution
437 mLabels
439
440 // sort labels
441 std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mLayerRenderingOrderIds ) );
442
443 QgsDebugMsgLevel( u"LABELING work: %1 ms ... labels# %2"_s.arg( t.elapsed() ).arg( mLabels.size() ), 4 );
444}
445
446void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
447{
448 QElapsedTimer t;
449 t.start();
450
451 std::unique_ptr< QgsScopedRuntimeProfile > drawingProfile;
453 {
454 drawingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering labels" ), u"rendering"_s );
455 }
456
457 const QgsLabelingEngineSettings &settings = mMapSettings.labelingEngineSettings();
458
460 QPainter *painter = context.painter();
461
462 // prepare for rendering
463 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
464 {
465 if ( !layerId.isEmpty() && provider->layerId() != layerId )
466 continue;
467
468 // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
469 // only contains generic scopes
471 popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
472
473 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
474 provider->startRender( context );
475 }
476
478 auto symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
479
480 // draw label backgrounds
481 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
482 {
483 if ( context.renderingStopped() )
484 break;
485
486 QgsLabelFeature *lf = label->getFeaturePart()->feature();
487 if ( !lf )
488 {
489 continue;
490 }
491
492 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
493 continue;
494
495 context.expressionContext().setFeature( lf->feature() );
496 context.expressionContext().setFields( lf->feature().fields() );
497
498 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
499
500 if ( lf->symbol() )
501 {
502 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
503 }
504 lf->provider()->drawLabelBackground( context, label );
505 }
506
508 {
509 // features are pre-rotated but not scaled/translated,
510 // so we only disable rotation here. Ideally, they'd be
511 // also pre-scaled/translated, as suggested here:
512 // https://github.com/qgis/QGIS/issues/20071
513 QgsMapToPixel xform = context.mapToPixel();
514 xform.setMapRotation( 0, 0, 0 );
515
516 std::function<void( pal::LabelPosition * )> drawLabelRect;
517 drawLabelRect = [&xform, painter, &drawLabelRect]( pal::LabelPosition *label ) {
518 QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
519
520 QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
521 QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
522 painter->save();
523 painter->setRenderHint( QPainter::Antialiasing, false );
524 painter->translate( QPointF( outPt.x(), outPt.y() ) );
525 painter->rotate( -label->getAlpha() * 180 / M_PI );
526
527 if ( label->conflictsWithObstacle() )
528 {
529 painter->setBrush( QColor( 255, 0, 0, 100 ) );
530 painter->setPen( QColor( 255, 0, 0, 150 ) );
531 }
532 else
533 {
534 painter->setBrush( QColor( 0, 255, 0, 100 ) );
535 painter->setPen( QColor( 0, 255, 0, 150 ) );
536 }
537
538 painter->drawRect( rect );
539 painter->restore();
540
541 if ( pal::LabelPosition *nextPart = label->nextPart() )
542 drawLabelRect( nextPart );
543 };
544
545 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
546 {
547 drawLabelRect( label );
548 }
549
551 {
552 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
553 {
554 drawLabelRect( label );
555 }
556 }
557 }
558 else
559 {
561 {
562 // features are pre-rotated but not scaled/translated,
563 // so we only disable rotation here. Ideally, they'd be
564 // also pre-scaled/translated, as suggested here:
565 // https://github.com/qgis/QGIS/issues/20071
566 QgsMapToPixel xform = context.mapToPixel();
567 xform.setMapRotation( 0, 0, 0 );
568
569 std::function<void( pal::LabelPosition * )> drawLabelMetricsRecursive;
570 drawLabelMetricsRecursive = [&xform, &context, &drawLabelMetricsRecursive]( pal::LabelPosition *label ) {
571 QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF();
572 QgsLabelingEngine::drawLabelMetrics( label, xform, context, outPt );
573 if ( pal::LabelPosition *nextPart = label->nextPart() )
574 drawLabelMetricsRecursive( nextPart );
575 };
576
577 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
578 {
579 drawLabelMetricsRecursive( label );
580 }
581 }
582
583 // draw the labels
584 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
585 {
586 if ( context.renderingStopped() )
587 break;
588
589 QgsLabelFeature *lf = label->getFeaturePart()->feature();
590 if ( !lf )
591 {
592 continue;
593 }
594
595 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
596 continue;
597
598 context.expressionContext().setFeature( lf->feature() );
599 context.expressionContext().setFields( lf->feature().fields() );
600
601 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
602 if ( lf->symbol() )
603 {
604 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
605 }
606 lf->provider()->drawLabel( context, label );
607 // finished with symbol -- we can't keep it around after this, it may be deleted
608 lf->setSymbol( nullptr );
609 }
610
611 // draw unplaced labels. These are always rendered on top
613 {
614 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
615 {
616 if ( context.renderingStopped() )
617 break;
618 QgsLabelFeature *lf = label->getFeaturePart()->feature();
619 if ( !lf )
620 {
621 continue;
622 }
623
624 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
625 continue;
626
627 context.expressionContext().setFeature( lf->feature() );
628 context.expressionContext().setFields( lf->feature().fields() );
629
630 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
631 if ( lf->symbol() )
632 {
633 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
634 }
635 lf->provider()->drawUnplacedLabel( context, label );
636 // finished with symbol -- we can't keep it around after this, it may be deleted
637 lf->setSymbol( nullptr );
638 }
639 }
640 }
641
642 symbolScopePopper.reset();
643
644 // cleanup
645 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
646 {
647 if ( !layerId.isEmpty() && provider->layerId() != layerId )
648 continue;
649
650 provider->stopRender( context );
651 }
652
653 // Reset composition mode for further drawing operations
654 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
655
656 QgsDebugMsgLevel( u"LABELING draw: %1 ms"_s.arg( t.elapsed() ), 4 );
657}
658
660{
661 mUnlabeled.clear();
662 mLabels.clear();
663 mProblem.reset();
664 mPal.reset();
665}
666
671
672void QgsLabelingEngine::drawLabelCandidateRect( pal::LabelPosition *lp, QgsRenderContext &context, const QgsMapToPixel *xform, QList<QgsLabelCandidate> *candidates )
673{
674 QPainter *painter = context.painter();
675 if ( !painter )
676 return;
677
678 QgsPointXY outPt = xform->transform( lp->getX(), lp->getY() );
679
680 painter->save();
681
682 QgsPointXY outPt2 = xform->transform( lp->getX() + lp->getWidth(), lp->getY() + lp->getHeight() );
683 QRectF rect( 0, 0, outPt2.x() - outPt.x(), outPt2.y() - outPt.y() );
684 painter->translate( QPointF( outPt.x(), outPt.y() ) );
685 painter->rotate( -lp->getAlpha() * 180 / M_PI );
686
687 if ( lp->conflictsWithObstacle() )
688 {
689 painter->setPen( QColor( 255, 0, 0, 64 ) );
690 }
691 else
692 {
693 painter->setPen( QColor( 0, 0, 0, 64 ) );
694 }
695 painter->drawRect( rect );
696 painter->restore();
697
698 // save the rect
699 rect.moveTo( outPt.x(), outPt.y() );
700 if ( candidates )
701 candidates->append( QgsLabelCandidate( rect, lp->cost() * 1000 ) );
702
703 // show all parts of the multipart label
704 if ( lp->nextPart() )
705 drawLabelCandidateRect( lp->nextPart(), context, xform, candidates );
706}
707
708void QgsLabelingEngine::drawLabelMetrics( pal::LabelPosition *label, const QgsMapToPixel &xform, QgsRenderContext &context, const QPointF &renderPoint )
709{
710 QPainter *painter = context.painter();
711 if ( !painter )
712 return;
713
714 QgsPointXY outPt2 = xform.transform( label->getX() + label->getWidth(), label->getY() + label->getHeight() );
715 QRectF rect( 0, 0, outPt2.x() - renderPoint.x(), outPt2.y() - renderPoint.y() );
716 painter->save();
717 painter->setRenderHint( QPainter::Antialiasing, false );
718 painter->translate( QPointF( renderPoint.x(), renderPoint.y() ) );
719 painter->rotate( -label->getAlpha() * 180 / M_PI );
720
721 painter->setBrush( Qt::NoBrush );
722 painter->setPen( QColor( 255, 0, 0, 220 ) );
723
724 painter->drawRect( rect );
725
726 painter->setPen( QColor( 0, 0, 0, 60 ) );
727 const QgsMargins &margins = label->getFeaturePart()->feature()->visualMargin();
728 if ( margins.top() > 0 )
729 {
730 const double topMargin = margins.top() / context.mapToPixel().mapUnitsPerPixel();
731 painter->drawLine( QPointF( rect.left(), rect.top() - topMargin ), QPointF( rect.right(), rect.top() - topMargin ) );
732 }
733 if ( margins.bottom() > 0 )
734 {
735 const double bottomMargin = margins.top() / context.mapToPixel().mapUnitsPerPixel();
736 painter->drawLine( QPointF( rect.left(), rect.bottom() + bottomMargin ), QPointF( rect.right(), rect.bottom() + bottomMargin ) );
737 }
738
739 const QRectF outerBounds = label->getFeaturePart()->feature()->outerBounds();
740 if ( !outerBounds.isNull() )
741 {
742 const QRectF mapOuterBounds = QRectF( label->getX() + outerBounds.left(), label->getY() + outerBounds.top(), outerBounds.width(), outerBounds.height() );
743
744 QgsPointXY outerBoundsPt1 = xform.transform( mapOuterBounds.left(), mapOuterBounds.top() );
745 QgsPointXY outerBoundsPt2 = xform.transform( mapOuterBounds.right(), mapOuterBounds.bottom() );
746
747 const QRectF outerBoundsPixel( outerBoundsPt1.x() - renderPoint.x(), outerBoundsPt1.y() - renderPoint.y(), outerBoundsPt2.x() - outerBoundsPt1.x(), outerBoundsPt2.y() - outerBoundsPt1.y() );
748
749 QPen pen( QColor( 255, 0, 255, 140 ) );
750 pen.setCosmetic( true );
751 pen.setWidth( 1 );
752 painter->setPen( pen );
753 painter->drawRect( outerBoundsPixel );
754 }
755
756 if ( QgsTextLabelFeature *textFeature = dynamic_cast< QgsTextLabelFeature * >( label->getFeaturePart()->feature() ) )
757 {
758 const QgsTextDocumentMetrics &metrics = textFeature->documentMetrics();
759 const QgsTextDocument &document = textFeature->document();
760 const int blockCount = document.size();
761
762 double prevBlockBaseline = rect.bottom() - rect.top();
763 const double verticalAlignOffset = -metrics.blockVerticalMargin( document.size() - 1 );
764
765 // draw block baselines
766 for ( int blockIndex = 0; blockIndex < blockCount; ++blockIndex )
767 {
768 const double blockBaseLine = metrics.baselineOffset( blockIndex, Qgis::TextLayoutMode::Labeling );
769
770 const QgsTextBlock &block = document.at( blockIndex );
771 const int fragmentCount = block.size();
772 double left = metrics.blockLeftMargin( blockIndex );
773 for ( int fragmentIndex = 0; fragmentIndex < fragmentCount; ++fragmentIndex )
774 {
775 const double fragmentVerticalOffset = metrics.fragmentVerticalOffset( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
776 const double right = left + metrics.fragmentHorizontalAdvance( blockIndex, fragmentIndex, Qgis::TextLayoutMode::Labeling );
777
778 if ( fragmentIndex > 0 )
779 {
780 QPen pen( QColor( 0, 0, 255, 220 ) );
781 pen.setStyle( Qt::PenStyle::DashLine );
782
783 painter->setPen( pen );
784
785 painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ), QPointF( rect.left() + left, rect.top() + prevBlockBaseline + verticalAlignOffset ) );
786 }
787
788 painter->setPen( QColor( 0, 0, 255, 220 ) );
789 painter->drawLine( QPointF( rect.left() + left, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ), QPointF( rect.left() + right, rect.top() + blockBaseLine + fragmentVerticalOffset + verticalAlignOffset ) );
790 left = right;
791 }
792 prevBlockBaseline = blockBaseLine;
793 }
794 }
795
796 painter->restore();
797}
798
799
800//
801// QgsDefaultLabelingEngine
802//
803
807
809{
810 registerLabels( context );
811 if ( context.renderingStopped() )
812 {
813 cleanup();
814 return; // it has been canceled
815 }
816
817 solve( context );
818 if ( context.renderingStopped() )
819 {
820 cleanup();
821 return;
822 }
823
824 drawLabels( context );
825 cleanup();
826}
827
828
829//
830// QgsStagedRenderLabelingEngine
831//
832
836
838{
839 registerLabels( context );
840 if ( context.renderingStopped() )
841 {
842 cleanup();
843 return; // it has been canceled
844 }
845
846 solve( context );
847 if ( context.renderingStopped() )
848 {
849 cleanup();
850 return;
851 }
852}
853
854
856{
857 drawLabels( context, layerId );
858}
859
864
865
867
869{
870 return mLayer ? mLayer->provider() : nullptr;
871}
872
874 : mLayerId( layer ? layer->id() : QString() )
875 , mLayer( layer )
877{
878 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
879 {
880 mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
881 if ( const QgsFeatureRenderer *renderer = vl->renderer() )
882 mLayerReferenceScale = renderer->referenceScale();
883 }
884}
885
888
891
893{
894 const auto subproviders = subProviders();
895 for ( QgsAbstractLabelProvider *subProvider : subproviders )
896 {
897 subProvider->startRender( context );
898 }
899}
900
902{
903 const auto subproviders = subProviders();
904 for ( QgsAbstractLabelProvider *subProvider : subproviders )
905 {
906 subProvider->stopRender( context );
907 }
908}
909
911{
912 return mLayerExpressionContextScope.get();
913}
914
915//
916// QgsLabelingUtils
917//
918
919QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
920{
921 QStringList predefinedOrderString;
922 const auto constPositions = positions;
923 for ( Qgis::LabelPredefinedPointPosition position : constPositions )
924 {
925 switch ( position )
926 {
928 predefinedOrderString << u"TL"_s;
929 break;
931 predefinedOrderString << u"TSL"_s;
932 break;
934 predefinedOrderString << u"T"_s;
935 break;
937 predefinedOrderString << u"TSR"_s;
938 break;
940 predefinedOrderString << u"TR"_s;
941 break;
943 predefinedOrderString << u"L"_s;
944 break;
946 predefinedOrderString << u"R"_s;
947 break;
949 predefinedOrderString << u"BL"_s;
950 break;
952 predefinedOrderString << u"BSL"_s;
953 break;
955 predefinedOrderString << u"B"_s;
956 break;
958 predefinedOrderString << u"BSR"_s;
959 break;
961 predefinedOrderString << u"BR"_s;
962 break;
964 predefinedOrderString << u"O"_s;
965 break;
966 }
967 }
968 return predefinedOrderString.join( ',' );
969}
970
971QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
972{
973 QVector<Qgis::LabelPredefinedPointPosition> result;
974 const QStringList predefinedOrderList = positionString.split( ',' );
975 result.reserve( predefinedOrderList.size() );
976 for ( const QString &position : predefinedOrderList )
977 {
978 QString cleaned = position.trimmed().toUpper();
979 if ( cleaned == "TL"_L1 )
981 else if ( cleaned == "TSL"_L1 )
983 else if ( cleaned == "T"_L1 )
985 else if ( cleaned == "TSR"_L1 )
987 else if ( cleaned == "TR"_L1 )
989 else if ( cleaned == "L"_L1 )
991 else if ( cleaned == "R"_L1 )
993 else if ( cleaned == "BL"_L1 )
995 else if ( cleaned == "BSL"_L1 )
997 else if ( cleaned == "B"_L1 )
999 else if ( cleaned == "BSR"_L1 )
1001 else if ( cleaned == "BR"_L1 )
1003 else if ( cleaned == "O"_L1 )
1005 }
1006 return result;
1007}
1008
1010{
1011 QStringList parts;
1013 parts << u"OL"_s;
1015 parts << u"AL"_s;
1017 parts << u"BL"_s;
1019 parts << u"LO"_s;
1020 return parts.join( ',' );
1021}
1022
1024{
1026 const QStringList flagList = string.split( ',' );
1027 bool foundLineOrientationFlag = false;
1028 for ( const QString &flag : flagList )
1029 {
1030 QString cleaned = flag.trimmed().toUpper();
1031 if ( cleaned == "OL"_L1 )
1033 else if ( cleaned == "AL"_L1 )
1035 else if ( cleaned == "BL"_L1 )
1037 else if ( cleaned == "LO"_L1 )
1038 foundLineOrientationFlag = true;
1039 }
1040 if ( !foundLineOrientationFlag )
1042 return flags;
1043}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1343
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
Definition qgis.h:1344
@ OnLine
Labels can be placed directly over a line feature.
Definition qgis.h:1341
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
Definition qgis.h:1342
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
Definition qgis.h:1355
@ Labeling
Labeling-specific layout mode.
Definition qgis.h:3005
@ DrawCandidates
Whether to draw rectangles of generated candidates (good for debugging).
Definition qgis.h:2948
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
Definition qgis.h:2950
@ DrawLabelMetrics
Whether to render label metric guides (for debugging).
Definition qgis.h:2951
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
Definition qgis.h:2949
@ UseAllLabels
Whether to draw all labels even if there would be collisions.
Definition qgis.h:2943
@ DrawLabelRectOnly
Whether to only draw the label rect and not the actual label text (used for unit tests).
Definition qgis.h:2947
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
Definition qgis.h:2944
@ Millimeters
Millimeters.
Definition qgis.h:5341
@ RecordProfile
Enable run-time profiling while rendering.
Definition qgis.h:2865
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
Definition qgis.h:1267
@ OverPoint
Label directly centered over point.
Definition qgis.h:1280
@ MiddleLeft
Label on left of point.
Definition qgis.h:1273
@ TopRight
Label on top-right of point.
Definition qgis.h:1272
@ MiddleRight
Label on right of point.
Definition qgis.h:1274
@ TopSlightlyRight
Label on top of point, slightly right of center.
Definition qgis.h:1271
@ TopMiddle
Label directly above point.
Definition qgis.h:1270
@ BottomSlightlyLeft
Label below point, slightly left of center.
Definition qgis.h:1276
@ BottomRight
Label on bottom right of point.
Definition qgis.h:1279
@ BottomLeft
Label on bottom-left of point.
Definition qgis.h:1275
@ BottomSlightlyRight
Label below point, slightly right of center.
Definition qgis.h:1278
@ TopLeft
Label on top-left of point.
Definition qgis.h:1268
@ BottomMiddle
Label directly below point.
Definition qgis.h:1277
@ TopSlightlyLeft
Label on top of point, slightly left of center.
Definition qgis.h:1269
An abstract interface class for label providers.
QgsExpressionContextScope * layerExpressionContextScope() const
Returns the expression context scope created from the layer associated with this provider.
virtual QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context)=0
Returns list of label features (they are owned by the provider and thus deleted on its destruction).
virtual void drawUnplacedLabel(QgsRenderContext &context, pal::LabelPosition *label) const
Draw an unplaced label.
virtual void stopRender(QgsRenderContext &context)
To be called after rendering is complete.
virtual QList< QgsAbstractLabelProvider * > subProviders()
Returns list of child providers - useful if the provider needs to put labels into more layers with di...
Qgis::LabelPlacement placement() const
What placement strategy to use for the labels.
void setEngine(const QgsLabelingEngine *engine)
Associate provider with a labeling engine (should be only called internally from QgsLabelingEngine).
virtual void drawLabel(QgsRenderContext &context, pal::LabelPosition *label) const =0
Draw this label at the position determined by the labeling engine.
QString mLayerId
Associated layer's ID, if applicable.
double priority() const
Default priority of labels (may be overridden by individual labels).
virtual void drawLabelBackground(QgsRenderContext &context, pal::LabelPosition *label) const
Draw the background for the specified label.
QString name() const
Name of the layer (for statistics, debugging etc.) - does not need to be unique.
double layerReferenceScale() const
Returns the symbology reference scale of the layer associated with this provider.
QgsMapLayer * layer() const
Returns the associated layer, or nullptr if no layer is associated with the provider.
virtual void startRender(QgsRenderContext &context)
To be called before rendering of labels begins.
Flags flags() const
Flags associated with the provider.
QgsLabelObstacleSettings::ObstacleType obstacleType() const
How the feature geometries will work as obstacles.
@ MergeConnectedLines
Whether adjacent lines (with the same label text) should be merged.
@ DrawLabels
Whether the labels should be rendered.
@ CentroidMustBeInside
Whether location of centroid must be inside of polygons.
QString layerId() const
Returns ID of associated layer, or empty string if no layer is associated with the provider.
QgsWeakMapLayerPointer mLayer
Weak pointer to source layer.
QString providerId() const
Returns provider ID - useful in case there is more than one label provider within a layer (e....
Qgis::UpsideDownLabelHandling upsidedownLabels() const
How to handle labels that would be upside down.
QgsAbstractLabelProvider(QgsMapLayer *layer, const QString &providerId=QString())
Construct the provider with default values.
QString mProviderId
Associated provider ID (one layer may have multiple providers, e.g. in rule-based labeling).
Abstract base class for labeling engine rules.
QgsDefaultLabelingEngine()
Construct the labeling engine with default settings.
void run(QgsRenderContext &context) override
Runs the labeling job.
RAII class to pop scope from an expression context on destruction.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * updateSymbolScope(const QgsSymbol *symbol, QgsExpressionContextScope *symbolScope=nullptr)
Updates a symbol scope related to a QgsSymbol to an expression context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
Abstract base class for all 2D vector feature renderers.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFields fields
Definition qgsfeature.h:70
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
static std::unique_ptr< QgsFillSymbol > createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
QgsGeometry difference(const QgsGeometry &geometry, const QgsGeometryParameters &parameters=QgsGeometryParameters()) const
Returns a geometry representing the points making up this geometry that do not make up other.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Qgis::GeometryOperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
Label blocking region (in map coordinates and CRS).
Represents a label candidate.
Describes a feature that should be used within the labeling engine.
QSizeF size(double angle=0.0) const
Size of the label (in map units).
QgsAbstractLabelProvider * provider() const
Returns provider of this instance.
void setSymbol(const QgsSymbol *symbol)
Sets the feature symbol associated with this label.
pal::Layer * mLayer
Pointer to PAL layer (assigned when registered to PAL).
QgsFeature feature() const
Returns the original feature associated with this label.
double zIndex() const
Returns the label's z-index.
const QgsMargins & visualMargin() const
Returns the visual margin for the label feature.
QRectF outerBounds() const
Returns the extreme outer bounds of the label feature, including any surrounding content like borders...
const QgsSymbol * symbol() const
Returns the feature symbol associated with this label.
QgsFeedback subclass for granular reporting of labeling engine progress.
Stores global configuration for labeling engine.
Qgis::LabelPlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
bool testFlag(Qgis::LabelingFlag f) const
Test whether a particular flag is enabled.
Qgis::LabelingFlags flags() const
Gets flags of the labeling engine.
double maximumPolygonCandidatesPerCmSquared() const
Returns the maximum number of polygon label candidate positions per centimeter squared.
double maximumLineCandidatesPerCm() const
Returns the maximum number of line label candidate positions per centimeter.
std::unique_ptr< pal::Pal > mPal
const QgsLabelingEngineSettings & engineSettings() const
Gets associated labeling engine settings.
std::unique_ptr< QgsLabelingResults > mResults
Resulting labeling layout.
QgsMapSettings mMapSettings
Associated map settings instance.
bool prepare(QgsRenderContext &context)
Prepares the engine for rendering in the specified context.
void solve(QgsRenderContext &context)
Solves the label problem.
QList< pal::LabelPosition * > mUnlabeled
std::vector< std::unique_ptr< QgsAbstractLabelingEngineRule > > mEngineRules
std::unique_ptr< pal::Problem > mProblem
QString addProvider(QgsAbstractLabelProvider *provider)
Adds a provider of label features.
const QgsMapSettings & mapSettings() const
Gets associated map settings.
QList< pal::LabelPosition * > mLabels
QgsLabelingResults * takeResults()
Returns pointer to recently computed results and pass the ownership of results to the caller.
void cleanup()
Cleans up the engine following a call to registerLabels() or solve().
void setMapSettings(const QgsMapSettings &mapSettings)
Associate map settings instance.
void registerLabels(QgsRenderContext &context)
Runs the label registration step.
QList< QgsAbstractLabelProvider * > mSubProviders
List of labeling engine rules (owned by the labeling engine).
static void drawLabelCandidateRect(pal::LabelPosition *lp, QgsRenderContext &context, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=nullptr)
Draws label candidate rectangles.
void drawLabels(QgsRenderContext &context, const QString &layerId=QString())
Draws labels to the specified render context.
QStringList participatingLayerIds() const
Returns a list of layer IDs for layers with providers in the engine.
QList< QgsMapLayer * > participatingLayers() const
Returns a list of layers with providers in the engine.
void processProvider(QgsAbstractLabelProvider *provider, QgsRenderContext &context, pal::Pal &p)
QgsAbstractLabelProvider * providerById(const QString &id)
Returns the provider with matching id, where id corresponds to the value returned by the addProvider(...
QgsLabelingEngine()
Construct the labeling engine with default settings.
QHash< QString, QgsAbstractLabelProvider * > mProvidersById
void removeProvider(QgsAbstractLabelProvider *provider)
Remove provider if the provider's initialization failed. Provider instance is deleted.
static void drawLabelMetrics(pal::LabelPosition *label, const QgsMapToPixel &xform, QgsRenderContext &context, const QPointF &renderPoint)
Draws label metrics.
QList< QgsAbstractLabelProvider * > mProviders
List of providers (the are owned by the labeling engine).
virtual ~QgsLabelingEngine()
Clean up everything (especially the registered providers).
Stores computed placement from labeling engine.
static QString encodePredefinedPositionOrder(const QVector< Qgis::LabelPredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
static QVector< Qgis::LabelPredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
static Qgis::LabelLinePlacementFlags decodeLinePlacementFlags(const QString &string)
Decodes a string to set of line placement flags.
static QString encodeLinePlacementFlags(Qgis::LabelLinePlacementFlags flags)
Encodes line placement flags to a string.
Base class for all map layer types.
Definition qgsmaplayer.h:83
Contains configuration for rendering maps.
Perform transforms between map coordinates and device coordinates.
void setMapRotation(double degrees, double cx, double cy)
Sets map rotation in degrees (clockwise).
double mapUnitsPerPixel() const
Returns the current map units per pixel.
QgsPointXY transform(const QgsPointXY &p) const
Transforms a point p from map (world) coordinates to device coordinates.
Defines the four margins of a rectangle.
Definition qgsmargins.h:40
double top() const
Returns the top margin.
Definition qgsmargins.h: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()
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:94
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63