QGIS API Documentation 3.41.0-Master (45a0abf3bec)
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#include "moc_qgslabelingengine.cpp"
19
20#include "qgslogger.h"
21
22#include "feature.h"
23#include "labelposition.h"
24#include "layer.h"
25#include "pal.h"
26#include "problem.h"
27#include "qgsrendercontext.h"
28#include "qgsmaplayer.h"
29#include "qgssymbol.h"
32#include "qgslabelingresults.h"
33#include "qgsfillsymbol.h"
34#include "qgsruntimeprofiler.h"
36
37#include <QUuid>
38
39// helper function for checking for job cancellation within PAL
40static bool _palIsCanceled( void *ctx )
41{
42 return ( reinterpret_cast< QgsRenderContext * >( ctx ) )->renderingStopped();
43}
44
46
52class QgsLabelSorter
53{
54 public:
55
56 explicit QgsLabelSorter( const QStringList &layerRenderingOrderIds )
57 : mLayerRenderingOrderIds( layerRenderingOrderIds )
58 {}
59
60 bool operator()( pal::LabelPosition *lp1, pal::LabelPosition *lp2 ) const
61 {
62 QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
63 QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
64
65 if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
66 return lf1->zIndex() < lf2->zIndex();
67
68 //equal z-index, so fallback to respecting layer render order
69 int layer1Pos = mLayerRenderingOrderIds.indexOf( lf1->provider()->layerId() );
70 int layer2Pos = mLayerRenderingOrderIds.indexOf( lf2->provider()->layerId() );
71 if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
72 return layer1Pos > layer2Pos; //higher positions are rendered first
73
74 //same layer, so render larger labels first
75 return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
76 }
77
78 private:
79
80 const QStringList mLayerRenderingOrderIds;
81};
82
84
85//
86// QgsLabelingEngine
87//
88
92
94{
95 qDeleteAll( mProviders );
96 qDeleteAll( mSubProviders );
97}
98
100{
102 mLayerRenderingOrderIds = mMapSettings.layerIds();
103 if ( mResults )
104 mResults->setMapSettings( mapSettings );
105}
106
108{
109 const QList<const QgsAbstractLabelingEngineRule *> rules = mMapSettings.labelingEngineSettings().rules();
110 bool res = true;
111 for ( const QgsAbstractLabelingEngineRule *rule : rules )
112 {
113 if ( !rule->active() || !rule->isAvailable() )
114 continue;
115
116 std::unique_ptr< QgsAbstractLabelingEngineRule > ruleClone( rule->clone() );
117 res = ruleClone->prepare( context ) && res;
118 mEngineRules.emplace_back( std::move( ruleClone ) );
119 }
120 return res;
121}
122
123QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
124{
125 QList< QgsMapLayer * > layers;
126
127 // try to return layers sorted in the desired z order for rendering
128 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
129 std::sort( providersByZ.begin(), providersByZ.end(),
130 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
131 {
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(),
144 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
145 {
146 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
147 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
148
149 if ( providerA && providerB )
150 {
151 return providerA->settings().zIndex < providerB->settings().zIndex ;
152 }
153 return false;
154 } );
155
156 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
157 {
158 if ( provider->layer() && !layers.contains( provider->layer() ) )
159 layers << provider->layer();
160 }
161 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
162 {
163 if ( provider->layer() && !layers.contains( provider->layer() ) )
164 layers << provider->layer();
165 }
166 return layers;
167}
168
170{
171 QStringList layers;
172
173 // try to return layers sorted in the desired z order for rendering
174 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
175 std::sort( providersByZ.begin(), providersByZ.end(),
176 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
177 {
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(),
190 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
191 {
192 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
193 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
194
195 if ( providerA && providerB )
196 {
197 return providerA->settings().zIndex < providerB->settings().zIndex ;
198 }
199 return false;
200 } );
201
202 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
203 {
204 if ( !layers.contains( provider->layerId() ) )
205 layers << provider->layerId();
206 }
207 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
208 {
209 if ( !layers.contains( provider->layerId() ) )
210 layers << provider->layerId();
211 }
212 return layers;
213}
214
216{
217 provider->setEngine( this );
218 mProviders << provider;
219 const QString id = QUuid::createUuid().toString( QUuid::WithoutBraces );
220 mProvidersById.insert( id, provider );
221 return id;
222}
223
225{
226 return mProvidersById.value( id );
227}
228
230{
231 int idx = mProviders.indexOf( provider );
232 if ( idx >= 0 )
233 {
234 mProvidersById.remove( mProvidersById.key( provider ) );
235 delete mProviders.takeAt( idx );
236 }
237}
238
240{
241 QgsAbstractLabelProvider::Flags flags = provider->flags();
242
243 // create the pal layer
244 pal::Layer *l = p.addLayer( provider,
245 provider->name(),
246 provider->placement(),
247 provider->priority(),
248 true,
249 flags.testFlag( QgsAbstractLabelProvider::DrawLabels ) );
250
251 // set whether adjacent lines should be merged
253
254 // set obstacle type
255 l->setObstacleType( provider->obstacleType() );
256
257 // set whether location of centroid must be inside of polygons
259
260 // set how to show upside-down labels
261 l->setUpsidedownLabels( provider->upsidedownLabels() );
262
263 const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
264
265 for ( QgsLabelFeature *feature : features )
266 {
267 try
268 {
269 l->registerFeature( feature );
270 }
271 catch ( std::exception &e )
272 {
273 Q_UNUSED( e )
274 QgsDebugMsgLevel( QStringLiteral( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
275 continue;
276 }
277 }
278
279 // any sub-providers?
280 const auto subproviders = provider->subProviders();
281 for ( QgsAbstractLabelProvider *subProvider : subproviders )
282 {
283 mSubProviders << subProvider;
284 processProvider( subProvider, context, p );
285 }
286}
287
289{
290 std::unique_ptr< QgsScopedRuntimeProfile > registeringProfile;
292 {
293 registeringProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Registering labels" ), QStringLiteral( "rendering" ) );
294 }
295
296 QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
297
298 if ( feedback )
299 feedback->emit labelRegistrationAboutToBegin();
300
302
303 mPal = std::make_unique< pal::Pal >();
304
305 mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ) );
306 mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ), 2 ) );
307
308 mPal->setShowPartialLabels( settings.testFlag( Qgis::LabelingFlag::UsePartialCandidates ) );
309 mPal->setPlacementVersion( settings.placementVersion() );
310
311 QList< QgsAbstractLabelingEngineRule * > rules;
312 rules.reserve( static_cast< int >( mEngineRules.size() ) );
313 for ( auto &it : mEngineRules )
314 {
315 rules.append( it.get() );
316 }
317 mPal->setRules( rules );
318
319 // for each provider: get labels and register them in PAL
320 const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
321 int index = 0;
322 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
323 {
324 if ( feedback )
325 {
326 feedback->emit providerRegistrationAboutToBegin( provider );
327 feedback->setProgress( index * step );
328 }
329 index++;
330 std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
331 if ( provider->layerExpressionContextScope() )
332 {
333 layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
334 }
335 processProvider( provider, context, *mPal );
336 if ( feedback )
337 feedback->emit providerRegistrationFinished( provider );
338 }
339 if ( feedback )
340 feedback->emit labelRegistrationFinished();
341}
342
344{
345 Q_ASSERT( mPal.get() );
346
347 // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
349
350 QPainter *painter = context.painter();
351
354 QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
355
356 QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
357 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
358
359 // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
361
362 // label blocking regions work by "chopping away" those regions from the permissible labeling area
363 const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
364 for ( const QgsLabelBlockingRegion &region : blockingRegions )
365 {
366 mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
367 }
368
369 if ( settings.flags() & Qgis::LabelingFlag::DrawCandidates )
370 {
371 // draw map boundary
372 QgsFeature f;
373 f.setGeometry( mapBoundaryGeom );
374 QVariantMap properties;
375 properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
376 properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
377 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "#0000ff" ) );
378 properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
379 properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
380 std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
381 boundarySymbol->startRender( context );
382 boundarySymbol->renderFeature( f, context );
383 boundarySymbol->stopRender( context );
384 }
385
386 if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
387 {
388 //PAL features are prerotated, so extent also needs to be unrotated
390 // yes - this is rotated in the opposite direction... phew, this is confusing!
392 }
393
394 QgsRectangle extent = extentGeom.boundingBox();
395
396 mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
397
398 QElapsedTimer t;
399 t.start();
400
401 // do the labeling itself
402 try
403 {
404 mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
405 }
406 catch ( std::exception &e )
407 {
408 Q_UNUSED( e )
409 QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
410 return;
411 }
412
413 if ( context.renderingStopped() )
414 {
415 return; // it has been canceled
416 }
417
418#if 1 // XXX strk
419 // features are pre-rotated but not scaled/translated,
420 // so we only disable rotation here. Ideally, they'd be
421 // also pre-scaled/translated, as suggested here:
422 // https://github.com/qgis/QGIS/issues/20071
424 xform.setMapRotation( 0, 0, 0 );
425#else
426 const QgsMapToPixel &xform = mMapSettings->mapToPixel();
427#endif
428
429 // draw rectangles with all candidates
430 // this is done before actual solution of the problem
431 // before number of candidates gets reduced
432 // TODO mCandidates.clear();
434 {
435 painter->setBrush( Qt::NoBrush );
436 for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
437 {
438 for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
439 {
440 pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
441
442 QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
443 }
444 }
445 }
446
447 // find the solution
448 mLabels = mPal->solveProblem( mProblem.get(), context,
452
453 // sort labels
454 std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mLayerRenderingOrderIds ) );
455
456 QgsDebugMsgLevel( QStringLiteral( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( mLabels.size() ), 4 );
457}
458
459void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
460{
461 QElapsedTimer t;
462 t.start();
463
464 std::unique_ptr< QgsScopedRuntimeProfile > drawingProfile;
466 {
467 drawingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering labels" ), QStringLiteral( "rendering" ) );
468 }
469
471
473 QPainter *painter = context.painter();
474
475 // prepare for rendering
476 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
477 {
478 if ( !layerId.isEmpty() && provider->layerId() != layerId )
479 continue;
480
481 // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
482 // only contains generic scopes
483 QgsExpressionContextScopePopper popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
484
485 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
486 provider->startRender( context );
487 }
488
490 std::unique_ptr< QgsExpressionContextScopePopper > symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
491
492 // draw label backgrounds
493 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
494 {
495 if ( context.renderingStopped() )
496 break;
497
498 QgsLabelFeature *lf = label->getFeaturePart()->feature();
499 if ( !lf )
500 {
501 continue;
502 }
503
504 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
505 continue;
506
507 context.expressionContext().setFeature( lf->feature() );
508 context.expressionContext().setFields( lf->feature().fields() );
509
510 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
511
512 if ( lf->symbol() )
513 {
514 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
515 }
516 lf->provider()->drawLabelBackground( context, label );
517 }
518
519 // draw the labels
520 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
521 {
522 if ( context.renderingStopped() )
523 break;
524
525 QgsLabelFeature *lf = label->getFeaturePart()->feature();
526 if ( !lf )
527 {
528 continue;
529 }
530
531 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
532 continue;
533
534 context.expressionContext().setFeature( lf->feature() );
535 context.expressionContext().setFields( lf->feature().fields() );
536
537 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
538 if ( lf->symbol() )
539 {
540 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
541 }
542 lf->provider()->drawLabel( context, label );
543 // finished with symbol -- we can't keep it around after this, it may be deleted
544 lf->setSymbol( nullptr );
545 }
546
547 // draw unplaced labels. These are always rendered on top
549 {
550 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
551 {
552 if ( context.renderingStopped() )
553 break;
554 QgsLabelFeature *lf = label->getFeaturePart()->feature();
555 if ( !lf )
556 {
557 continue;
558 }
559
560 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
561 continue;
562
563 context.expressionContext().setFeature( lf->feature() );
564 context.expressionContext().setFields( lf->feature().fields() );
565
566 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
567 if ( lf->symbol() )
568 {
569 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
570 }
571 lf->provider()->drawUnplacedLabel( context, label );
572 // finished with symbol -- we can't keep it around after this, it may be deleted
573 lf->setSymbol( nullptr );
574 }
575 }
576
577 symbolScopePopper.reset();
578
579 // cleanup
580 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
581 {
582 if ( !layerId.isEmpty() && provider->layerId() != layerId )
583 continue;
584
585 provider->stopRender( context );
586 }
587
588 // Reset composition mode for further drawing operations
589 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
590
591 QgsDebugMsgLevel( QStringLiteral( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
592}
593
595{
596 mUnlabeled.clear();
597 mLabels.clear();
598 mProblem.reset();
599 mPal.reset();
600}
601
606
607
608//
609// QgsDefaultLabelingEngine
610//
611
617
619{
620 registerLabels( context );
621 if ( context.renderingStopped() )
622 {
623 cleanup();
624 return; // it has been canceled
625 }
626
627 solve( context );
628 if ( context.renderingStopped() )
629 {
630 cleanup();
631 return;
632 }
633
634 drawLabels( context );
635 cleanup();
636}
637
638
639//
640// QgsStagedRenderLabelingEngine
641//
642
648
650{
651 registerLabels( context );
652 if ( context.renderingStopped() )
653 {
654 cleanup();
655 return; // it has been canceled
656 }
657
658 solve( context );
659 if ( context.renderingStopped() )
660 {
661 cleanup();
662 return;
663 }
664}
665
666
668{
669 drawLabels( context, layerId );
670}
671
676
677
679
681{
682 return mLayer ? mLayer->provider() : nullptr;
683
684}
685
687 : mLayerId( layer ? layer->id() : QString() )
688 , mLayer( layer )
689 , mProviderId( providerId )
690{
691 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
692 {
693 mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
694 if ( const QgsFeatureRenderer *renderer = vl->renderer() )
695 mLayerReferenceScale = renderer->referenceScale();
696 }
697}
698
703
708
710{
711 const auto subproviders = subProviders();
712 for ( QgsAbstractLabelProvider *subProvider : subproviders )
713 {
714 subProvider->startRender( context );
715 }
716}
717
719{
720 const auto subproviders = subProviders();
721 for ( QgsAbstractLabelProvider *subProvider : subproviders )
722 {
723 subProvider->stopRender( context );
724 }
725}
726
728{
729 return mLayerExpressionContextScope.get();
730}
731
732//
733// QgsLabelingUtils
734//
735
736QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
737{
738 QStringList predefinedOrderString;
739 const auto constPositions = positions;
740 for ( Qgis::LabelPredefinedPointPosition position : constPositions )
741 {
742 switch ( position )
743 {
745 predefinedOrderString << QStringLiteral( "TL" );
746 break;
748 predefinedOrderString << QStringLiteral( "TSL" );
749 break;
751 predefinedOrderString << QStringLiteral( "T" );
752 break;
754 predefinedOrderString << QStringLiteral( "TSR" );
755 break;
757 predefinedOrderString << QStringLiteral( "TR" );
758 break;
760 predefinedOrderString << QStringLiteral( "L" );
761 break;
763 predefinedOrderString << QStringLiteral( "R" );
764 break;
766 predefinedOrderString << QStringLiteral( "BL" );
767 break;
769 predefinedOrderString << QStringLiteral( "BSL" );
770 break;
772 predefinedOrderString << QStringLiteral( "B" );
773 break;
775 predefinedOrderString << QStringLiteral( "BSR" );
776 break;
778 predefinedOrderString << QStringLiteral( "BR" );
779 break;
781 predefinedOrderString << QStringLiteral( "O" );
782 break;
783 }
784 }
785 return predefinedOrderString.join( ',' );
786}
787
788QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
789{
790 QVector<Qgis::LabelPredefinedPointPosition> result;
791 const QStringList predefinedOrderList = positionString.split( ',' );
792 result.reserve( predefinedOrderList.size() );
793 for ( const QString &position : predefinedOrderList )
794 {
795 QString cleaned = position.trimmed().toUpper();
796 if ( cleaned == QLatin1String( "TL" ) )
798 else if ( cleaned == QLatin1String( "TSL" ) )
800 else if ( cleaned == QLatin1String( "T" ) )
802 else if ( cleaned == QLatin1String( "TSR" ) )
804 else if ( cleaned == QLatin1String( "TR" ) )
806 else if ( cleaned == QLatin1String( "L" ) )
808 else if ( cleaned == QLatin1String( "R" ) )
810 else if ( cleaned == QLatin1String( "BL" ) )
812 else if ( cleaned == QLatin1String( "BSL" ) )
814 else if ( cleaned == QLatin1String( "B" ) )
816 else if ( cleaned == QLatin1String( "BSR" ) )
818 else if ( cleaned == QLatin1String( "BR" ) )
820 else if ( cleaned == QLatin1String( "O" ) )
822 }
823 return result;
824}
825
827{
828 QStringList parts;
830 parts << QStringLiteral( "OL" );
832 parts << QStringLiteral( "AL" );
834 parts << QStringLiteral( "BL" );
836 parts << QStringLiteral( "LO" );
837 return parts.join( ',' );
838}
839
841{
843 const QStringList flagList = string.split( ',' );
844 bool foundLineOrientationFlag = false;
845 for ( const QString &flag : flagList )
846 {
847 QString cleaned = flag.trimmed().toUpper();
848 if ( cleaned == QLatin1String( "OL" ) )
850 else if ( cleaned == QLatin1String( "AL" ) )
852 else if ( cleaned == QLatin1String( "BL" ) )
854 else if ( cleaned == QLatin1String( "LO" ) )
855 foundLineOrientationFlag = true;
856 }
857 if ( !foundLineOrientationFlag )
859 return flags;
860}
@ BelowLine
Labels can be placed below a line feature. Unless MapOrientation is also specified this mode respects...
@ MapOrientation
Signifies that the AboveLine and BelowLine flags should respect the map's orientation rather than the...
@ OnLine
Labels can be placed directly over a line feature.
@ AboveLine
Labels can be placed above a line feature. Unless MapOrientation is also specified this mode respects...
QFlags< LabelLinePlacementFlag > LabelLinePlacementFlags
Line placement flags, which control how candidates are generated for a linear feature.
Definition qgis.h:1221
@ DrawCandidates
Whether to draw rectangles of generated candidates (good for debugging)
@ CollectUnplacedLabels
Whether unplaced labels should be collected in the labeling results (regardless of whether they are b...
@ DrawUnplacedLabels
Whether to render unplaced labels as an indicator/warning for users.
@ UseAllLabels
Whether to draw all labels even if there would be collisions.
@ UsePartialCandidates
Whether to use also label candidates that are partially outside of the map view.
@ Millimeters
Millimeters.
@ RecordProfile
Enable run-time profiling while rendering.
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
Definition qgis.h:1146
@ OverPoint
Label directly centered over point.
@ MiddleLeft
Label on left of point.
@ TopRight
Label on top-right of point.
@ MiddleRight
Label on right of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopMiddle
Label directly above point.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ TopLeft
Label on top-left of point.
@ BottomMiddle
Label directly below point.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
The QgsAbstractLabelProvider class is an interface class.
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.
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.
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.
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:58
QgsFields fields
Definition qgsfeature.h:68
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:61
static 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).
The QgsLabelFeature class 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 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.
QList< QgsAbstractLabelingEngineRule * > rules()
Returns a list of labeling engine rules which must be satifisfied while placing labels.
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.
The QgsLabelingEngine class provides map labeling functionality.
std::unique_ptr< pal::Pal > mPal
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)
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.
QList< QgsAbstractLabelProvider * > mProviders
List of providers (the are owned by the labeling engine)
virtual ~QgsLabelingEngine()
Clean up everything (especially the registered providers)
Class that 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:76
The QgsMapSettings class contains configuration for rendering of the map.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
QgsGeometry labelBoundaryGeometry() const
Returns the label boundary geometry, which restricts where in the rendered map labels are permitted t...
QStringList layerIds(bool expandGroupLayers=false) const
Returns the list of layer IDs which will be rendered in the map.
const QgsMapToPixel & mapToPixel() const
QList< QgsLabelBlockingRegion > labelBlockingRegions() const
Returns the list of regions to avoid placing labels within.
double extentBuffer() const
Returns the buffer in map units to use around the visible extent for rendering symbols whose correspo...
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
double rotation() const
Returns the rotation of the resulting map image, in degrees clockwise.
QPolygonF visiblePolygonWithBuffer() const
Returns the visible area as a polygon (may be rotated) with extent buffer included.
Perform transforms between map coordinates and device coordinates.
void setMapRotation(double degrees, double cx, double cy)
Sets map rotation in degrees (clockwise).
static void drawLabelCandidateRect(pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList< QgsLabelCandidate > *candidates=nullptr)
A rectangle specified with double values.
QgsPointXY center() const
Returns the center point of the rectangle.
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.
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 vector layer which manages a vector based data sets.
QgsLabelFeature * feature()
Returns the parent feature.
Definition feature.h:94
LabelPosition is a candidate feature label position.
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
A set of features which influence the labeling process.
Definition layer.h:63
void setUpsidedownLabels(Qgis::UpsideDownLabelHandling ud)
Sets how upside down labels will be handled within the layer.
Definition layer.h:263
bool registerFeature(QgsLabelFeature *label)
Register a feature in the layer.
Definition layer.cpp:79
QgsAbstractLabelProvider * provider() const
Returns pointer to the associated provider.
Definition layer.h:157
void setObstacleType(QgsLabelObstacleSettings::ObstacleType obstacleType)
Sets the obstacle type, which controls how features within the layer act as obstacles for labels.
Definition layer.h:228
void setMergeConnectedLines(bool merge)
Sets whether connected lines should be merged before labeling.
Definition layer.h:250
void setCentroidInside(bool forceInside)
Sets whether labels placed at the centroid of features within the layer are forced to be placed insid...
Definition layer.h:278
Main Pal labeling class.
Definition pal.h:83
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
add a new layer
Definition pal.cpp:87
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5958
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39