QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
pal.cpp
Go to the documentation of this file.
1 /*
2  * libpal - Automated Placement of Labels Library
3  *
4  * Copyright (C) 2008 Maxence Laurent, MIS-TIC, HEIG-VD
5  * University of Applied Sciences, Western Switzerland
6  * http://www.hes-so.ch
7  *
8  * Contact:
9  * maxence.laurent <at> heig-vd <dot> ch
10  * or
11  * eric.taillard <at> heig-vd <dot> ch
12  *
13  * This file is part of libpal.
14  *
15  * libpal is free software: you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation, either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * libpal is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with libpal. If not, see <http://www.gnu.org/licenses/>.
27  *
28  */
29 
30 #include "qgsgeometry.h"
31 #include "pal.h"
32 #include "layer.h"
33 #include "palexception.h"
34 #include "palstat.h"
35 #include "costcalculator.h"
36 #include "feature.h"
37 #include "geomfunction.h"
38 #include "labelposition.h"
39 #include "problem.h"
40 #include "pointset.h"
41 #include "internalexception.h"
42 #include "util.h"
43 #include "palrtree.h"
44 #include "qgssettings.h"
45 #include "qgslabelingengine.h"
46 #include "qgsrendercontext.h"
47 #include <cfloat>
48 #include <list>
49 
50 using namespace pal;
51 
53 {
54  QgsSettings settings;
55  mGlobalCandidatesLimitPoint = settings.value( QStringLiteral( "rendering/label_candidates_limit_points" ), 0, QgsSettings::Core ).toInt();
56  mGlobalCandidatesLimitLine = settings.value( QStringLiteral( "rendering/label_candidates_limit_lines" ), 0, QgsSettings::Core ).toInt();
57  mGlobalCandidatesLimitPolygon = settings.value( QStringLiteral( "rendering/label_candidates_limit_polygons" ), 0, QgsSettings::Core ).toInt();
58 }
59 
60 Pal::~Pal() = default;
61 
62 void Pal::removeLayer( Layer *layer )
63 {
64  if ( !layer )
65  return;
66 
67  mMutex.lock();
68 
69  for ( auto it = mLayers.begin(); it != mLayers.end(); ++it )
70  {
71  if ( it->second.get() == layer )
72  {
73  mLayers.erase( it );
74  break;
75  }
76  }
77  mMutex.unlock();
78 }
79 
80 Layer *Pal::addLayer( QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel )
81 {
82  mMutex.lock();
83 
84  Q_ASSERT( mLayers.find( provider ) == mLayers.end() );
85 
86  std::unique_ptr< Layer > layer = std::make_unique< Layer >( provider, layerName, arrangement, defaultPriority, active, toLabel, this );
87  Layer *res = layer.get();
88  mLayers.insert( std::pair<QgsAbstractLabelProvider *, std::unique_ptr< Layer >>( provider, std::move( layer ) ) );
89  mMutex.unlock();
90 
91  return res;
92 }
93 
94 std::unique_ptr<Problem> Pal::extractProblem( const QgsRectangle &extent, const QgsGeometry &mapBoundary, QgsRenderContext &context )
95 {
96  QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
97 
98  // expand out the incoming buffer by 1000x -- that's the visible map extent, yet we may be getting features which exceed this extent
99  // (while 1000x may seem excessive here, this value is only used for scaling coordinates in the spatial indexes
100  // and the consequence of inserting coordinates outside this extent is worse than the consequence of setting this value too large.)
101  const QgsRectangle maxCoordinateExtentForSpatialIndices = extent.buffered( std::max( extent.width(), extent.height() ) * 1000 );
102 
103  // to store obstacles
104  PalRtree< FeaturePart > obstacles( maxCoordinateExtentForSpatialIndices );
105  PalRtree< LabelPosition > allCandidatesFirstRound( maxCoordinateExtentForSpatialIndices );
106  std::vector< FeaturePart * > allObstacleParts;
107  std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
108 
109  double bbx[4];
110  double bby[4];
111 
112  bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.xMinimum();
113  bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.yMinimum();
114  bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.xMaximum();
115  bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.yMaximum();
116 
117  prob->pal = this;
118 
119  std::list< std::unique_ptr< Feats > > features;
120 
121  // prepare map boundary
122  geos::unique_ptr mapBoundaryGeos( QgsGeos::asGeos( mapBoundary ) );
123  geos::prepared_unique_ptr mapBoundaryPrepared( GEOSPrepare_r( QgsGeos::getGEOSHandler(), mapBoundaryGeos.get() ) );
124 
125  int obstacleCount = 0;
126 
127  // first step : extract features from layers
128 
129  std::size_t previousFeatureCount = 0;
130  int previousObstacleCount = 0;
131 
132  QStringList layersWithFeaturesInBBox;
133 
134  QMutexLocker palLocker( &mMutex );
135 
136  double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
137  int index = -1;
138  for ( const auto &it : mLayers )
139  {
140  index++;
141  if ( feedback )
142  feedback->setProgress( index * step );
143 
144  Layer *layer = it.second.get();
145  if ( !layer )
146  {
147  // invalid layer name
148  continue;
149  }
150 
151  // only select those who are active
152  if ( !layer->active() )
153  continue;
154 
155  if ( feedback )
156  feedback->emit candidateCreationAboutToBegin( it.first );
157 
158  // check for connected features with the same label text and join them
159  if ( layer->mergeConnectedLines() )
160  layer->joinConnectedFeatures();
161 
162  if ( isCanceled() )
163  return nullptr;
164 
166 
167  if ( isCanceled() )
168  return nullptr;
169 
170  QMutexLocker locker( &layer->mMutex );
171 
172  const double featureStep = !layer->mFeatureParts.empty() ? step / layer->mFeatureParts.size() : 1;
173  std::size_t featureIndex = 0;
174  // generate candidates for all features
175  for ( const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->mFeatureParts ) )
176  {
177  if ( feedback )
178  feedback->setProgress( index * step + featureIndex * featureStep );
179  featureIndex++;
180 
181  if ( isCanceled() )
182  break;
183 
184  // Holes of the feature are obstacles
185  for ( int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
186  {
187  FeaturePart *selfObstacle = featurePart->getSelfObstacle( i );
188  obstacles.insert( selfObstacle, selfObstacle->boundingBox() );
189  allObstacleParts.emplace_back( selfObstacle );
190 
191  if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
192  {
193  //ERROR: SHOULD HAVE A PARENT!!!!!
194  }
195  }
196 
197  // generate candidates for the feature part
198  std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates( this );
199 
200  if ( isCanceled() )
201  break;
202 
203  // purge candidates that are outside the bbox
204  candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared, this]( std::unique_ptr< LabelPosition > &candidate )
205  {
206  if ( showPartialLabels() )
207  return !candidate->intersects( mapBoundaryPrepared.get() );
208  else
209  return !candidate->within( mapBoundaryPrepared.get() );
210  } ), candidates.end() );
211 
212  if ( isCanceled() )
213  break;
214 
215  if ( !candidates.empty() )
216  {
217  for ( std::unique_ptr< LabelPosition > &candidate : candidates )
218  {
219  candidate->insertIntoIndex( allCandidatesFirstRound );
220  candidate->setGlobalId( mNextCandidateId++ );
221  }
222 
223  std::sort( candidates.begin(), candidates.end(), CostCalculator::candidateSortGrow );
224 
225  // valid features are added to fFeats
226  std::unique_ptr< Feats > ft = std::make_unique< Feats >();
227  ft->feature = featurePart.get();
228  ft->shape = nullptr;
229  ft->candidates = std::move( candidates );
230  ft->priority = featurePart->calculatePriority();
231  features.emplace_back( std::move( ft ) );
232  }
233  else
234  {
235  // no candidates, so generate a default "point on surface" one
236  std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
237  if ( !unplacedPosition )
238  continue;
239 
240  if ( featurePart->feature()->allowDegradedPlacement() )
241  {
242  // if we are allowing degraded placements, we throw the default candidate in too
243  unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
244  unplacedPosition->setGlobalId( mNextCandidateId++ );
245  candidates.emplace_back( std::move( unplacedPosition ) );
246 
247  // valid features are added to fFeats
248  std::unique_ptr< Feats > ft = std::make_unique< Feats >();
249  ft->feature = featurePart.get();
250  ft->shape = nullptr;
251  ft->candidates = std::move( candidates );
252  ft->priority = featurePart->calculatePriority();
253  features.emplace_back( std::move( ft ) );
254  }
255  else
256  {
257  // not displaying all labels for this layer, so it goes into the unlabeled feature list
258  prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
259  }
260  }
261  }
262  if ( isCanceled() )
263  return nullptr;
264 
265  // collate all layer obstacles
266  for ( FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
267  {
268  if ( isCanceled() )
269  break; // do not continue searching
270 
271  // insert into obstacles
272  obstacles.insert( obstaclePart, obstaclePart->boundingBox() );
273  allObstacleParts.emplace_back( obstaclePart );
274  obstacleCount++;
275  }
276 
277  if ( isCanceled() )
278  return nullptr;
279 
280  locker.unlock();
281 
282  if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
283  {
284  layersWithFeaturesInBBox << layer->name();
285  }
286  previousFeatureCount = features.size();
287  previousObstacleCount = obstacleCount;
288 
289  if ( feedback )
290  feedback->emit candidateCreationFinished( it.first );
291  }
292  palLocker.unlock();
293 
294  if ( isCanceled() )
295  return nullptr;
296 
297  prob->mLayerCount = layersWithFeaturesInBBox.size();
298  prob->labelledLayersName = layersWithFeaturesInBBox;
299 
300  prob->mFeatureCount = features.size();
301  prob->mTotalCandidates = 0;
302  prob->mFeatNbLp.resize( prob->mFeatureCount );
303  prob->mFeatStartId.resize( prob->mFeatureCount );
304  prob->mInactiveCost.resize( prob->mFeatureCount );
305 
306  if ( !features.empty() )
307  {
308  if ( feedback )
309  feedback->emit obstacleCostingAboutToBegin();
310  // Filtering label positions against obstacles
311  index = -1;
312  step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
313 
314  for ( FeaturePart *obstaclePart : allObstacleParts )
315  {
316  index++;
317  if ( feedback )
318  feedback->setProgress( step * index );
319 
320  if ( isCanceled() )
321  break; // do not continue searching
322 
323  allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart, this]( const LabelPosition * candidatePosition ) -> bool
324  {
325  // test whether we should ignore this obstacle for the candidate. We do this if:
326  // 1. it's not a hole, and the obstacle belongs to the same label feature as the candidate (e.g.,
327  // features aren't obstacles for their own labels)
328  // 2. it IS a hole, and the hole belongs to a different label feature to the candidate (e.g., holes
329  // are ONLY obstacles for the labels of the feature they belong to)
330  // 3. The label is set to "Always Allow" overlap mode
331  if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
332  || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
333  || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
334  {
335  return true;
336  }
337 
338  CostCalculator::addObstacleCostPenalty( const_cast< LabelPosition * >( candidatePosition ), obstaclePart, this );
339 
340  return true;
341  } );
342  }
343 
344  if ( feedback )
345  feedback->emit obstacleCostingFinished();
346 
347  if ( isCanceled() )
348  {
349  return nullptr;
350  }
351 
352  step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
353  if ( feedback )
354  feedback->emit calculatingConflictsAboutToBegin();
355 
356  int idlp = 0;
357  for ( std::size_t i = 0; i < prob->mFeatureCount; i++ ) /* for each feature into prob */
358  {
359  if ( feedback )
360  feedback->setProgress( i * step );
361 
362  std::unique_ptr< Feats > feat = std::move( features.front() );
363  features.pop_front();
364 
365  prob->mFeatStartId[i] = idlp;
366  prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
367 
368  std::size_t maxCandidates = 0;
369  switch ( feat->feature->getGeosType() )
370  {
371  case GEOS_POINT:
372  // this is usually 0, i.e. no maximum
373  maxCandidates = feat->feature->maximumPointCandidates();
374  break;
375 
376  case GEOS_LINESTRING:
377  maxCandidates = feat->feature->maximumLineCandidates();
378  break;
379 
380  case GEOS_POLYGON:
381  maxCandidates = std::max( static_cast< std::size_t >( 16 ), feat->feature->maximumPolygonCandidates() );
382  break;
383  }
384 
385  if ( isCanceled() )
386  return nullptr;
387 
388  auto pruneHardConflicts = [&]
389  {
390  switch ( mPlacementVersion )
391  {
393  break;
394 
396  {
397  // v2 placement rips out candidates where the candidate cost is too high when compared to
398  // their inactive cost
399 
400  // note, we start this at the SECOND candidate (you'll see why after this loop)
401  feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
402  {
403  if ( candidate->hasHardObstacleConflict() )
404  {
405  return true;
406  }
407  return false;
408  } ), feat->candidates.end() );
409 
410  if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
411  {
412  switch ( feat->feature->feature()->overlapHandling() )
413  {
415  {
416  // we're going to end up removing ALL candidates for this label. Oh well, that's allowed. We just need to
417  // make sure we move this last candidate to the unplaced labels list
418  prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
419  feat->candidates.clear();
420  break;
421  }
422 
425  // we can't avoid overlaps for this label, but in this mode we are allowing overlaps as a last resort.
426  // => don't discard this last remaining candidate.
427  break;
428  }
429  }
430  }
431  }
432  };
433 
434  // if we're not showing all labels (including conflicts) for this layer, then we prune the candidates
435  // upfront to avoid extra work...
436  switch ( feat->feature->feature()->overlapHandling() )
437  {
439  pruneHardConflicts();
440  break;
441 
444  break;
445  }
446 
447  if ( feat->candidates.empty() )
448  continue;
449 
450  // calculate final costs
451  CostCalculator::finalizeCandidatesCosts( feat.get(), bbx, bby );
452 
453  // sort candidates list, best label to worst
454  std::sort( feat->candidates.begin(), feat->candidates.end(), CostCalculator::candidateSortGrow );
455 
456  // but if we ARE showing all labels (including conflicts), let's go ahead and prune them now.
457  // Since we've calculated all their costs and sorted them, if we've hit the situation that ALL
458  // candidates have conflicts, then at least when we pick the first candidate to display it will be
459  // the lowest cost (i.e. best possible) overlapping candidate...
460  switch ( feat->feature->feature()->overlapHandling() )
461  {
463  break;
466  pruneHardConflicts();
467  break;
468  }
469 
470  // only keep the 'maxCandidates' best candidates
471  if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
472  {
473  feat->candidates.resize( maxCandidates );
474  }
475 
476  if ( isCanceled() )
477  return nullptr;
478 
479  // update problem's # candidate
480  prob->mFeatNbLp[i] = static_cast< int >( feat->candidates.size() );
481  prob->mTotalCandidates += static_cast< int >( feat->candidates.size() );
482 
483  // add all candidates into a rtree (to speed up conflicts searching)
484  for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
485  {
486  candidate->insertIntoIndex( prob->allCandidatesIndex() );
487  candidate->setProblemIds( static_cast< int >( i ), idlp++ );
488  }
489  features.emplace_back( std::move( feat ) );
490  }
491 
492  if ( feedback )
493  feedback->emit calculatingConflictsFinished();
494 
495  int nbOverlaps = 0;
496 
497  double amin[2];
498  double amax[2];
499 
500  if ( feedback )
501  feedback->emit finalizingCandidatesAboutToBegin();
502 
503  index = -1;
504  step = !features.empty() ? 100.0 / features.size() : 1;
505  while ( !features.empty() ) // for each feature
506  {
507  index++;
508  if ( feedback )
509  feedback->setProgress( step * index );
510 
511  if ( isCanceled() )
512  return nullptr;
513 
514  std::unique_ptr< Feats > feat = std::move( features.front() );
515  features.pop_front();
516 
517  for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
518  {
519  std::unique_ptr< LabelPosition > lp = std::move( candidate );
520 
521  lp->resetNumOverlaps();
522 
523  // make sure that candidate's cost is less than 1
524  lp->validateCost();
525 
526  //prob->feat[idlp] = j;
527 
528  // lookup for overlapping candidate
529  lp->getBoundingBox( amin, amax );
530  prob->allCandidatesIndex().intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp, this]( const LabelPosition * lp2 )->bool
531  {
532  if ( candidatesAreConflicting( lp.get(), lp2 ) )
533  {
534  lp->incrementNumOverlaps();
535  }
536 
537  return true;
538 
539  } );
540 
541  nbOverlaps += lp->getNumOverlaps();
542 
543  prob->addCandidatePosition( std::move( lp ) );
544 
545  if ( isCanceled() )
546  return nullptr;
547  }
548  }
549 
550  if ( feedback )
551  feedback->emit finalizingCandidatesFinished();
552 
553  nbOverlaps /= 2;
554  prob->mAllNblp = prob->mTotalCandidates;
555  prob->mNbOverlap = nbOverlaps;
556  }
557 
558  return prob;
559 }
560 
561 void Pal::registerCancellationCallback( Pal::FnIsCanceled fnCanceled, void *context )
562 {
563  fnIsCanceled = fnCanceled;
564  fnIsCanceledContext = context;
565 }
566 
567 
568 QList<LabelPosition *> Pal::solveProblem( Problem *prob, QgsRenderContext &context, bool displayAll, QList<LabelPosition *> *unlabeled )
569 {
570  QgsLabelingEngineFeedback *feedback = qobject_cast< QgsLabelingEngineFeedback * >( context.feedback() );
571 
572  if ( !prob )
573  return QList<LabelPosition *>();
574 
575  if ( feedback )
576  feedback->emit reductionAboutToBegin();
577 
578  prob->reduce();
579 
580  if ( feedback )
581  feedback->emit reductionFinished();
582 
583  if ( feedback )
584  feedback->emit solvingPlacementAboutToBegin();
585 
586  try
587  {
588  prob->chainSearch( context );
589  }
590  catch ( InternalException::Empty & )
591  {
592  return QList<LabelPosition *>();
593  }
594 
595  if ( feedback )
596  feedback->emit solvingPlacementFinished();
597 
598  return prob->getSolution( displayAll, unlabeled );
599 }
600 
601 void Pal::setMinIt( int min_it )
602 {
603  if ( min_it >= 0 )
604  mTabuMinIt = min_it;
605 }
606 
607 void Pal::setMaxIt( int max_it )
608 {
609  if ( max_it > 0 )
610  mTabuMaxIt = max_it;
611 }
612 
613 void Pal::setPopmusicR( int r )
614 {
615  if ( r > 0 )
616  mPopmusicR = r;
617 }
618 
619 void Pal::setEjChainDeg( int degree )
620 {
621  this->mEjChainDeg = degree;
622 }
623 
624 void Pal::setTenure( int tenure )
625 {
626  this->mTenure = tenure;
627 }
628 
629 void Pal::setCandListSize( double fact )
630 {
631  this->mCandListSize = fact;
632 }
633 
634 void Pal::setShowPartialLabels( bool show )
635 {
636  this->mShowPartialLabels = show;
637 }
638 
640 {
641  return mPlacementVersion;
642 }
643 
645 {
646  mPlacementVersion = placementVersion;
647 }
648 
649 bool Pal::candidatesAreConflicting( const LabelPosition *lp1, const LabelPosition *lp2 ) const
650 {
651  // we cache the value -- this can be costly to calculate, and we check this multiple times
652  // per candidate during the labeling problem solving
653 
654  // conflicts are commutative - so we always store them in the cache using the smaller id as the first element of the key pair
655  auto key = qMakePair( std::min( lp1->globalId(), lp2->globalId() ), std::max( lp1->globalId(), lp2->globalId() ) );
656  auto it = mCandidateConflicts.constFind( key );
657  if ( it != mCandidateConflicts.constEnd() )
658  return *it;
659 
660  const bool res = lp1->isInConflict( lp2 );
661  mCandidateConflicts.insert( key, res );
662  return res;
663 }
664 
665 int Pal::getMinIt() const
666 {
667  return mTabuMaxIt;
668 }
669 
670 int Pal::getMaxIt() const
671 {
672  return mTabuMinIt;
673 }
674 
676 {
677  return mShowPartialLabels;
678 }
pal::Pal::extractProblem
std::unique_ptr< Problem > extractProblem(const QgsRectangle &extent, const QgsGeometry &mapBoundary, QgsRenderContext &context)
Extracts the labeling problem for the specified map extent - only features within this extent will be...
Definition: pal.cpp:94
pal::Layer::mMutex
QMutex mMutex
Definition: layer.h:345
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:76
pal::Pal::isCanceled
bool isCanceled()
Check whether the job has been canceled.
Definition: pal.h:126
QgsRectangle::height
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
pal::Layer::mObstacleParts
QList< FeaturePart * > mObstacleParts
List of obstacle parts.
Definition: layer.h:319
PalRtree::intersects
bool intersects(const QgsRectangle &bounds, const std::function< bool(T *data)> &callback) const
Performs an intersection check against the index, for data intersecting the specified bounds.
Definition: palrtree.h:96
pal::LabelPosition::isInConflict
bool isInConflict(const LabelPosition *ls) const
Check whether or not this overlap with another labelPosition.
Definition: labelposition.cpp:265
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
pal::Pal::placementVersion
QgsLabelingEngineSettings::PlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
Definition: pal.cpp:639
labelposition.h
QgsSettings::Core
@ Core
Definition: qgssettings.h:70
pal::PointSet::boundingBox
QgsRectangle boundingBox() const
Returns the point set bounding box.
Definition: pointset.h:163
pal::LabelPosition
LabelPosition is a candidate feature label position.
Definition: labelposition.h:55
pal::Pal::Pal
Pal()
Create an new pal instance.
Definition: pal.cpp:52
QgsRectangle::yMinimum
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
pal::Pal::~Pal
~Pal()
QgsRenderContext
Contains information about the context of a rendering operation.
Definition: qgsrendercontext.h:59
pal::Layer::mergeConnectedLines
bool mergeConnectedLines() const
Returns whether connected lines will be merged before labeling.
Definition: layer.h:256
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
layer.h
pal::Layer::active
bool active() const
Returns whether the layer is currently active.
Definition: layer.h:198
pal::Pal::solveProblem
QList< LabelPosition * > solveProblem(Problem *prob, QgsRenderContext &context, bool displayAll, QList< pal::LabelPosition * > *unlabeled=nullptr)
Solves the labeling problem, selecting the best candidate locations for all labels and returns a list...
Definition: pal.cpp:568
Qgis::LabelOverlapHandling::AllowOverlapIfRequired
@ AllowOverlapIfRequired
Avoids overlapping labels when possible, but permit overlaps if labels for features cannot otherwise ...
pal::Problem
Representation of a labeling problem.
Definition: problem.h:72
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:41
pal
Definition: qgsdiagramrenderer.h:50
pal::Pal::setShowPartialLabels
void setShowPartialLabels(bool show)
Sets whether partial labels show be allowed.
Definition: pal.cpp:634
problem.h
geos::unique_ptr
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79
palstat.h
QgsRectangle::xMaximum
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
QgsLabelingEngineSettings::PlacementEngineVersion
PlacementEngineVersion
Placement engine version.
Definition: qgslabelingenginesettings.h:68
feature.h
pal::Problem::chainSearch
void chainSearch(QgsRenderContext &context)
Test with very-large scale neighborhood.
Definition: problem.cpp:573
QgsGeos::asGeos
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
Definition: qgsgeos.cpp:181
pal::Layer::name
QString name() const
Returns the layer's name.
Definition: layer.h:162
QgsAbstractLabelProvider
The QgsAbstractLabelProvider class is an interface class. Implementations return list of labels and t...
Definition: qgslabelingengine.h:52
pal::CostCalculator::candidateSortGrow
static bool candidateSortGrow(const std::unique_ptr< pal::LabelPosition > &c1, const std::unique_ptr< pal::LabelPosition > &c2)
Sorts label candidates in ascending order of cost.
Definition: costcalculator.cpp:28
palrtree.h
costcalculator.h
palexception.h
pal::LabelPosition::globalId
unsigned int globalId() const
Returns the global ID for the candidate, which is unique for a single run of the pal labelling engine...
Definition: labelposition.h:329
pal::Problem::reduce
void reduce()
Definition: problem.cpp:66
geomfunction.h
pointset.h
qgsrendercontext.h
pal::CostCalculator::finalizeCandidatesCosts
static void finalizeCandidatesCosts(Feats *feat, double bbx[4], double bby[4])
Sort candidates by costs, skip the worse ones, evaluate polygon candidates.
Definition: costcalculator.cpp:210
pal::FeaturePart
Main class to handle feature.
Definition: feature.h:64
pal::FeaturePart::getSelfObstacle
FeaturePart * getSelfObstacle(int i)
Gets hole (inner ring) - considered as obstacle.
Definition: feature.h:308
QgsLabelingEngineSettings::PlacementEngineVersion1
@ PlacementEngineVersion1
Version 1, matches placement from QGIS <= 3.10.1.
Definition: qgslabelingenginesettings.h:70
QgsRectangle::xMinimum
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
QgsRectangle::buffered
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Definition: qgsrectangle.h:325
pal::Layer::mFeatureParts
std::deque< std::unique_ptr< FeaturePart > > mFeatureParts
List of feature parts.
Definition: layer.h:316
Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
@ AllowOverlapAtNoCost
Labels may freely overlap other labels, at no cost.
pal::Layer::joinConnectedFeatures
void joinConnectedFeatures()
Join connected features with the same label text.
Definition: layer.cpp:298
QgsGeos::getGEOSHandler
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:3369
pal::Pal::showPartialLabels
bool showPartialLabels() const
Returns whether partial labels should be allowed.
Definition: pal.cpp:675
pal::Problem::getSolution
QList< LabelPosition * > getSolution(bool returnInactive, QList< LabelPosition * > *unlabeled=nullptr)
Solves the labeling problem, selecting the best candidate locations for all labels and returns a list...
Definition: problem.cpp:659
Qgis::LabelOverlapHandling::PreventOverlap
@ PreventOverlap
Do not allow labels to overlap other labels.
QgsRenderContext::feedback
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
Definition: qgsrendercontext.cpp:206
pal::CostCalculator::addObstacleCostPenalty
static void addObstacleCostPenalty(pal::LabelPosition *lp, pal::FeaturePart *obstacle, Pal *pal)
Increase candidate's cost according to its collision with passed feature.
Definition: costcalculator.cpp:33
QgsRectangle::yMaximum
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
qgsgeometry.h
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsRectangle::width
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
PalRtree
A rtree spatial index for use in the pal labeling engine.
Definition: palrtree.h:35
qgssettings.h
internalexception.h
pal::Pal::removeLayer
void removeLayer(Layer *layer)
remove a layer
Definition: pal.cpp:62
pal::Pal::addLayer
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
add a new layer
Definition: pal.cpp:80
pal::Layer::chopFeaturesAtRepeatDistance
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
Definition: layer.cpp:366
pal::Layer
A set of features which influence the labeling process.
Definition: layer.h:62
pal::Pal::setPlacementVersion
void setPlacementVersion(QgsLabelingEngineSettings::PlacementEngineVersion placementVersion)
Sets the placement engine version, which dictates how the label placement problem is solved.
Definition: pal.cpp:644
geos::prepared_unique_ptr
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
Definition: qgsgeos.h:84
pal.h
QgsLabelingEngineSettings::PlacementEngineVersion2
@ PlacementEngineVersion2
Version 2 (default for new projects since QGIS 3.12)
Definition: qgslabelingenginesettings.h:71
pal::InternalException::Empty
Thrown when trying to access an empty data set.
Definition: internalexception.h:64
pal::Pal::candidatesAreConflicting
bool candidatesAreConflicting(const LabelPosition *lp1, const LabelPosition *lp2) const
Returns true if a labelling candidate lp1 conflicts with lp2.
Definition: pal.cpp:649
util.h
QgsLabelingEngineFeedback
QgsFeedback subclass for granular reporting of labeling engine progress.
Definition: qgslabelingengine.h:214
PalRtree::insert
void insert(T *data, const QgsRectangle &bounds)
Inserts new data into the spatial index, with the specified bounds.
Definition: palrtree.h:59
qgslabelingengine.h
Qgis::LabelPlacement
LabelPlacement
Placement modes which determine how label candidates are generated for a feature.
Definition: qgis.h:560