QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 <cfloat>
46 #include <list>
47 
48 using namespace pal;
49 
51 {
52  QgsSettings settings;
53  mGlobalCandidatesLimitPoint = settings.value( QStringLiteral( "rendering/label_candidates_limit_points" ), 0, QgsSettings::Core ).toInt();
54  mGlobalCandidatesLimitLine = settings.value( QStringLiteral( "rendering/label_candidates_limit_lines" ), 0, QgsSettings::Core ).toInt();
55  mGlobalCandidatesLimitPolygon = settings.value( QStringLiteral( "rendering/label_candidates_limit_polygons" ), 0, QgsSettings::Core ).toInt();
56 }
57 
58 Pal::~Pal() = default;
59 
60 void Pal::removeLayer( Layer *layer )
61 {
62  if ( !layer )
63  return;
64 
65  mMutex.lock();
66 
67  for ( auto it = mLayers.begin(); it != mLayers.end(); ++it )
68  {
69  if ( it->second.get() == layer )
70  {
71  mLayers.erase( it );
72  break;
73  }
74  }
75  mMutex.unlock();
76 }
77 
78 Layer *Pal::addLayer( QgsAbstractLabelProvider *provider, const QString &layerName, QgsPalLayerSettings::Placement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll )
79 {
80  mMutex.lock();
81 
82  Q_ASSERT( mLayers.find( provider ) == mLayers.end() );
83 
84  std::unique_ptr< Layer > layer = std::make_unique< Layer >( provider, layerName, arrangement, defaultPriority, active, toLabel, this, displayAll );
85  Layer *res = layer.get();
86  mLayers.insert( std::pair<QgsAbstractLabelProvider *, std::unique_ptr< Layer >>( provider, std::move( layer ) ) );
87  mMutex.unlock();
88 
89  return res;
90 }
91 
92 std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeometry &mapBoundary )
93 {
94  // expand out the incoming buffer by 1000x -- that's the visible map extent, yet we may be getting features which exceed this extent
95  // (while 1000x may seem excessive here, this value is only used for scaling coordinates in the spatial indexes
96  // and the consequence of inserting coordinates outside this extent is worse than the consequence of setting this value too large.)
97  const QgsRectangle maxCoordinateExtentForSpatialIndices = extent.buffered( std::max( extent.width(), extent.height() ) * 1000 );
98 
99  // to store obstacles
100  PalRtree< FeaturePart > obstacles( maxCoordinateExtentForSpatialIndices );
101  PalRtree< LabelPosition > allCandidatesFirstRound( maxCoordinateExtentForSpatialIndices );
102  std::vector< FeaturePart * > allObstacleParts;
103  std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
104 
105  double bbx[4];
106  double bby[4];
107 
108  bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.xMinimum();
109  bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.yMinimum();
110  bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.xMaximum();
111  bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.yMaximum();
112 
113  prob->pal = this;
114 
115  std::list< std::unique_ptr< Feats > > features;
116 
117  // prepare map boundary
118  geos::unique_ptr mapBoundaryGeos( QgsGeos::asGeos( mapBoundary ) );
119  geos::prepared_unique_ptr mapBoundaryPrepared( GEOSPrepare_r( QgsGeos::getGEOSHandler(), mapBoundaryGeos.get() ) );
120 
121  int obstacleCount = 0;
122 
123  // first step : extract features from layers
124 
125  std::size_t previousFeatureCount = 0;
126  int previousObstacleCount = 0;
127 
128  QStringList layersWithFeaturesInBBox;
129 
130  QMutexLocker palLocker( &mMutex );
131  for ( const auto &it : mLayers )
132  {
133  Layer *layer = it.second.get();
134  if ( !layer )
135  {
136  // invalid layer name
137  continue;
138  }
139 
140  // only select those who are active
141  if ( !layer->active() )
142  continue;
143 
144  // check for connected features with the same label text and join them
145  if ( layer->mergeConnectedLines() )
146  layer->joinConnectedFeatures();
147 
148  if ( isCanceled() )
149  return nullptr;
150 
152 
153  if ( isCanceled() )
154  return nullptr;
155 
156  QMutexLocker locker( &layer->mMutex );
157 
158  // generate candidates for all features
159  for ( const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->mFeatureParts ) )
160  {
161  if ( isCanceled() )
162  break;
163 
164  // Holes of the feature are obstacles
165  for ( int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
166  {
167  FeaturePart *selfObstacle = featurePart->getSelfObstacle( i );
168  obstacles.insert( selfObstacle, selfObstacle->boundingBox() );
169  allObstacleParts.emplace_back( selfObstacle );
170 
171  if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
172  {
173  //ERROR: SHOULD HAVE A PARENT!!!!!
174  }
175  }
176 
177  // generate candidates for the feature part
178  std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates( this );
179 
180  if ( isCanceled() )
181  break;
182 
183  // purge candidates that are outside the bbox
184  candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared, this]( std::unique_ptr< LabelPosition > &candidate )
185  {
186  if ( showPartialLabels() )
187  return !candidate->intersects( mapBoundaryPrepared.get() );
188  else
189  return !candidate->within( mapBoundaryPrepared.get() );
190  } ), candidates.end() );
191 
192  if ( isCanceled() )
193  break;
194 
195  if ( !candidates.empty() )
196  {
197  for ( std::unique_ptr< LabelPosition > &candidate : candidates )
198  {
199  candidate->insertIntoIndex( allCandidatesFirstRound );
200  candidate->setGlobalId( mNextCandidateId++ );
201  }
202 
203  std::sort( candidates.begin(), candidates.end(), CostCalculator::candidateSortGrow );
204 
205  // valid features are added to fFeats
206  std::unique_ptr< Feats > ft = std::make_unique< Feats >();
207  ft->feature = featurePart.get();
208  ft->shape = nullptr;
209  ft->candidates = std::move( candidates );
210  ft->priority = featurePart->calculatePriority();
211  features.emplace_back( std::move( ft ) );
212  }
213  else
214  {
215  // no candidates, so generate a default "point on surface" one
216  std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
217  if ( !unplacedPosition )
218  continue;
219 
220  if ( layer->displayAll() )
221  {
222  // if we are displaying all labels, we throw the default candidate in too
223  unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
224  unplacedPosition->setGlobalId( mNextCandidateId++ );
225  candidates.emplace_back( std::move( unplacedPosition ) );
226 
227  // valid features are added to fFeats
228  std::unique_ptr< Feats > ft = std::make_unique< Feats >();
229  ft->feature = featurePart.get();
230  ft->shape = nullptr;
231  ft->candidates = std::move( candidates );
232  ft->priority = featurePart->calculatePriority();
233  features.emplace_back( std::move( ft ) );
234  }
235  else
236  {
237  // not displaying all labels for this layer, so it goes into the unlabeled feature list
238  prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
239  }
240  }
241  }
242  if ( isCanceled() )
243  return nullptr;
244 
245  // collate all layer obstacles
246  for ( FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
247  {
248  if ( isCanceled() )
249  break; // do not continue searching
250 
251  // insert into obstacles
252  obstacles.insert( obstaclePart, obstaclePart->boundingBox() );
253  allObstacleParts.emplace_back( obstaclePart );
254  obstacleCount++;
255  }
256 
257  if ( isCanceled() )
258  return nullptr;
259 
260  locker.unlock();
261 
262  if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
263  {
264  layersWithFeaturesInBBox << layer->name();
265  }
266  previousFeatureCount = features.size();
267  previousObstacleCount = obstacleCount;
268  }
269  palLocker.unlock();
270 
271  if ( isCanceled() )
272  return nullptr;
273 
274  prob->mLayerCount = layersWithFeaturesInBBox.size();
275  prob->labelledLayersName = layersWithFeaturesInBBox;
276 
277  prob->mFeatureCount = features.size();
278  prob->mTotalCandidates = 0;
279  prob->mFeatNbLp.resize( prob->mFeatureCount );
280  prob->mFeatStartId.resize( prob->mFeatureCount );
281  prob->mInactiveCost.resize( prob->mFeatureCount );
282 
283  if ( !features.empty() )
284  {
285  // Filtering label positions against obstacles
286  for ( FeaturePart *obstaclePart : allObstacleParts )
287  {
288  if ( isCanceled() )
289  break; // do not continue searching
290 
291  allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart, this]( const LabelPosition * candidatePosition ) -> bool
292  {
293  // test whether we should ignore this obstacle for the candidate. We do this if:
294  // 1. it's not a hole, and the obstacle belongs to the same label feature as the candidate (e.g.,
295  // features aren't obstacles for their own labels)
296  // 2. it IS a hole, and the hole belongs to a different label feature to the candidate (e.g., holes
297  // are ONLY obstacles for the labels of the feature they belong to)
298  if ( ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
299  || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
300  {
301  return true;
302  }
303 
304  CostCalculator::addObstacleCostPenalty( const_cast< LabelPosition * >( candidatePosition ), obstaclePart, this );
305 
306  return true;
307  } );
308  }
309 
310  if ( isCanceled() )
311  {
312  return nullptr;
313  }
314 
315  int idlp = 0;
316  for ( std::size_t i = 0; i < prob->mFeatureCount; i++ ) /* for each feature into prob */
317  {
318  std::unique_ptr< Feats > feat = std::move( features.front() );
319  features.pop_front();
320 
321  prob->mFeatStartId[i] = idlp;
322  prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
323 
324  std::size_t maxCandidates = 0;
325  switch ( feat->feature->getGeosType() )
326  {
327  case GEOS_POINT:
328  // this is usually 0, i.e. no maximum
329  maxCandidates = feat->feature->maximumPointCandidates();
330  break;
331 
332  case GEOS_LINESTRING:
333  maxCandidates = feat->feature->maximumLineCandidates();
334  break;
335 
336  case GEOS_POLYGON:
337  maxCandidates = std::max( static_cast< std::size_t >( 16 ), feat->feature->maximumPolygonCandidates() );
338  break;
339  }
340 
341  if ( isCanceled() )
342  return nullptr;
343 
344  auto pruneHardConflicts = [&]
345  {
346  switch ( mPlacementVersion )
347  {
349  break;
350 
352  {
353  // v2 placement rips out candidates where the candidate cost is too high when compared to
354  // their inactive cost
355 
356  // note, we start this at the SECOND candidate (you'll see why after this loop)
357  feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
358  {
359  if ( candidate->hasHardObstacleConflict() )
360  {
361  return true;
362  }
363  return false;
364  } ), feat->candidates.end() );
365 
366  if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() && !feat->feature->layer()->displayAll() )
367  {
368  // we've going to end up removing ALL candidates for this label. Oh well, that's allowed. We just need to
369  // make sure we move this last candidate to the unplaced labels list
370  prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
371  feat->candidates.clear();
372  }
373  }
374  }
375  };
376 
377  // if we're not showing all labels (including conflicts) for this layer, then we prune the candidates
378  // upfront to avoid extra work...
379  if ( !feat->feature->layer()->displayAll() )
380  {
381  pruneHardConflicts();
382  }
383 
384  if ( feat->candidates.empty() )
385  continue;
386 
387  // calculate final costs
388  CostCalculator::finalizeCandidatesCosts( feat.get(), bbx, bby );
389 
390  // sort candidates list, best label to worst
391  std::sort( feat->candidates.begin(), feat->candidates.end(), CostCalculator::candidateSortGrow );
392 
393  // but if we ARE showing all labels (including conflicts), let's go ahead and prune them now.
394  // Since we've calculated all their costs and sorted them, if we've hit the situation that ALL
395  // candidates have conflicts, then at least when we pick the first candidate to display it will be
396  // the lowest cost (i.e. best possible) overlapping candidate...
397  if ( feat->feature->layer()->displayAll() )
398  {
399  pruneHardConflicts();
400  }
401 
402 
403  // only keep the 'maxCandidates' best candidates
404  if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
405  {
406  feat->candidates.resize( maxCandidates );
407  }
408 
409  if ( isCanceled() )
410  return nullptr;
411 
412  // update problem's # candidate
413  prob->mFeatNbLp[i] = static_cast< int >( feat->candidates.size() );
414  prob->mTotalCandidates += static_cast< int >( feat->candidates.size() );
415 
416  // add all candidates into a rtree (to speed up conflicts searching)
417  for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
418  {
419  candidate->insertIntoIndex( prob->allCandidatesIndex() );
420  candidate->setProblemIds( static_cast< int >( i ), idlp++ );
421  }
422  features.emplace_back( std::move( feat ) );
423  }
424 
425  int nbOverlaps = 0;
426 
427  double amin[2];
428  double amax[2];
429  while ( !features.empty() ) // for each feature
430  {
431  if ( isCanceled() )
432  return nullptr;
433 
434  std::unique_ptr< Feats > feat = std::move( features.front() );
435  features.pop_front();
436 
437  for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
438  {
439  std::unique_ptr< LabelPosition > lp = std::move( candidate );
440 
441  lp->resetNumOverlaps();
442 
443  // make sure that candidate's cost is less than 1
444  lp->validateCost();
445 
446  //prob->feat[idlp] = j;
447 
448  // lookup for overlapping candidate
449  lp->getBoundingBox( amin, amax );
450  prob->allCandidatesIndex().intersects( QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp, this]( const LabelPosition * lp2 )->bool
451  {
452  if ( candidatesAreConflicting( lp.get(), lp2 ) )
453  {
454  lp->incrementNumOverlaps();
455  }
456 
457  return true;
458 
459  } );
460 
461  nbOverlaps += lp->getNumOverlaps();
462 
463  prob->addCandidatePosition( std::move( lp ) );
464 
465  if ( isCanceled() )
466  return nullptr;
467  }
468  }
469  nbOverlaps /= 2;
470  prob->mAllNblp = prob->mTotalCandidates;
471  prob->mNbOverlap = nbOverlaps;
472  }
473 
474  return prob;
475 }
476 
477 void Pal::registerCancellationCallback( Pal::FnIsCanceled fnCanceled, void *context )
478 {
479  fnIsCanceled = fnCanceled;
480  fnIsCanceledContext = context;
481 }
482 
483 std::unique_ptr<Problem> Pal::extractProblem( const QgsRectangle &extent, const QgsGeometry &mapBoundary )
484 {
485  return extract( extent, mapBoundary );
486 }
487 
488 QList<LabelPosition *> Pal::solveProblem( Problem *prob, bool displayAll, QList<LabelPosition *> *unlabeled )
489 {
490  if ( !prob )
491  return QList<LabelPosition *>();
492 
493  prob->reduce();
494 
495  try
496  {
497  prob->chain_search();
498  }
499  catch ( InternalException::Empty & )
500  {
501  return QList<LabelPosition *>();
502  }
503 
504  return prob->getSolution( displayAll, unlabeled );
505 }
506 
507 void Pal::setMinIt( int min_it )
508 {
509  if ( min_it >= 0 )
510  mTabuMinIt = min_it;
511 }
512 
513 void Pal::setMaxIt( int max_it )
514 {
515  if ( max_it > 0 )
516  mTabuMaxIt = max_it;
517 }
518 
519 void Pal::setPopmusicR( int r )
520 {
521  if ( r > 0 )
522  mPopmusicR = r;
523 }
524 
525 void Pal::setEjChainDeg( int degree )
526 {
527  this->mEjChainDeg = degree;
528 }
529 
530 void Pal::setTenure( int tenure )
531 {
532  this->mTenure = tenure;
533 }
534 
535 void Pal::setCandListSize( double fact )
536 {
537  this->mCandListSize = fact;
538 }
539 
540 void Pal::setShowPartialLabels( bool show )
541 {
542  this->mShowPartialLabels = show;
543 }
544 
546 {
547  return mPlacementVersion;
548 }
549 
551 {
552  mPlacementVersion = placementVersion;
553 }
554 
555 bool Pal::candidatesAreConflicting( const LabelPosition *lp1, const LabelPosition *lp2 ) const
556 {
557  // we cache the value -- this can be costly to calculate, and we check this multiple times
558  // per candidate during the labeling problem solving
559 
560  // conflicts are commutative - so we always store them in the cache using the smaller id as the first element of the key pair
561  auto key = qMakePair( std::min( lp1->globalId(), lp2->globalId() ), std::max( lp1->globalId(), lp2->globalId() ) );
562  auto it = mCandidateConflicts.constFind( key );
563  if ( it != mCandidateConflicts.constEnd() )
564  return *it;
565 
566  const bool res = lp1->isInConflict( lp2 );
567  mCandidateConflicts.insert( key, res );
568  return res;
569 }
570 
571 int Pal::getMinIt()
572 {
573  return mTabuMaxIt;
574 }
575 
576 int Pal::getMaxIt()
577 {
578  return mTabuMinIt;
579 }
580 
582 {
583  return mShowPartialLabels;
584 }
A rtree spatial index for use in the pal labeling engine.
Definition: palrtree.h:36
The QgsAbstractLabelProvider class is an interface class.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
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
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:3222
PlacementEngineVersion
Placement engine version.
@ PlacementEngineVersion1
Version 1, matches placement from QGIS <= 3.10.1.
@ PlacementEngineVersion2
Version 2 (default for new projects since QGIS 3.12)
Placement
Placement modes which determine how label candidates are generated for a feature.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
Definition: qgsrectangle.h:325
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
static void addObstacleCostPenalty(pal::LabelPosition *lp, pal::FeaturePart *obstacle, Pal *pal)
Increase candidate's cost according to its collision with passed feature.
static void finalizeCandidatesCosts(Feats *feat, double bbx[4], double bby[4])
Sort candidates by costs, skip the worse ones, evaluate polygon candidates.
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.
Main class to handle feature.
Definition: feature.h:65
std::vector< std::unique_ptr< LabelPosition > > createCandidates(Pal *pal)
Generates a list of candidate positions for labels for this feature.
Definition: feature.cpp:1959
FeaturePart * getSelfObstacle(int i)
Gets hole (inner ring) - considered as obstacle.
Definition: feature.h:307
Thrown when trying to access an empty data set.
LabelPosition is a candidate feature label position.
Definition: labelposition.h:56
bool isInConflict(const LabelPosition *ls) const
Check whether or not this overlap with another labelPosition.
unsigned int globalId() const
Returns the global ID for the candidate, which is unique for a single run of the pal labelling engine...
A set of features which influence the labeling process.
Definition: layer.h:62
QMutex mMutex
Definition: layer.h:355
std::deque< std::unique_ptr< FeaturePart > > mFeatureParts
List of feature parts.
Definition: layer.h:325
QList< FeaturePart * > mObstacleParts
List of obstacle parts.
Definition: layer.h:328
QString name() const
Returns the layer's name.
Definition: layer.h:171
bool active() const
Returns whether the layer is currently active.
Definition: layer.h:207
bool displayAll() const
Definition: layer.h:95
bool mergeConnectedLines() const
Returns whether connected lines will be merged before labeling.
Definition: layer.h:265
void joinConnectedFeatures()
Join connected features with the same label text.
Definition: layer.cpp:302
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
Definition: layer.cpp:370
void setPlacementVersion(QgsLabelingEngineSettings::PlacementEngineVersion placementVersion)
Sets the placement engine version, which dictates how the label placement problem is solved.
Definition: pal.cpp:550
QgsLabelingEngineSettings::PlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
Definition: pal.cpp:545
void setShowPartialLabels(bool show)
Sets whether partial labels show be allowed.
Definition: pal.cpp:540
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
std::unique_ptr< Problem > extractProblem(const QgsRectangle &extent, const QgsGeometry &mapBoundary)
Extracts the labeling problem for the specified map extent - only features within this extent will be...
Definition: pal.cpp:483
void removeLayer(Layer *layer)
remove a layer
Definition: pal.cpp:60
bool candidatesAreConflicting(const LabelPosition *lp1, const LabelPosition *lp2) const
Returns true if a labelling candidate lp1 conflicts with lp2.
Definition: pal.cpp:555
QList< LabelPosition * > solveProblem(Problem *prob, 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:488
bool showPartialLabels() const
Returns whether partial labels should be allowed.
Definition: pal.cpp:581
Pal()
Create an new pal instance.
Definition: pal.cpp:50
bool isCanceled()
Check whether the job has been canceled.
Definition: pal.h:127
QgsRectangle boundingBox() const
Returns the point set bounding box.
Definition: pointset.h:162
Representation of a labeling problem.
Definition: problem.h:72
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:653
void chain_search()
Test with very-large scale neighborhood.
Definition: problem.cpp:567
void reduce()
Definition: problem.cpp:66
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
Definition: qgsgeos.h:84
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:79