QGIS API Documentation  3.27.0-Master (0e23467727)
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 
217  // set whether adjacent lines should be merged
219 
220  // set obstacle type
221  l->setObstacleType( provider->obstacleType() );
222 
223  // set whether location of centroid must be inside of polygons
225 
226  // set how to show upside-down labels
227  l->setUpsidedownLabels( provider->upsidedownLabels() );
228 
229  const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
230 
231  for ( QgsLabelFeature *feature : features )
232  {
233  try
234  {
235  l->registerFeature( feature );
236  }
237  catch ( std::exception &e )
238  {
239  Q_UNUSED( e )
240  QgsDebugMsgLevel( QStringLiteral( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
241  continue;
242  }
243  }
244 
245  // any sub-providers?
246  const auto subproviders = provider->subProviders();
247  for ( QgsAbstractLabelProvider *subProvider : subproviders )
248  {
249  mSubProviders << subProvider;
250  processProvider( subProvider, context, p );
251  }
252 }
253 
255 {
256  QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
257 
258  if ( feedback )
259  feedback->emit labelRegistrationAboutToBegin();
260 
262 
263  mPal = std::make_unique< pal::Pal >();
264 
265  mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ) );
266  mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ), 2 ) );
267 
268  mPal->setShowPartialLabels( settings.testFlag( QgsLabelingEngineSettings::UsePartialCandidates ) );
269  mPal->setPlacementVersion( settings.placementVersion() );
270 
271  // for each provider: get labels and register them in PAL
272  const double step = !mProviders.empty() ? 100.0 / mProviders.size() : 1;
273  int index = 0;
274  for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
275  {
276  if ( feedback )
277  {
278  feedback->emit providerRegistrationAboutToBegin( provider );
279  feedback->setProgress( index * step );
280  }
281  index++;
282  std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
283  if ( provider->layerExpressionContextScope() )
284  {
285  layerScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) );
286  }
287  processProvider( provider, context, *mPal );
288  if ( feedback )
289  feedback->emit providerRegistrationFinished( provider );
290  }
291  if ( feedback )
292  feedback->emit labelRegistrationFinished();
293 }
294 
296 {
297  Q_ASSERT( mPal.get() );
298 
299  // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
301 
302  QPainter *painter = context.painter();
303 
306  QgsGeometry extentGeom = QgsGeometry::fromRect( r1 );
307 
308  QPolygonF visiblePoly = mMapSettings.visiblePolygonWithBuffer();
309  visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
310 
311  // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
313 
314  // label blocking regions work by "chopping away" those regions from the permissible labeling area
315  const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
316  for ( const QgsLabelBlockingRegion &region : blockingRegions )
317  {
318  mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
319  }
320 
322  {
323  // draw map boundary
324  QgsFeature f;
325  f.setGeometry( mapBoundaryGeom );
326  QVariantMap properties;
327  properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
328  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
329  properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "#0000ff" ) );
330  properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
331  properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
332  std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
333  boundarySymbol->startRender( context );
334  boundarySymbol->renderFeature( f, context );
335  boundarySymbol->stopRender( context );
336  }
337 
338  if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
339  {
340  //PAL features are prerotated, so extent also needs to be unrotated
342  // yes - this is rotated in the opposite direction... phew, this is confusing!
343  mapBoundaryGeom.rotate( mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
344  }
345 
346  QgsRectangle extent = extentGeom.boundingBox();
347 
348  mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
349 
350  QElapsedTimer t;
351  t.start();
352 
353  // do the labeling itself
354  try
355  {
356  mProblem = mPal->extractProblem( extent, mapBoundaryGeom, context );
357  }
358  catch ( std::exception &e )
359  {
360  Q_UNUSED( e )
361  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
362  return;
363  }
364 
365  if ( context.renderingStopped() )
366  {
367  return; // it has been canceled
368  }
369 
370 #if 1 // XXX strk
371  // features are pre-rotated but not scaled/translated,
372  // so we only disable rotation here. Ideally, they'd be
373  // also pre-scaled/translated, as suggested here:
374  // https://github.com/qgis/QGIS/issues/20071
376  xform.setMapRotation( 0, 0, 0 );
377 #else
378  const QgsMapToPixel &xform = mMapSettings->mapToPixel();
379 #endif
380 
381  // draw rectangles with all candidates
382  // this is done before actual solution of the problem
383  // before number of candidates gets reduced
384  // TODO mCandidates.clear();
386  {
387  painter->setBrush( Qt::NoBrush );
388  for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
389  {
390  for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
391  {
392  pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
393 
394  QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
395  }
396  }
397  }
398 
399  // find the solution
400  mLabels = mPal->solveProblem( mProblem.get(), context,
403 
404  // sort labels
405  std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mMapSettings ) );
406 
407  QgsDebugMsgLevel( QStringLiteral( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( mLabels.size() ), 4 );
408 }
409 
410 void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
411 {
412  QElapsedTimer t;
413  t.start();
414 
416 
417  context.setPainterFlagsUsingContext();
418  QPainter *painter = context.painter();
419 
420  // prepare for rendering
421  for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
422  {
423  if ( !layerId.isEmpty() && provider->layerId() != layerId )
424  continue;
425 
426  // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
427  // only contains generic scopes
428  QgsExpressionContextScopePopper popper( context.expressionContext(), provider->layerExpressionContextScope() ? new QgsExpressionContextScope( *provider->layerExpressionContextScope() ) : new QgsExpressionContextScope() );
429 
430  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, provider->layerReferenceScale() );
431  provider->startRender( context );
432  }
433 
435  std::unique_ptr< QgsExpressionContextScopePopper > symbolScopePopper = std::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
436 
437  // draw label backgrounds
438  for ( pal::LabelPosition *label : std::as_const( mLabels ) )
439  {
440  if ( context.renderingStopped() )
441  break;
442 
443  QgsLabelFeature *lf = label->getFeaturePart()->feature();
444  if ( !lf )
445  {
446  continue;
447  }
448 
449  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
450  continue;
451 
452  context.expressionContext().setFeature( lf->feature() );
453  context.expressionContext().setFields( lf->feature().fields() );
454 
455  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
456 
457  if ( lf->symbol() )
458  {
459  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
460  }
461  lf->provider()->drawLabelBackground( context, label );
462  }
463 
464  // draw the labels
465  for ( pal::LabelPosition *label : std::as_const( mLabels ) )
466  {
467  if ( context.renderingStopped() )
468  break;
469 
470  QgsLabelFeature *lf = label->getFeaturePart()->feature();
471  if ( !lf )
472  {
473  continue;
474  }
475 
476  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
477  continue;
478 
479  context.expressionContext().setFeature( lf->feature() );
480  context.expressionContext().setFields( lf->feature().fields() );
481 
482  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
483  if ( lf->symbol() )
484  {
485  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
486  }
487  lf->provider()->drawLabel( context, label );
488  // finished with symbol -- we can't keep it around after this, it may be deleted
489  lf->setSymbol( nullptr );
490  }
491 
492  // draw unplaced labels. These are always rendered on top
494  {
495  for ( pal::LabelPosition *label : std::as_const( mUnlabeled ) )
496  {
497  if ( context.renderingStopped() )
498  break;
499  QgsLabelFeature *lf = label->getFeaturePart()->feature();
500  if ( !lf )
501  {
502  continue;
503  }
504 
505  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
506  continue;
507 
508  context.expressionContext().setFeature( lf->feature() );
509  context.expressionContext().setFields( lf->feature().fields() );
510 
511  QgsScopedRenderContextReferenceScaleOverride referenceScaleOverride( context, lf->provider()->layerReferenceScale() );
512  if ( lf->symbol() )
513  {
514  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
515  }
516  lf->provider()->drawUnplacedLabel( context, label );
517  // finished with symbol -- we can't keep it around after this, it may be deleted
518  lf->setSymbol( nullptr );
519  }
520  }
521 
522  symbolScopePopper.reset();
523 
524  // cleanup
525  for ( QgsAbstractLabelProvider *provider : std::as_const( mProviders ) )
526  {
527  if ( !layerId.isEmpty() && provider->layerId() != layerId )
528  continue;
529 
530  provider->stopRender( context );
531  }
532 
533  // Reset composition mode for further drawing operations
534  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
535 
536  QgsDebugMsgLevel( QStringLiteral( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
537 }
538 
540 {
541  mUnlabeled.clear();
542  mLabels.clear();
543  mProblem.reset();
544  mPal.reset();
545 }
546 
548 {
549  return mResults.release();
550 }
551 
552 
553 //
554 // QgsDefaultLabelingEngine
555 //
556 
559 {
560 
561 }
562 
564 {
565  registerLabels( context );
566  if ( context.renderingStopped() )
567  {
568  cleanup();
569  return; // it has been canceled
570  }
571 
572  solve( context );
573  if ( context.renderingStopped() )
574  {
575  cleanup();
576  return;
577  }
578 
579  drawLabels( context );
580  cleanup();
581 }
582 
583 
584 //
585 // QgsStagedRenderLabelingEngine
586 //
587 
590 {
591 
592 }
593 
595 {
596  registerLabels( context );
597  if ( context.renderingStopped() )
598  {
599  cleanup();
600  return; // it has been canceled
601  }
602 
603  solve( context );
604  if ( context.renderingStopped() )
605  {
606  cleanup();
607  return;
608  }
609 }
610 
611 
613 {
614  drawLabels( context, layerId );
615 }
616 
618 {
619  cleanup();
620 }
621 
622 
624 
626 {
627  return mLayer ? mLayer->provider() : nullptr;
628 
629 }
630 
632  : mLayerId( layer ? layer->id() : QString() )
633  , mLayer( layer )
634  , mProviderId( providerId )
635 {
636  if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
637  {
638  mLayerExpressionContextScope.reset( vl->createExpressionContextScope() );
639  if ( const QgsFeatureRenderer *renderer = vl->renderer() )
640  mLayerReferenceScale = renderer->referenceScale();
641  }
642 }
643 
645 {
646 
647 }
648 
650 {
651 
652 }
653 
655 {
656  const auto subproviders = subProviders();
657  for ( QgsAbstractLabelProvider *subProvider : subproviders )
658  {
659  subProvider->startRender( context );
660  }
661 }
662 
664 {
665  const auto subproviders = subProviders();
666  for ( QgsAbstractLabelProvider *subProvider : subproviders )
667  {
668  subProvider->stopRender( context );
669  }
670 }
671 
673 {
674  return mLayerExpressionContextScope.get();
675 }
676 
677 //
678 // QgsLabelingUtils
679 //
680 
681 QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<Qgis::LabelPredefinedPointPosition> &positions )
682 {
683  QStringList predefinedOrderString;
684  const auto constPositions = positions;
685  for ( Qgis::LabelPredefinedPointPosition position : constPositions )
686  {
687  switch ( position )
688  {
690  predefinedOrderString << QStringLiteral( "TL" );
691  break;
693  predefinedOrderString << QStringLiteral( "TSL" );
694  break;
696  predefinedOrderString << QStringLiteral( "T" );
697  break;
699  predefinedOrderString << QStringLiteral( "TSR" );
700  break;
702  predefinedOrderString << QStringLiteral( "TR" );
703  break;
705  predefinedOrderString << QStringLiteral( "L" );
706  break;
708  predefinedOrderString << QStringLiteral( "R" );
709  break;
711  predefinedOrderString << QStringLiteral( "BL" );
712  break;
714  predefinedOrderString << QStringLiteral( "BSL" );
715  break;
717  predefinedOrderString << QStringLiteral( "B" );
718  break;
720  predefinedOrderString << QStringLiteral( "BSR" );
721  break;
723  predefinedOrderString << QStringLiteral( "BR" );
724  break;
725  }
726  }
727  return predefinedOrderString.join( ',' );
728 }
729 
730 QVector<Qgis::LabelPredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
731 {
732  QVector<Qgis::LabelPredefinedPointPosition> result;
733  const QStringList predefinedOrderList = positionString.split( ',' );
734  result.reserve( predefinedOrderList.size() );
735  for ( const QString &position : predefinedOrderList )
736  {
737  QString cleaned = position.trimmed().toUpper();
738  if ( cleaned == QLatin1String( "TL" ) )
740  else if ( cleaned == QLatin1String( "TSL" ) )
742  else if ( cleaned == QLatin1String( "T" ) )
744  else if ( cleaned == QLatin1String( "TSR" ) )
746  else if ( cleaned == QLatin1String( "TR" ) )
748  else if ( cleaned == QLatin1String( "L" ) )
750  else if ( cleaned == QLatin1String( "R" ) )
752  else if ( cleaned == QLatin1String( "BL" ) )
754  else if ( cleaned == QLatin1String( "BSL" ) )
756  else if ( cleaned == QLatin1String( "B" ) )
758  else if ( cleaned == QLatin1String( "BSR" ) )
760  else if ( cleaned == QLatin1String( "BR" ) )
762  }
763  return result;
764 }
765 
766 QString QgsLabelingUtils::encodeLinePlacementFlags( QgsLabeling::LinePlacementFlags flags )
767 {
768  QStringList parts;
769  if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
770  parts << QStringLiteral( "OL" );
771  if ( flags & QgsLabeling::LinePlacementFlag::AboveLine )
772  parts << QStringLiteral( "AL" );
773  if ( flags & QgsLabeling::LinePlacementFlag::BelowLine )
774  parts << QStringLiteral( "BL" );
775  if ( !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
776  parts << QStringLiteral( "LO" );
777  return parts.join( ',' );
778 }
779 
780 QgsLabeling::LinePlacementFlags QgsLabelingUtils::decodeLinePlacementFlags( const QString &string )
781 {
782  QgsLabeling::LinePlacementFlags flags = QgsLabeling::LinePlacementFlags();
783  const QStringList flagList = string.split( ',' );
784  bool foundLineOrientationFlag = false;
785  for ( const QString &flag : flagList )
786  {
787  QString cleaned = flag.trimmed().toUpper();
788  if ( cleaned == QLatin1String( "OL" ) )
789  flags |= QgsLabeling::LinePlacementFlag::OnLine;
790  else if ( cleaned == QLatin1String( "AL" ) )
791  flags |= QgsLabeling::LinePlacementFlag::AboveLine;
792  else if ( cleaned == QLatin1String( "BL" ) )
793  flags |= QgsLabeling::LinePlacementFlag::BelowLine;
794  else if ( cleaned == QLatin1String( "LO" ) )
795  foundLineOrientationFlag = true;
796  }
797  if ( !foundLineOrientationFlag )
798  flags |= QgsLabeling::LinePlacementFlag::MapOrientation;
799  return flags;
800 }
LabelPredefinedPointPosition
Positions for labels when using the Qgis::LabelPlacement::OrderedPositionsAroundPoint placement mode.
Definition: qgis.h:583
@ 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 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.
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.
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.
@ 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.
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.
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.
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.
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.
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 QString encodePredefinedPositionOrder(const QVector< Qgis::LabelPredefinedPointPosition > &positions)
Encodes an ordered list of predefined point label positions to a string.
static QString encodeLinePlacementFlags(QgsLabeling::LinePlacementFlags flags)
Encodes line placement flags to a string.
static QVector< Qgis::LabelPredefinedPointPosition > decodePredefinedPositionOrder(const QString &positionString)
Decodes a string to an ordered list of predefined point label positions.
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)
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: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:80
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
QgsAbstractLabelProvider * provider() const
Returns pointer to the associated provider.
Definition: layer.h:157
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:80
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
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:2260
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39