QGIS API Documentation 3.39.0-Master (d85f3c2a281)
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 "qgslogger.h"
20
21#include "feature.h"
22#include "labelposition.h"
23#include "layer.h"
24#include "pal.h"
25#include "problem.h"
26#include "qgsrendercontext.h"
27#include "qgsmaplayer.h"
28#include "qgssymbol.h"
31#include "qgslabelingresults.h"
32#include "qgsfillsymbol.h"
33#include "qgsruntimeprofiler.h"
35
36#include <QUuid>
37
38// helper function for checking for job cancellation within PAL
39static bool _palIsCanceled( void *ctx )
40{
41 return ( reinterpret_cast< QgsRenderContext * >( ctx ) )->renderingStopped();
42}
43
45
51class QgsLabelSorter
52{
53 public:
54
55 explicit QgsLabelSorter( const QStringList &layerRenderingOrderIds )
56 : mLayerRenderingOrderIds( layerRenderingOrderIds )
57 {}
58
59 bool operator()( pal::LabelPosition *lp1, pal::LabelPosition *lp2 ) const
60 {
61 QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
62 QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
63
64 if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
65 return lf1->zIndex() < lf2->zIndex();
66
67 //equal z-index, so fallback to respecting layer render order
68 int layer1Pos = mLayerRenderingOrderIds.indexOf( lf1->provider()->layerId() );
69 int layer2Pos = mLayerRenderingOrderIds.indexOf( lf2->provider()->layerId() );
70 if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
71 return layer1Pos > layer2Pos; //higher positions are rendered first
72
73 //same layer, so render larger labels first
74 return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
75 }
76
77 private:
78
79 const QStringList mLayerRenderingOrderIds;
80};
81
83
84//
85// QgsLabelingEngine
86//
87
91
93{
94 qDeleteAll( mProviders );
95 qDeleteAll( mSubProviders );
96}
97
99{
101 mLayerRenderingOrderIds = mMapSettings.layerIds();
102 if ( mResults )
103 mResults->setMapSettings( mapSettings );
104}
105
107{
108 const QList<const QgsAbstractLabelingEngineRule *> rules = mMapSettings.labelingEngineSettings().rules();
109 bool res = true;
110 for ( const QgsAbstractLabelingEngineRule *rule : rules )
111 {
112 if ( !rule->active() || !rule->isAvailable() )
113 continue;
114
115 std::unique_ptr< QgsAbstractLabelingEngineRule > ruleClone( rule->clone() );
116 res = ruleClone->prepare( context ) && res;
117 mEngineRules.emplace_back( std::move( ruleClone ) );
118 }
119 return res;
120}
121
122QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
123{
124 QList< QgsMapLayer * > layers;
125
126 // try to return layers sorted in the desired z order for rendering
127 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
128 std::sort( providersByZ.begin(), providersByZ.end(),
129 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
130 {
131 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
132 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
133
134 if ( providerA && providerB )
135 {
136 return providerA->settings().zIndex < providerB->settings().zIndex ;
137 }
138 return false;
139 } );
140
141 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
142 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
143 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
144 {
145 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
146 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
147
148 if ( providerA && providerB )
149 {
150 return providerA->settings().zIndex < providerB->settings().zIndex ;
151 }
152 return false;
153 } );
154
155 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
156 {
157 if ( provider->layer() && !layers.contains( provider->layer() ) )
158 layers << provider->layer();
159 }
160 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
161 {
162 if ( provider->layer() && !layers.contains( provider->layer() ) )
163 layers << provider->layer();
164 }
165 return layers;
166}
167
169{
170 QStringList layers;
171
172 // try to return layers sorted in the desired z order for rendering
173 QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
174 std::sort( providersByZ.begin(), providersByZ.end(),
175 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
176 {
177 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
178 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
179
180 if ( providerA && providerB )
181 {
182 return providerA->settings().zIndex < providerB->settings().zIndex ;
183 }
184 return false;
185 } );
186
187 QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
188 std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
189 []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
190 {
191 const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
192 const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
193
194 if ( providerA && providerB )
195 {
196 return providerA->settings().zIndex < providerB->settings().zIndex ;
197 }
198 return false;
199 } );
200
201 for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
202 {
203 if ( !layers.contains( provider->layerId() ) )
204 layers << provider->layerId();
205 }
206 for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
207 {
208 if ( !layers.contains( provider->layerId() ) )
209 layers << provider->layerId();
210 }
211 return layers;
212}
213
215{
216 provider->setEngine( this );
217 mProviders << provider;
218 const QString id = QUuid::createUuid().toString( QUuid::WithoutBraces );
219 mProvidersById.insert( id, provider );
220 return id;
221}
222
224{
225 return mProvidersById.value( id );
226}
227
229{
230 int idx = mProviders.indexOf( provider );
231 if ( idx >= 0 )
232 {
233 mProvidersById.remove( mProvidersById.key( provider ) );
234 delete mProviders.takeAt( idx );
235 }
236}
237
239{
240 QgsAbstractLabelProvider::Flags flags = provider->flags();
241
242 // create the pal layer
243 pal::Layer *l = p.addLayer( provider,
244 provider->name(),
245 provider->placement(),
246 provider->priority(),
247 true,
248 flags.testFlag( QgsAbstractLabelProvider::DrawLabels ) );
249
250 // set whether adjacent lines should be merged
252
253 // set obstacle type
254 l->setObstacleType( provider->obstacleType() );
255
256 // set whether location of centroid must be inside of polygons
258
259 // set how to show upside-down labels
260 l->setUpsidedownLabels( provider->upsidedownLabels() );
261
262 const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
263
264 for ( QgsLabelFeature *feature : features )
265 {
266 try
267 {
268 l->registerFeature( feature );
269 }
270 catch ( std::exception &e )
271 {
272 Q_UNUSED( e )
273 QgsDebugMsgLevel( QStringLiteral( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
274 continue;
275 }
276 }
277
278 // any sub-providers?
279 const auto subproviders = provider->subProviders();
280 for ( QgsAbstractLabelProvider *subProvider : subproviders )
281 {
282 mSubProviders << subProvider;
283 processProvider( subProvider, context, p );
284 }
285}
286
288{
289 std::unique_ptr< QgsScopedRuntimeProfile > registeringProfile;
291 {
292 registeringProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Registering labels" ), QStringLiteral( "rendering" ) );
293 }
294
295 QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
296
297 if ( feedback )
298 feedback->emit labelRegistrationAboutToBegin();
299
301
302 mPal = std::make_unique< pal::Pal >();
303
304 mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ) );
305 mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, Qgis::RenderUnit::Millimeters ), 2 ) );
306
307 mPal->setShowPartialLabels( settings.testFlag( Qgis::LabelingFlag::UsePartialCandidates ) );
308 mPal->setPlacementVersion( settings.placementVersion() );
309
310 QList< QgsAbstractLabelingEngineRule * > rules;
311 rules.reserve( static_cast< int >( mEngineRules.size() ) );
312 for ( auto &it : mEngineRules )
313 {
314 rules.append( it.get() );
315 }
316 mPal->setRules( rules );
317
318 // for each provider: get labels and register them in PAL
319 const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
320 int index = 0;
321 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
322 {
323 if ( feedback )
324 {
325 feedback->emit providerRegistrationAboutToBegin( provider );
326 feedback->setProgress( index * step );
327 }
328 index++;
329 std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
330 if ( provider->layerExpressionContextScope() )
331 {
332 layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
333 }
334 processProvider( provider, context, *mPal );
335 if ( feedback )
336 feedback->emit providerRegistrationFinished( provider );
337 }
338 if ( feedback )
339 feedback->emit labelRegistrationFinished();
340}
341
343{
344 Q_ASSERT( mPal.get() );
345
346 // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
348
349 QPainter *painter = context.painter();
350
353 QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
354
355 QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
356 visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
357
358 // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
360
361 // label blocking regions work by "chopping away" those regions from the permissible labeling area
362 const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
363 for ( const QgsLabelBlockingRegion &region : blockingRegions )
364 {
365 mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
366 }
367
368 if ( settings.flags() & Qgis::LabelingFlag::DrawCandidates )
369 {
370 // draw map boundary
371 QgsFeature f;
372 f.setGeometry( mapBoundaryGeom );
373 QVariantMap properties;
374 properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
375 properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
376 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "#0000ff" ) );
377 properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
378 properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
379 std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
380 boundarySymbol->startRender( context );
381 boundarySymbol->renderFeature( f, context );
382 boundarySymbol->stopRender( context );
383 }
384
385 if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
386 {
387 //PAL features are prerotated, so extent also needs to be unrotated
389 // yes - this is rotated in the opposite direction... phew, this is confusing!
391 }
392
393 QgsRectangle extent = extentGeom.boundingBox();
394
395 mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
396
397 QElapsedTimer t;
398 t.start();
399
400 // do the labeling itself
401 try
402 {
403 mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
404 }
405 catch ( std::exception &e )
406 {
407 Q_UNUSED( e )
408 QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
409 return;
410 }
411
412 if ( context.renderingStopped() )
413 {
414 return; // it has been canceled
415 }
416
417#if 1 // XXX strk
418 // features are pre-rotated but not scaled/translated,
419 // so we only disable rotation here. Ideally, they'd be
420 // also pre-scaled/translated, as suggested here:
421 // https://github.com/qgis/QGIS/issues/20071
423 xform.setMapRotation( 0, 0, 0 );
424#else
425 const QgsMapToPixel &xform = mMapSettings->mapToPixel();
426#endif
427
428 // draw rectangles with all candidates
429 // this is done before actual solution of the problem
430 // before number of candidates gets reduced
431 // TODO mCandidates.clear();
433 {
434 painter->setBrush( Qt::NoBrush );
435 for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
436 {
437 for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
438 {
439 pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
440
441 QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
442 }
443 }
444 }
445
446 // find the solution
447 mLabels = mPal->solveProblem( mProblem.get(), context,
451
452 // sort labels
453 std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mLayerRenderingOrderIds ) );
454
455 QgsDebugMsgLevel( QStringLiteral( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( mLabels.size() ), 4 );
456}
457
458void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
459{
460 QElapsedTimer t;
461 t.start();
462
463 std::unique_ptr< QgsScopedRuntimeProfile > drawingProfile;
465 {
466 drawingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Rendering labels" ), QStringLiteral( "rendering" ) );
467 }
468
470
472 QPainter *painter = context.painter();
473
474 // prepare for rendering
475 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
476 {
477 if ( !layerId.isEmpty() && provider->layerId() != layerId )
478 continue;
479
480 // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
481 // only contains generic scopes
482 QgsExpressionContextScopePopper popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
483
484 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
485 provider->startRender( context );
486 }
487
489 std::unique_ptr< QgsExpressionContextScopePopper > symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
490
491 // draw label backgrounds
492 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
493 {
494 if ( context.renderingStopped() )
495 break;
496
497 QgsLabelFeature *lf = label->getFeaturePart()->feature();
498 if ( !lf )
499 {
500 continue;
501 }
502
503 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
504 continue;
505
506 context.expressionContext().setFeature( lf->feature() );
507 context.expressionContext().setFields( lf->feature().fields() );
508
509 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
510
511 if ( lf->symbol() )
512 {
513 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
514 }
515 lf->provider()->drawLabelBackground( context, label );
516 }
517
518 // draw the labels
519 for ( pal::LabelPosition *label : std::as_const( mLabels ) )
520 {
521 if ( context.renderingStopped() )
522 break;
523
524 QgsLabelFeature *lf = label->getFeaturePart()->feature();
525 if ( !lf )
526 {
527 continue;
528 }
529
530 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
531 continue;
532
533 context.expressionContext().setFeature( lf->feature() );
534 context.expressionContext().setFields( lf->feature().fields() );
535
536 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
537 if ( lf->symbol() )
538 {
539 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
540 }
541 lf->provider()->drawLabel( context, label );
542 // finished with symbol -- we can't keep it around after this, it may be deleted
543 lf->setSymbol( nullptr );
544 }
545
546 // draw unplaced labels. These are always rendered on top
548 {
549 for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
550 {
551 if ( context.renderingStopped() )
552 break;
553 QgsLabelFeature *lf = label->getFeaturePart()->feature();
554 if ( !lf )
555 {
556 continue;
557 }
558
559 if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
560 continue;
561
562 context.expressionContext().setFeature( lf->feature() );
563 context.expressionContext().setFields( lf->feature().fields() );
564
565 QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
566 if ( lf->symbol() )
567 {
568 symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
569 }
570 lf->provider()->drawUnplacedLabel( context, label );
571 // finished with symbol -- we can't keep it around after this, it may be deleted
572 lf->setSymbol( nullptr );
573 }
574 }
575
576 symbolScopePopper.reset();
577
578 // cleanup
579 for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
580 {
581 if ( !layerId.isEmpty() && provider->layerId() != layerId )
582 continue;
583
584 provider->stopRender( context );
585 }
586
587 // Reset composition mode for further drawing operations
588 painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
589
590 QgsDebugMsgLevel( QStringLiteral( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
591}
592
594{
595 mUnlabeled.clear();
596 mLabels.clear();
597 mProblem.reset();
598 mPal.reset();
599}
600
605
606
607//
608// QgsDefaultLabelingEngine
609//
610
616
618{
619 registerLabels( context );
620 if ( context.renderingStopped() )
621 {
622 cleanup();
623 return; // it has been canceled
624 }
625
626 solve( context );
627 if ( context.renderingStopped() )
628 {
629 cleanup();
630 return;
631 }
632
633 drawLabels( context );
634 cleanup();
635}
636
637
638//
639// QgsStagedRenderLabelingEngine
640//
641
647
649{
650 registerLabels( context );
651 if ( context.renderingStopped() )
652 {
653 cleanup();
654 return; // it has been canceled
655 }
656
657 solve( context );
658 if ( context.renderingStopped() )
659 {
660 cleanup();
661 return;
662 }
663}
664
665
667{
668 drawLabels( context, layerId );
669}
670
675
676
678
680{
681 return mLayer ? mLayer->provider() : nullptr;
682
683}
684
686 : mLayerId( layer ? layer->id() : QString() )
687 , mLayer( layer )
688 , mProviderId( providerId )
689{
690 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
691 {
692 mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
693 if ( const QgsFeatureRenderer *renderer = vl->renderer() )
694 mLayerReferenceScale = renderer->referenceScale();
695 }
696}
697
702
707
709{
710 const auto subproviders = subProviders();
711 for ( QgsAbstractLabelProvider *subProvider : subproviders )
712 {
713 subProvider->startRender( context );
714 }
715}
716
718{
719 const auto subproviders = subProviders();
720 for ( QgsAbstractLabelProvider *subProvider : subproviders )
721 {
722 subProvider->stopRender( context );
723 }
724}
725
727{
728 return mLayerExpressionContextScope.get();
729}
730
731//
732// QgsLabelingUtils
733//
734
735QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
736{
737 QStringList predefinedOrderString;
738 const auto constPositions = positions;
739 for ( Qgis::LabelPredefinedPointPosition position : constPositions )
740 {
741 switch ( position )
742 {
744 predefinedOrderString << QStringLiteral( "TL" );
745 break;
747 predefinedOrderString << QStringLiteral( "TSL" );
748 break;
750 predefinedOrderString << QStringLiteral( "T" );
751 break;
753 predefinedOrderString << QStringLiteral( "TSR" );
754 break;
756 predefinedOrderString << QStringLiteral( "TR" );
757 break;
759 predefinedOrderString << QStringLiteral( "L" );
760 break;
762 predefinedOrderString << QStringLiteral( "R" );
763 break;
765 predefinedOrderString << QStringLiteral( "BL" );
766 break;
768 predefinedOrderString << QStringLiteral( "BSL" );
769 break;
771 predefinedOrderString << QStringLiteral( "B" );
772 break;
774 predefinedOrderString << QStringLiteral( "BSR" );
775 break;
777 predefinedOrderString << QStringLiteral( "BR" );
778 break;
780 predefinedOrderString << QStringLiteral( "O" );
781 break;
782 }
783 }
784 return predefinedOrderString.join( ',' );
785}
786
787QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
788{
789 QVector<Qgis::LabelPredefinedPointPosition> result;
790 const QStringList predefinedOrderList = positionString.split( ',' );
791 result.reserve( predefinedOrderList.size() );
792 for ( const QString &position : predefinedOrderList )
793 {
794 QString cleaned = position.trimmed().toUpper();
795 if ( cleaned == QLatin1String( "TL" ) )
797 else if ( cleaned == QLatin1String( "TSL" ) )
799 else if ( cleaned == QLatin1String( "T" ) )
801 else if ( cleaned == QLatin1String( "TSR" ) )
803 else if ( cleaned == QLatin1String( "TR" ) )
805 else if ( cleaned == QLatin1String( "L" ) )
807 else if ( cleaned == QLatin1String( "R" ) )
809 else if ( cleaned == QLatin1String( "BL" ) )
811 else if ( cleaned == QLatin1String( "BSL" ) )
813 else if ( cleaned == QLatin1String( "B" ) )
815 else if ( cleaned == QLatin1String( "BSR" ) )
817 else if ( cleaned == QLatin1String( "BR" ) )
819 else if ( cleaned == QLatin1String( "O" ) )
821 }
822 return result;
823}
824
826{
827 QStringList parts;
829 parts << QStringLiteral( "OL" );
831 parts << QStringLiteral( "AL" );
833 parts << QStringLiteral( "BL" );
835 parts << QStringLiteral( "LO" );
836 return parts.join( ',' );
837}
838
840{
842 const QStringList flagList = string.split( ',' );
843 bool foundLineOrientationFlag = false;
844 for ( const QString &flag : flagList )
845 {
846 QString cleaned = flag.trimmed().toUpper();
847 if ( cleaned == QLatin1String( "OL" ) )
849 else if ( cleaned == QLatin1String( "AL" ) )
851 else if ( cleaned == QLatin1String( "BL" ) )
853 else if ( cleaned == QLatin1String( "LO" ) )
854 foundLineOrientationFlag = true;
855 }
856 if ( !foundLineOrientationFlag )
858 return flags;
859}
@ 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:84
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
add a new layer
Definition pal.cpp:88
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5857
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39