QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 
34 // helper function for checking for job cancellation within PAL
35 static bool _palIsCanceled( void *ctx )
36 {
37  return ( reinterpret_cast< QgsRenderContext * >( ctx ) )->renderingStopped();
38 }
39 
46 {
47  public:
48 
49  explicit QgsLabelSorter( const QgsMapSettings &mapSettings )
50  : mMapSettings( mapSettings )
51  {}
52 
54  {
55  QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
56  QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
57 
58  if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
59  return lf1->zIndex() < lf2->zIndex();
60 
61  //equal z-index, so fallback to respecting layer render order
62  QStringList layerIds = mMapSettings.layerIds();
63  int layer1Pos = layerIds.indexOf( lf1->provider()->layerId() );
64  int layer2Pos = layerIds.indexOf( lf2->provider()->layerId() );
65  if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
66  return layer1Pos > layer2Pos; //higher positions are rendered first
67 
68  //same layer, so render larger labels first
69  return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
70  }
71 
72  private:
73 
74  const QgsMapSettings &mMapSettings;
75 };
76 
77 //
78 // QgsLabelingEngine
79 //
80 
82  : mResults( new QgsLabelingResults )
83 {}
84 
86 {
87  qDeleteAll( mProviders );
88  qDeleteAll( mSubProviders );
89 }
90 
92 {
94  if ( mResults )
95  mResults->setMapSettings( mapSettings );
96 }
97 
98 QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
99 {
100  QList< QgsMapLayer * > layers;
101 
102  // try to return layers sorted in the desired z order for rendering
103  QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
104  std::sort( providersByZ.begin(), providersByZ.end(),
105  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
106  {
107  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
108  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
109 
110  if ( providerA && providerB )
111  {
112  return providerA->settings().zIndex < providerB->settings().zIndex ;
113  }
114  return false;
115  } );
116 
117  QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
118  std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
119  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
120  {
121  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
122  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
123 
124  if ( providerA && providerB )
125  {
126  return providerA->settings().zIndex < providerB->settings().zIndex ;
127  }
128  return false;
129  } );
130 
131  for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
132  {
133  if ( provider->layer() && !layers.contains( provider->layer() ) )
134  layers << provider->layer();
135  }
136  for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
137  {
138  if ( provider->layer() && !layers.contains( provider->layer() ) )
139  layers << provider->layer();
140  }
141  return layers;
142 }
143 
145 {
146  QStringList layers;
147 
148  // try to return layers sorted in the desired z order for rendering
149  QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
150  std::sort( providersByZ.begin(), providersByZ.end(),
151  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
152  {
153  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
154  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
155 
156  if ( providerA && providerB )
157  {
158  return providerA->settings().zIndex < providerB->settings().zIndex ;
159  }
160  return false;
161  } );
162 
163  QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
164  std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
165  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
166  {
167  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
168  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
169 
170  if ( providerA && providerB )
171  {
172  return providerA->settings().zIndex < providerB->settings().zIndex ;
173  }
174  return false;
175  } );
176 
177  for ( QgsAbstractLabelProvider *provider : std::as_const( providersByZ ) )
178  {
179  if ( !layers.contains( provider->layerId() ) )
180  layers << provider->layerId();
181  }
182  for ( QgsAbstractLabelProvider *provider : std::as_const( subProvidersByZ ) )
183  {
184  if ( !layers.contains( provider->layerId() ) )
185  layers << provider->layerId();
186  }
187  return layers;
188 }
189 
191 {
192  provider->setEngine( this );
193  mProviders << provider;
194 }
195 
197 {
198  int idx = mProviders.indexOf( provider );
199  if ( idx >= 0 )
200  {
201  delete mProviders.takeAt( idx );
202  }
203 }
204 
206 {
207  QgsAbstractLabelProvider::Flags flags = provider->flags();
208 
209  // create the pal layer
210  pal::Layer *l = p.addLayer( provider,
211  provider->name(),
212  provider->placement(),
213  provider->priority(),
214  true,
215  flags.testFlag( QgsAbstractLabelProvider::DrawLabels ),
216  flags.testFlag( QgsAbstractLabelProvider::DrawAllLabels ) );
217 
218  // set whether adjacent lines should be merged
220 
221  // set obstacle type
222  l->setObstacleType( provider->obstacleType() );
223 
224  // set whether location of centroid must be inside of polygons
226 
227  // set how to show upside-down labels
229  switch ( provider->upsidedownLabels() )
230  {
232  upsdnlabels = pal::Layer::Upright;
233  break;
235  upsdnlabels = pal::Layer::ShowDefined;
236  break;
238  upsdnlabels = pal::Layer::ShowAll;
239  break;
240  }
241  l->setUpsidedownLabels( upsdnlabels );
242 
243 
244  const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
245 
246  for ( QgsLabelFeature *feature : features )
247  {
248  try
249  {
250  l->registerFeature( feature );
251  }
252  catch ( std::exception &e )
253  {
254  Q_UNUSED( e )
255  QgsDebugMsgLevel( QStringLiteral( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
256  continue;
257  }
258  }
259 
260  // any sub-providers?
261  const auto subproviders = provider->subProviders();
262  for ( QgsAbstractLabelProvider *subProvider : subproviders )
263  {
264  mSubProviders << subProvider;
265  processProvider( subProvider, context, p );
266  }
267 }
268 
270 {
271  QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
272 
273  if ( feedback )
274  feedback->emit labelRegistrationAboutToBegin();
275 
277 
278  mPal = std::make_unique< pal::Pal >();
279 
280  mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ) );
281  mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ), 2 ) );
282 
283  mPal->setShowPartialLabels( settings.testFlag( QgsLabelingEngineSettings::UsePartialCandidates ) );
284  mPal->setPlacementVersion( settings.placementVersion() );
285 
286  // for each provider: get labels and register them in PAL
287  const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
288  int index = 0;
289  for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
290  {
291  if ( feedback )
292  {
293  feedback->emit providerRegistrationAboutToBegin( provider );
294  feedback->setProgress( index * step );
295  }
296  index++;
297  std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
298  if ( provider->layerExpressionContextScope() )
299  {
300  layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
301  }
302  processProvider( provider, context, *mPal );
303  if ( feedback )
304  feedback->emit providerRegistrationFinished( provider );
305  }
306  if ( feedback )
307  feedback->emit labelRegistrationFinished();
308 }
309 
311 {
312  Q_ASSERT( mPal.get() );
313 
314  // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
316 
317  QPainter *painter = context.painter();
318 
321  QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
322 
323  QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
324  visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
325 
326  // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
328 
329  // label blocking regions work by "chopping away" those regions from the permissible labeling area
330  const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
331  for ( const QgsLabelBlockingRegion &region : blockingRegions )
332  {
333  mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
334  }
335 
337  {
338  // draw map boundary
339  QgsFeature f;
340  f.setGeometry( mapBoundaryGeom );
341  QVariantMap properties;
342  properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
343  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
344  properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "#0000ff" ) );
345  properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
346  properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
347  std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
348  boundarySymbol->startRender( context );
349  boundarySymbol->renderFeature( f, context );
350  boundarySymbol->stopRender( context );
351  }
352 
353  if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
354  {
355  //PAL features are prerotated, so extent also needs to be unrotated
357  // yes - this is rotated in the opposite direction... phew, this is confusing!
358  mapBoundaryGeom.rotate( mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
359  }
360 
361  QgsRectangle extent = extentGeom.boundingBox();
362 
363  mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
364 
365  QElapsedTimer t;
366  t.start();
367 
368  // do the labeling itself
369  try
370  {
371  mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
372  }
373  catch ( std::exception &e )
374  {
375  Q_UNUSED( e )
376  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
377  return;
378  }
379 
380  if ( context.renderingStopped() )
381  {
382  return; // it has been canceled
383  }
384 
385 #if 1 // XXX strk
386  // features are pre-rotated but not scaled/translated,
387  // so we only disable rotation here. Ideally, they'd be
388  // also pre-scaled/translated, as suggested here:
389  // https://github.com/qgis/QGIS/issues/20071
391  xform.setMapRotation( 0, 0, 0 );
392 #else
393  const QgsMapToPixel &xform = mMapSettings->mapToPixel();
394 #endif
395 
396  // draw rectangles with all candidates
397  // this is done before actual solution of the problem
398  // before number of candidates gets reduced
399  // TODO mCandidates.clear();
401  {
402  painter->setBrush( Qt::NoBrush );
403  for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
404  {
405  for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
406  {
407  pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
408 
409  QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
410  }
411  }
412  }
413 
414  // find the solution
415  mLabels = mPal->solveProblem( mProblem.get(), context,
418 
419  // sort labels
420  std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mMapSettings ) );
421 
422  QgsDebugMsgLevel( QStringLiteral( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( mLabels.size() ), 4 );
423 }
424 
425 void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
426 {
427  QElapsedTimer t;
428  t.start();
429 
431 
432  context.setPainterFlagsUsingContext();
433  QPainter *painter = context.painter();
434 
435  // prepare for rendering
436  for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
437  {
438  if ( !layerId.isEmpty() && provider->layerId() != layerId )
439  continue;
440 
441  // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
442  // only contains generic scopes
443  QgsExpressionContextScopePopper popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
444 
445  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
446  provider->startRender( context );
447  }
448 
450  std::unique_ptr< QgsExpressionContextScopePopper > symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
451 
452  // draw label backgrounds
453  for ( pal::LabelPosition *label : std::as_const( mLabels ) )
454  {
455  if ( context.renderingStopped() )
456  break;
457 
458  QgsLabelFeature *lf = label->getFeaturePart()->feature();
459  if ( !lf )
460  {
461  continue;
462  }
463 
464  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
465  continue;
466 
467  context.expressionContext().setFeature( lf->feature() );
468  context.expressionContext().setFields( lf->feature().fields() );
469 
470  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
471 
472  if ( lf->symbol() )
473  {
474  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
475  }
476  lf->provider()->drawLabelBackground( context, label );
477  }
478 
479  // draw the labels
480  for ( pal::LabelPosition *label : std::as_const( mLabels ) )
481  {
482  if ( context.renderingStopped() )
483  break;
484 
485  QgsLabelFeature *lf = label->getFeaturePart()->feature();
486  if ( !lf )
487  {
488  continue;
489  }
490 
491  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
492  continue;
493 
494  context.expressionContext().setFeature( lf->feature() );
495  context.expressionContext().setFields( lf->feature().fields() );
496 
497  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
498  if ( lf->symbol() )
499  {
500  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
501  }
502  lf->provider()->drawLabel( context, label );
503  // finished with symbol -- we can't keep it around after this, it may be deleted
504  lf->setSymbol( nullptr );
505  }
506 
507  // draw unplaced labels. These are always rendered on top
509  {
510  for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
511  {
512  if ( context.renderingStopped() )
513  break;
514  QgsLabelFeature *lf = label->getFeaturePart()->feature();
515  if ( !lf )
516  {
517  continue;
518  }
519 
520  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
521  continue;
522 
523  context.expressionContext().setFeature( lf->feature() );
524  context.expressionContext().setFields( lf->feature().fields() );
525 
526  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
527  if ( lf->symbol() )
528  {
529  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
530  }
531  lf->provider()->drawUnplacedLabel( context, label );
532  // finished with symbol -- we can't keep it around after this, it may be deleted
533  lf->setSymbol( nullptr );
534  }
535  }
536 
537  symbolScopePopper.reset();
538 
539  // cleanup
540  for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
541  {
542  if ( !layerId.isEmpty() && provider->layerId() != layerId )
543  continue;
544 
545  provider->stopRender( context );
546  }
547 
548  // Reset composition mode for further drawing operations
549  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
550 
551  QgsDebugMsgLevel( QStringLiteral( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
552 }
553 
555 {
556  mUnlabeled.clear();
557  mLabels.clear();
558  mProblem.reset();
559  mPal.reset();
560 }
561 
563 {
564  return mResults.release();
565 }
566 
567 
568 //
569 // QgsDefaultLabelingEngine
570 //
571 
574 {
575 
576 }
577 
579 {
580  registerLabels( context );
581  if ( context.renderingStopped() )
582  {
583  cleanup();
584  return; // it has been canceled
585  }
586 
587  solve( context );
588  if ( context.renderingStopped() )
589  {
590  cleanup();
591  return;
592  }
593 
594  drawLabels( context );
595  cleanup();
596 }
597 
598 
599 //
600 // QgsStagedRenderLabelingEngine
601 //
602 
605 {
606 
607 }
608 
610 {
611  registerLabels( context );
612  if ( context.renderingStopped() )
613  {
614  cleanup();
615  return; // it has been canceled
616  }
617 
618  solve( context );
619  if ( context.renderingStopped() )
620  {
621  cleanup();
622  return;
623  }
624 }
625 
626 
628 {
629  drawLabels( context, layerId );
630 }
631 
633 {
634  cleanup();
635 }
636 
637 
639 
641 {
642  return mLayer ? mLayer->provider() : nullptr;
643 
644 }
645 
647  : mLayerId( layer ? layer->id() : QString() )
648  , mLayer( layer )
649  , mProviderId( providerId )
650  , mFlags( DrawLabels )
651  , mPlacement( QgsPalLayerSettings::AroundPoint )
652  , mPriority( 0.5 )
653  , mUpsidedownLabels( QgsPalLayerSettings::Upright )
654 {
655  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
656  {
657  mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
658  if ( const QgsFeatureRenderer *renderer = vl->renderer() )
659  mLayerReferenceScale = renderer->referenceScale();
660  }
661 }
662 
664 {
665 
666 }
667 
669 {
670 
671 }
672 
674 {
675  const auto subproviders = subProviders();
676  for ( QgsAbstractLabelProvider *subProvider : subproviders )
677  {
678  subProvider->startRender( context );
679  }
680 }
681 
683 {
684  const auto subproviders = subProviders();
685  for ( QgsAbstractLabelProvider *subProvider : subproviders )
686  {
687  subProvider->stopRender( context );
688  }
689 }
690 
692 {
693  return mLayerExpressionContextScope.get();
694 }
695 
696 //
697 // QgsLabelingUtils
698 //
699 
700 QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<QgsPalLayerSettings::PredefinedPointPosition> &positions )
701 {
702  QStringList predefinedOrderString;
703  const auto constPositions = positions;
704  for ( QgsPalLayerSettings::PredefinedPointPosition position : constPositions )
705  {
706  switch ( position )
707  {
709  predefinedOrderString << QStringLiteral( "TL" );
710  break;
712  predefinedOrderString << QStringLiteral( "TSL" );
713  break;
715  predefinedOrderString << QStringLiteral( "T" );
716  break;
718  predefinedOrderString << QStringLiteral( "TSR" );
719  break;
721  predefinedOrderString << QStringLiteral( "TR" );
722  break;
724  predefinedOrderString << QStringLiteral( "L" );
725  break;
727  predefinedOrderString << QStringLiteral( "R" );
728  break;
730  predefinedOrderString << QStringLiteral( "BL" );
731  break;
733  predefinedOrderString << QStringLiteral( "BSL" );
734  break;
736  predefinedOrderString << QStringLiteral( "B" );
737  break;
739  predefinedOrderString << QStringLiteral( "BSR" );
740  break;
742  predefinedOrderString << QStringLiteral( "BR" );
743  break;
744  }
745  }
746  return predefinedOrderString.join( ',' );
747 }
748 
749 QVector<QgsPalLayerSettings::PredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
750 {
751  QVector<QgsPalLayerSettings::PredefinedPointPosition> result;
752  const QStringList predefinedOrderList = positionString.split( ',' );
753  result.reserve( predefinedOrderList.size() );
754  for ( const QString &position : predefinedOrderList )
755  {
756  QString cleaned = position.trimmed().toUpper();
757  if ( cleaned == QLatin1String( "TL" ) )
759  else if ( cleaned == QLatin1String( "TSL" ) )
761  else if ( cleaned == QLatin1String( "T" ) )
763  else if ( cleaned == QLatin1String( "TSR" ) )
765  else if ( cleaned == QLatin1String( "TR" ) )
767  else if ( cleaned == QLatin1String( "L" ) )
769  else if ( cleaned == QLatin1String( "R" ) )
771  else if ( cleaned == QLatin1String( "BL" ) )
773  else if ( cleaned == QLatin1String( "BSL" ) )
775  else if ( cleaned == QLatin1String( "B" ) )
777  else if ( cleaned == QLatin1String( "BSR" ) )
779  else if ( cleaned == QLatin1String( "BR" ) )
781  }
782  return result;
783 }
784 
785 QString QgsLabelingUtils::encodeLinePlacementFlags( QgsLabeling::LinePlacementFlags flags )
786 {
787  QStringList parts;
788  if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
789  parts << QStringLiteral( "OL" );
790  if ( flags & QgsLabeling::LinePlacementFlag::AboveLine )
791  parts << QStringLiteral( "AL" );
792  if ( flags & QgsLabeling::LinePlacementFlag::BelowLine )
793  parts << QStringLiteral( "BL" );
794  if ( !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
795  parts << QStringLiteral( "LO" );
796  return parts.join( ',' );
797 }
798 
799 QgsLabeling::LinePlacementFlags QgsLabelingUtils::decodeLinePlacementFlags( const QString &string )
800 {
801  QgsLabeling::LinePlacementFlags flags = QgsLabeling::LinePlacementFlags();
802  const QStringList flagList = string.split( ',' );
803  bool foundLineOrientationFlag = false;
804  for ( const QString &flag : flagList )
805  {
806  QString cleaned = flag.trimmed().toUpper();
807  if ( cleaned == QLatin1String( "OL" ) )
808  flags |= QgsLabeling::LinePlacementFlag::OnLine;
809  else if ( cleaned == QLatin1String( "AL" ) )
810  flags |= QgsLabeling::LinePlacementFlag::AboveLine;
811  else if ( cleaned == QLatin1String( "BL" ) )
812  flags |= QgsLabeling::LinePlacementFlag::BelowLine;
813  else if ( cleaned == QLatin1String( "LO" ) )
814  foundLineOrientationFlag = true;
815  }
816  if ( !foundLineOrientationFlag )
817  flags |= QgsLabeling::LinePlacementFlag::MapOrientation;
818  return flags;
819 }
The QgsAbstractLabelProvider class is an interface class.
QgsExpressionContextScope * layerExpressionContextScope() const
Returns the expression context scope created from the layer associated with this provider.
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.
QgsMapLayer * layer() const
Returns the associated layer, or nullptr if no layer is associated with the provider.
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.
QgsPalLayerSettings::Placement placement() const
What placement strategy to use for the labels.
virtual QList< QgsLabelFeature * > labelFeatures(QgsRenderContext &context)=0
Returns list of label features (they are owned by the provider and thus deleted on its destruction)
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.
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.
@ DrawAllLabels
Whether all features will be labelled even though overlaps occur.
@ 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.
QgsPalLayerSettings::UpsideDownLabels upsidedownLabels() const
How to handle labels that would be upside down.
virtual QList< QgsAbstractLabelProvider * > subProviders()
Returns list of child providers - useful if the provider needs to put labels into more layers with di...
QString layerId() const
Returns ID of associated layer, or empty string if no layer is associated with the provider.
QgsAbstractLabelProvider(QgsMapLayer *layer, const QString &providerId=QString())
Construct the provider with default values.
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.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsFields fields
Definition: qgsfeature.h:66
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:163
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
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.
Definition: qgsgeometry.h:125
QgsGeometry difference(const QgsGeometry &geometry) 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.
Q_GADGET bool isNull
Definition: qgsgeometry.h:127
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
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.
const QgsSymbol * symbol()
Returns the feature symbol associated with this label.
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.
Helper class for sorting labels into correct draw order.
QgsLabelSorter(const QgsMapSettings &mapSettings)
bool operator()(pal::LabelPosition *lp1, pal::LabelPosition *lp2) const
QgsFeedback subclass for granular reporting of labeling engine progress.
Stores global configuration for labeling engine.
bool testFlag(Flag f) const
Test whether a particular flag is enabled.
Flags flags() const
Gets flags of the labeling engine.
@ 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.
@ 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.
@ DrawCandidates
Whether to draw rectangles of generated candidates (good for debugging)
PlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
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.
void solve(QgsRenderContext &context)
Solves the label problem.
QList< pal::LabelPosition * > mUnlabeled
std::unique_ptr< pal::Problem > mProblem
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
void addProvider(QgsAbstractLabelProvider *provider)
Add provider of label features. Takes ownership of the provider.
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)
QgsLabelingEngine()
Construct the labeling engine with default settings.
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 QVector< QgsPalLayerSettings::PredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
static QString encodeLinePlacementFlags(QgsLabeling::LinePlacementFlags flags)
Encodes line placement flags to a string.
static QString encodePredefinedPositionOrder(const QVector< QgsPalLayerSettings::PredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
static QgsLabeling::LinePlacementFlags decodeLinePlacementFlags(const QString &string)
Decodes a string to set of line placement flags.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
The QgsMapSettings class contains configuration for rendering of the map.
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.
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...
const QgsMapToPixel & mapToPixel() const
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns the global configuration of the labeling engine.
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.
Definition: qgsmaptopixel.h:39
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)
Contains settings for how a map layer will be labeled.
@ ShowDefined
Show upside down when rotation is layer- or data-defined.
@ Upright
Upside-down labels (90 <= angle < 270) are shown upright.
@ ShowAll
Show upside down for all labels, including dynamic ones.
PredefinedPointPosition
Positions for labels when using the QgsPalLabeling::OrderedPositionsAroundPoint placement mode.
@ BottomSlightlyLeft
Label below point, slightly left of center.
@ BottomMiddle
Label directly below point.
@ BottomSlightlyRight
Label below point, slightly right of center.
@ MiddleLeft
Label on left of point.
@ TopSlightlyRight
Label on top of point, slightly right of center.
@ TopSlightlyLeft
Label on top of point, slightly left of center.
@ MiddleRight
Label on right of point.
@ TopMiddle
Label directly above point.
@ BottomRight
Label on bottom right of point.
@ BottomLeft
Label on bottom-left of point.
@ TopRight
Label on top-right of point.
@ TopLeft
Label on top-left of point.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
void grow(double delta)
Grows the rectangle in place by the specified amount.
Definition: qgsrectangle.h:296
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:251
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
void setPainterFlagsUsingContext(QPainter *painter=nullptr) const
Sets relevant flags on a destination painter, using the flags and settings currently defined for the ...
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
double convertToMapUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to map units.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
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...
@ RenderMillimeters
Millimeters.
Definition: qgsunittypes.h:169
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.
Definition: labelposition.h:56
FeaturePart * getFeaturePart() const
Returns the feature corresponding to this labelposition.
A set of features which influence the labeling process.
Definition: layer.h:62
void setUpsidedownLabels(UpsideDownLabels ud)
Sets how upside down labels will be handled within the layer.
Definition: layer.h:272
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:237
void setMergeConnectedLines(bool merge)
Sets whether connected lines should be merged before labeling.
Definition: layer.h:259
QgsAbstractLabelProvider * provider() const
Returns pointer to the associated provider.
Definition: layer.h:166
UpsideDownLabels
Definition: layer.h:72
@ ShowAll
Definition: layer.h:75
@ Upright
Definition: layer.h:73
@ ShowDefined
Definition: layer.h:74
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:287
Main Pal labeling class.
Definition: pal.h:80
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, QgsPalLayerSettings::Placement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll=false)
add a new layer
Definition: pal.cpp:80
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1578
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39