QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 
32 // helper function for checking for job cancellation within PAL
33 static bool _palIsCanceled( void *ctx )
34 {
35  return ( reinterpret_cast< QgsRenderContext * >( ctx ) )->renderingStopped();
36 }
37 
44 {
45  public:
46 
47  explicit QgsLabelSorter( const QgsMapSettings &mapSettings )
48  : mMapSettings( mapSettings )
49  {}
50 
52  {
53  QgsLabelFeature *lf1 = lp1->getFeaturePart()->feature();
54  QgsLabelFeature *lf2 = lp2->getFeaturePart()->feature();
55 
56  if ( !qgsDoubleNear( lf1->zIndex(), lf2->zIndex() ) )
57  return lf1->zIndex() < lf2->zIndex();
58 
59  //equal z-index, so fallback to respecting layer render order
60  QStringList layerIds = mMapSettings.layerIds();
61  int layer1Pos = layerIds.indexOf( lf1->provider()->layerId() );
62  int layer2Pos = layerIds.indexOf( lf2->provider()->layerId() );
63  if ( layer1Pos != layer2Pos && layer1Pos >= 0 && layer2Pos >= 0 )
64  return layer1Pos > layer2Pos; //higher positions are rendered first
65 
66  //same layer, so render larger labels first
67  return lf1->size().width() * lf1->size().height() > lf2->size().width() * lf2->size().height();
68  }
69 
70  private:
71 
72  const QgsMapSettings &mMapSettings;
73 };
74 
75 //
76 // QgsLabelingEngine
77 //
78 
80  : mResults( new QgsLabelingResults )
81 {}
82 
84 {
85  qDeleteAll( mProviders );
86  qDeleteAll( mSubProviders );
87 }
88 
90 {
92  if ( mResults )
93  mResults->setMapSettings( mapSettings );
94 }
95 
96 QList< QgsMapLayer * > QgsLabelingEngine::participatingLayers() const
97 {
98  QList< QgsMapLayer * > layers;
99 
100  // try to return layers sorted in the desired z order for rendering
101  QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
102  std::sort( providersByZ.begin(), providersByZ.end(),
103  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
104  {
105  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
106  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
107 
108  if ( providerA && providerB )
109  {
110  return providerA->settings().zIndex < providerB->settings().zIndex ;
111  }
112  return false;
113  } );
114 
115  QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
116  std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
117  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
118  {
119  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
120  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
121 
122  if ( providerA && providerB )
123  {
124  return providerA->settings().zIndex < providerB->settings().zIndex ;
125  }
126  return false;
127  } );
128 
129  for ( QgsAbstractLabelProvider *provider : qgis::as_const( providersByZ ) )
130  {
131  if ( provider->layer() && !layers.contains( provider->layer() ) )
132  layers << provider->layer();
133  }
134  for ( QgsAbstractLabelProvider *provider : qgis::as_const( subProvidersByZ ) )
135  {
136  if ( provider->layer() && !layers.contains( provider->layer() ) )
137  layers << provider->layer();
138  }
139  return layers;
140 }
141 
143 {
144  QStringList layers;
145 
146  // try to return layers sorted in the desired z order for rendering
147  QList< QgsAbstractLabelProvider * > providersByZ = mProviders;
148  std::sort( providersByZ.begin(), providersByZ.end(),
149  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
150  {
151  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
152  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
153 
154  if ( providerA && providerB )
155  {
156  return providerA->settings().zIndex < providerB->settings().zIndex ;
157  }
158  return false;
159  } );
160 
161  QList< QgsAbstractLabelProvider * > subProvidersByZ = mSubProviders;
162  std::sort( subProvidersByZ.begin(), subProvidersByZ.end(),
163  []( const QgsAbstractLabelProvider * a, const QgsAbstractLabelProvider * b ) -> bool
164  {
165  const QgsVectorLayerLabelProvider *providerA = dynamic_cast<const QgsVectorLayerLabelProvider *>( a );
166  const QgsVectorLayerLabelProvider *providerB = dynamic_cast<const QgsVectorLayerLabelProvider *>( b );
167 
168  if ( providerA && providerB )
169  {
170  return providerA->settings().zIndex < providerB->settings().zIndex ;
171  }
172  return false;
173  } );
174 
175  for ( QgsAbstractLabelProvider *provider : qgis::as_const( providersByZ ) )
176  {
177  if ( !layers.contains( provider->layerId() ) )
178  layers << provider->layerId();
179  }
180  for ( QgsAbstractLabelProvider *provider : qgis::as_const( subProvidersByZ ) )
181  {
182  if ( !layers.contains( provider->layerId() ) )
183  layers << provider->layerId();
184  }
185  return layers;
186 }
187 
189 {
190  provider->setEngine( this );
191  mProviders << provider;
192 }
193 
195 {
196  int idx = mProviders.indexOf( provider );
197  if ( idx >= 0 )
198  {
199  delete mProviders.takeAt( idx );
200  }
201 }
202 
204 {
205  QgsAbstractLabelProvider::Flags flags = provider->flags();
206 
207  // create the pal layer
208  pal::Layer *l = p.addLayer( provider,
209  provider->name(),
210  provider->placement(),
211  provider->priority(),
212  true,
213  flags.testFlag( QgsAbstractLabelProvider::DrawLabels ),
214  flags.testFlag( QgsAbstractLabelProvider::DrawAllLabels ) );
215 
216  // set whether adjacent lines should be merged
218 
219  // set obstacle type
220  l->setObstacleType( provider->obstacleType() );
221 
222  // set whether location of centroid must be inside of polygons
224 
225  // set how to show upside-down labels
227  switch ( provider->upsidedownLabels() )
228  {
230  upsdnlabels = pal::Layer::Upright;
231  break;
233  upsdnlabels = pal::Layer::ShowDefined;
234  break;
236  upsdnlabels = pal::Layer::ShowAll;
237  break;
238  }
239  l->setUpsidedownLabels( upsdnlabels );
240 
241 
242  const QList<QgsLabelFeature *> features = provider->labelFeatures( context );
243 
244  for ( QgsLabelFeature *feature : features )
245  {
246  try
247  {
248  l->registerFeature( feature );
249  }
250  catch ( std::exception &e )
251  {
252  Q_UNUSED( e )
253  QgsDebugMsgLevel( QStringLiteral( "Ignoring feature %1 due PAL exception:" ).arg( feature->id() ) + QString::fromLatin1( e.what() ), 4 );
254  continue;
255  }
256  }
257 
258  // any sub-providers?
259  const auto subproviders = provider->subProviders();
260  for ( QgsAbstractLabelProvider *subProvider : subproviders )
261  {
262  mSubProviders << subProvider;
263  processProvider( subProvider, context, p );
264  }
265 }
266 
268 {
270 
271  mPal = qgis::make_unique< pal::Pal >();
272 
273  mPal->setMaximumLineCandidatesPerMapUnit( settings.maximumLineCandidatesPerCm() / context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ) );
274  mPal->setMaximumPolygonCandidatesPerMapUnitSquared( settings.maximumPolygonCandidatesPerCmSquared() / std::pow( context.convertToMapUnits( 10, QgsUnitTypes::RenderMillimeters ), 2 ) );
275 
276  mPal->setShowPartialLabels( settings.testFlag( QgsLabelingEngineSettings::UsePartialCandidates ) );
277  mPal->setPlacementVersion( settings.placementVersion() );
278 
279  // for each provider: get labels and register them in PAL
280  for ( QgsAbstractLabelProvider *provider : qgis::as_const( mProviders ) )
281  {
282  std::unique_ptr< QgsExpressionContextScopePopper > layerScopePopper;
283  if ( QgsMapLayer *ml = provider->layer() )
284  {
285  layerScopePopper = qgis::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), QgsExpressionContextUtils::layerScope( ml ) );
286  }
287  processProvider( provider, context, *mPal );
288  }
289 }
290 
292 {
293  Q_ASSERT( mPal.get() );
294 
295  // NOW DO THE LAYOUT (from QgsPalLabeling::drawLabeling)
297 
298  QPainter *painter = context.painter();
299 
301  QPolygonF visiblePoly = mMapSettings.visiblePolygon();
302  visiblePoly.append( visiblePoly.at( 0 ) ); //close polygon
303 
304  // get map label boundary geometry - if one hasn't been explicitly set, we use the whole of the map's visible polygon
306 
307  // label blocking regions work by "chopping away" those regions from the permissible labeling area
308  const QList< QgsLabelBlockingRegion > blockingRegions = mMapSettings.labelBlockingRegions();
309  for ( const QgsLabelBlockingRegion &region : blockingRegions )
310  {
311  mapBoundaryGeom = mapBoundaryGeom.difference( region.geometry );
312  }
313 
315  {
316  // draw map boundary
317  QgsFeature f;
318  f.setGeometry( mapBoundaryGeom );
319  QVariantMap properties;
320  properties.insert( QStringLiteral( "style" ), QStringLiteral( "no" ) );
321  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "solid" ) );
322  properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "#0000ff" ) );
323  properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "0.3" ) );
324  properties.insert( QStringLiteral( "joinstyle" ), QStringLiteral( "miter" ) );
325  std::unique_ptr< QgsFillSymbol > boundarySymbol( QgsFillSymbol::createSimple( properties ) );
326  boundarySymbol->startRender( context );
327  boundarySymbol->renderFeature( f, context );
328  boundarySymbol->stopRender( context );
329  }
330 
331  if ( !qgsDoubleNear( mMapSettings.rotation(), 0.0 ) )
332  {
333  //PAL features are prerotated, so extent also needs to be unrotated
335  // yes - this is rotated in the opposite direction... phew, this is confusing!
336  mapBoundaryGeom.rotate( mMapSettings.rotation(), mMapSettings.visibleExtent().center() );
337  }
338 
339  QgsRectangle extent = extentGeom.boundingBox();
340 
341  mPal->registerCancellationCallback( &_palIsCanceled, reinterpret_cast< void * >( &context ) );
342 
343  QElapsedTimer t;
344  t.start();
345 
346  // do the labeling itself
347  try
348  {
349  mProblem = mPal->extractProblem( extent, mapBoundaryGeom );
350  }
351  catch ( std::exception &e )
352  {
353  Q_UNUSED( e )
354  QgsDebugMsgLevel( "PAL EXCEPTION :-( " + QString::fromLatin1( e.what() ), 4 );
355  return;
356  }
357 
358  if ( context.renderingStopped() )
359  {
360  return; // it has been canceled
361  }
362 
363 #if 1 // XXX strk
364  // features are pre-rotated but not scaled/translated,
365  // so we only disable rotation here. Ideally, they'd be
366  // also pre-scaled/translated, as suggested here:
367  // https://github.com/qgis/QGIS/issues/20071
369  xform.setMapRotation( 0, 0, 0 );
370 #else
371  const QgsMapToPixel &xform = mMapSettings->mapToPixel();
372 #endif
373 
374  // draw rectangles with all candidates
375  // this is done before actual solution of the problem
376  // before number of candidates gets reduced
377  // TODO mCandidates.clear();
379  {
380  painter->setBrush( Qt::NoBrush );
381  for ( int i = 0; i < static_cast< int >( mProblem->featureCount() ); i++ )
382  {
383  for ( int j = 0; j < mProblem->featureCandidateCount( i ); j++ )
384  {
385  pal::LabelPosition *lp = mProblem->featureCandidate( i, j );
386 
387  QgsPalLabeling::drawLabelCandidateRect( lp, painter, &xform );
388  }
389  }
390  }
391 
392  // find the solution
394 
395  // sort labels
396  std::sort( mLabels.begin(), mLabels.end(), QgsLabelSorter( mMapSettings ) );
397 
398  QgsDebugMsgLevel( QStringLiteral( "LABELING work: %1 ms ... labels# %2" ).arg( t.elapsed() ).arg( mLabels.size() ), 4 );
399 }
400 
401 void QgsLabelingEngine::drawLabels( QgsRenderContext &context, const QString &layerId )
402 {
403  QElapsedTimer t;
404  t.start();
405 
407 
408  context.setPainterFlagsUsingContext();
409  QPainter *painter = context.painter();
410 
411  // prepare for rendering
412  for ( QgsAbstractLabelProvider *provider : qgis::as_const( mProviders ) )
413  {
414  if ( !layerId.isEmpty() && provider->layerId() != layerId )
415  continue;
416 
417  // provider will require the correct layer scope for expression preparation - at this stage, the existing expression context
418  // only contains generic scopes
420  provider->startRender( context );
421  }
422 
424  std::unique_ptr< QgsExpressionContextScopePopper > symbolScopePopper = qgis::make_unique< QgsExpressionContextScopePopper >( context.expressionContext(), symbolScope );
425 
426  // draw label backgrounds
427  for ( pal::LabelPosition *label : qgis::as_const( mLabels ) )
428  {
429  if ( context.renderingStopped() )
430  break;
431 
432  QgsLabelFeature *lf = label->getFeaturePart()->feature();
433  if ( !lf )
434  {
435  continue;
436  }
437 
438  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
439  continue;
440 
441  context.expressionContext().setFeature( lf->feature() );
442  context.expressionContext().setFields( lf->feature().fields() );
443  if ( lf->symbol() )
444  {
445  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
446  }
447  lf->provider()->drawLabelBackground( context, label );
448  }
449 
450  // draw the labels
451  for ( pal::LabelPosition *label : qgis::as_const( mLabels ) )
452  {
453  if ( context.renderingStopped() )
454  break;
455 
456  QgsLabelFeature *lf = label->getFeaturePart()->feature();
457  if ( !lf )
458  {
459  continue;
460  }
461 
462  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
463  continue;
464 
465  context.expressionContext().setFeature( lf->feature() );
466  context.expressionContext().setFields( lf->feature().fields() );
467  if ( lf->symbol() )
468  {
469  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
470  }
471  lf->provider()->drawLabel( context, label );
472  // finished with symbol -- we can't keep it around after this, it may be deleted
473  lf->setSymbol( nullptr );
474  }
475 
476  // draw unplaced labels. These are always rendered on top
478  {
479  for ( pal::LabelPosition *label : qgis::as_const( mUnlabeled ) )
480  {
481  if ( context.renderingStopped() )
482  break;
483  QgsLabelFeature *lf = label->getFeaturePart()->feature();
484  if ( !lf )
485  {
486  continue;
487  }
488 
489  if ( !layerId.isEmpty() && lf->provider()->layerId() != layerId )
490  continue;
491 
492  context.expressionContext().setFeature( lf->feature() );
493  context.expressionContext().setFields( lf->feature().fields() );
494  if ( lf->symbol() )
495  {
496  symbolScope = QgsExpressionContextUtils::updateSymbolScope( lf->symbol(), symbolScope );
497  }
498  lf->provider()->drawUnplacedLabel( context, label );
499  // finished with symbol -- we can't keep it around after this, it may be deleted
500  lf->setSymbol( nullptr );
501  }
502  }
503 
504  symbolScopePopper.reset();
505 
506  // cleanup
507  for ( QgsAbstractLabelProvider *provider : qgis::as_const( mProviders ) )
508  {
509  if ( !layerId.isEmpty() && provider->layerId() != layerId )
510  continue;
511 
512  provider->stopRender( context );
513  }
514 
515  // Reset composition mode for further drawing operations
516  painter->setCompositionMode( QPainter::CompositionMode_SourceOver );
517 
518  QgsDebugMsgLevel( QStringLiteral( "LABELING draw: %1 ms" ).arg( t.elapsed() ), 4 );
519 }
520 
522 {
523  mUnlabeled.clear();
524  mLabels.clear();
525  mProblem.reset();
526  mPal.reset();
527 }
528 
530 {
531  return mResults.release();
532 }
533 
534 
535 //
536 // QgsDefaultLabelingEngine
537 //
538 
541 {
542 
543 }
544 
546 {
547  registerLabels( context );
548  if ( context.renderingStopped() )
549  {
550  cleanup();
551  return; // it has been canceled
552  }
553 
554  solve( context );
555  if ( context.renderingStopped() )
556  {
557  cleanup();
558  return;
559  }
560 
561  drawLabels( context );
562  cleanup();
563 }
564 
565 
566 //
567 // QgsStagedRenderLabelingEngine
568 //
569 
572 {
573 
574 }
575 
577 {
578  registerLabels( context );
579  if ( context.renderingStopped() )
580  {
581  cleanup();
582  return; // it has been canceled
583  }
584 
585  solve( context );
586  if ( context.renderingStopped() )
587  {
588  cleanup();
589  return;
590  }
591 }
592 
593 
595 {
596  drawLabels( context, layerId );
597 }
598 
600 {
601  cleanup();
602 }
603 
604 
606 
608 {
609  return mLayer ? mLayer->provider() : nullptr;
610 
611 }
612 
614  : mLayerId( layer ? layer->id() : QString() )
615  , mLayer( layer )
616  , mProviderId( providerId )
617  , mFlags( DrawLabels )
618  , mPlacement( QgsPalLayerSettings::AroundPoint )
619  , mPriority( 0.5 )
620  , mUpsidedownLabels( QgsPalLayerSettings::Upright )
621 {
622 }
623 
625 {
626 
627 }
628 
630 {
631 
632 }
633 
635 {
636  const auto subproviders = subProviders();
637  for ( QgsAbstractLabelProvider *subProvider : subproviders )
638  {
639  subProvider->startRender( context );
640  }
641 }
642 
644 {
645  const auto subproviders = subProviders();
646  for ( QgsAbstractLabelProvider *subProvider : subproviders )
647  {
648  subProvider->stopRender( context );
649  }
650 }
651 
652 //
653 // QgsLabelingUtils
654 //
655 
656 QString QgsLabelingUtils::encodePredefinedPositionOrder( const QVector<QgsPalLayerSettings::PredefinedPointPosition> &positions )
657 {
658  QStringList predefinedOrderString;
659  const auto constPositions = positions;
660  for ( QgsPalLayerSettings::PredefinedPointPosition position : constPositions )
661  {
662  switch ( position )
663  {
665  predefinedOrderString << QStringLiteral( "TL" );
666  break;
668  predefinedOrderString << QStringLiteral( "TSL" );
669  break;
671  predefinedOrderString << QStringLiteral( "T" );
672  break;
674  predefinedOrderString << QStringLiteral( "TSR" );
675  break;
677  predefinedOrderString << QStringLiteral( "TR" );
678  break;
680  predefinedOrderString << QStringLiteral( "L" );
681  break;
683  predefinedOrderString << QStringLiteral( "R" );
684  break;
686  predefinedOrderString << QStringLiteral( "BL" );
687  break;
689  predefinedOrderString << QStringLiteral( "BSL" );
690  break;
692  predefinedOrderString << QStringLiteral( "B" );
693  break;
695  predefinedOrderString << QStringLiteral( "BSR" );
696  break;
698  predefinedOrderString << QStringLiteral( "BR" );
699  break;
700  }
701  }
702  return predefinedOrderString.join( ',' );
703 }
704 
705 QVector<QgsPalLayerSettings::PredefinedPointPosition> QgsLabelingUtils::decodePredefinedPositionOrder( const QString &positionString )
706 {
707  QVector<QgsPalLayerSettings::PredefinedPointPosition> result;
708  const QStringList predefinedOrderList = positionString.split( ',' );
709  result.reserve( predefinedOrderList.size() );
710  for ( const QString &position : predefinedOrderList )
711  {
712  QString cleaned = position.trimmed().toUpper();
713  if ( cleaned == QLatin1String( "TL" ) )
715  else if ( cleaned == QLatin1String( "TSL" ) )
717  else if ( cleaned == QLatin1String( "T" ) )
719  else if ( cleaned == QLatin1String( "TSR" ) )
721  else if ( cleaned == QLatin1String( "TR" ) )
723  else if ( cleaned == QLatin1String( "L" ) )
725  else if ( cleaned == QLatin1String( "R" ) )
727  else if ( cleaned == QLatin1String( "BL" ) )
729  else if ( cleaned == QLatin1String( "BSL" ) )
731  else if ( cleaned == QLatin1String( "B" ) )
733  else if ( cleaned == QLatin1String( "BSR" ) )
735  else if ( cleaned == QLatin1String( "BR" ) )
737  }
738  return result;
739 }
740 
741 QString QgsLabelingUtils::encodeLinePlacementFlags( QgsLabeling::LinePlacementFlags flags )
742 {
743  QStringList parts;
744  if ( flags & QgsLabeling::LinePlacementFlag::OnLine )
745  parts << QStringLiteral( "OL" );
746  if ( flags & QgsLabeling::LinePlacementFlag::AboveLine )
747  parts << QStringLiteral( "AL" );
748  if ( flags & QgsLabeling::LinePlacementFlag::BelowLine )
749  parts << QStringLiteral( "BL" );
750  if ( !( flags & QgsLabeling::LinePlacementFlag::MapOrientation ) )
751  parts << QStringLiteral( "LO" );
752  return parts.join( ',' );
753 }
754 
755 QgsLabeling::LinePlacementFlags QgsLabelingUtils::decodeLinePlacementFlags( const QString &string )
756 {
757  QgsLabeling::LinePlacementFlags flags = QgsLabeling::LinePlacementFlags();
758  const QStringList flagList = string.split( ',' );
759  bool foundLineOrientationFlag = false;
760  for ( const QString &flag : flagList )
761  {
762  QString cleaned = flag.trimmed().toUpper();
763  if ( cleaned == QLatin1String( "OL" ) )
764  flags |= QgsLabeling::LinePlacementFlag::OnLine;
765  else if ( cleaned == QLatin1String( "AL" ) )
766  flags |= QgsLabeling::LinePlacementFlag::AboveLine;
767  else if ( cleaned == QLatin1String( "BL" ) )
768  flags |= QgsLabeling::LinePlacementFlag::BelowLine;
769  else if ( cleaned == QLatin1String( "LO" ) )
770  foundLineOrientationFlag = true;
771  }
772  if ( !foundLineOrientationFlag )
773  flags |= QgsLabeling::LinePlacementFlag::MapOrientation;
774  return flags;
775 }
776 
The QgsAbstractLabelProvider class is an interface class.
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.
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.
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.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
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 id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsFields fields
Definition: qgsfeature.h:66
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Definition: qgsfeature.cpp:144
static QgsFillSymbol * createSimple(const QVariantMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties.
Definition: qgssymbol.cpp:1578
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsGeometry difference(const QgsGeometry &geometry) const
Returns a geometry representing the points making up this geometry that do not make up other.
OperationResult rotate(double rotation, const QgsPointXY &center)
Rotate this geometry around the Z axis.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Q_GADGET bool isNull
Definition: qgsgeometry.h:126
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.
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
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.
@ 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:85
The QgsMapSettings class contains configuration for rendering of the map.
QPolygonF visiblePolygon() const
Returns the visible area as a polygon (may be rotated)
QStringList layerIds() const
Returns the list of layer IDs which will be rendered in the map.
QgsGeometry labelBoundaryGeometry() const
Returns the label boundary geometry, which restricts where in the rendered map labels are permitted t...
QList< QgsLabelBlockingRegion > labelBlockingRegions() const
Returns the list of regions to avoid placing labels within.
const QgsMapToPixel & mapToPixel() const
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes takes output image size into accou...
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.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
void setMapRotation(double degrees, double cx, double cy)
Set 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
QgsPointXY center() const SIP_HOLDGIL
Returns the center point of the rectangle.
Definition: qgsrectangle.h:230
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 ...
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...
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:168
QgsLabelFeature * feature()
Returns the parent feature.
Definition: feature.h:118
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:85
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:78
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:316
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39