QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
50using namespace pal;
51
52Pal::Pal()
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
60Pal::~Pal() = default;
61
62void 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
80Layer *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
94std::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
562{
563 fnIsCanceled = fnCanceled;
564 fnIsCanceledContext = context;
565}
566
567
568QList<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
601void Pal::setMinIt( int min_it )
602{
603 if ( min_it >= 0 )
604 mTabuMinIt = min_it;
605}
606
607void Pal::setMaxIt( int max_it )
608{
609 if ( max_it > 0 )
610 mTabuMaxIt = max_it;
611}
612
613void Pal::setPopmusicR( int r )
614{
615 if ( r > 0 )
616 mPopmusicR = r;
617}
618
619void Pal::setEjChainDeg( int degree )
620{
621 this->mEjChainDeg = degree;
622}
623
624void Pal::setTenure( int tenure )
625{
626 this->mTenure = tenure;
627}
628
629void Pal::setCandListSize( double fact )
630{
631 this->mCandListSize = fact;
632}
633
635{
636 this->mShowPartialLabels = show;
637}
638
640{
641 return mPlacementVersion;
642}
643
645{
646 mPlacementVersion = placementVersion;
647}
648
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
665int Pal::getMinIt() const
666{
667 return mTabuMaxIt;
668}
669
670int Pal::getMaxIt() const
671{
672 return mTabuMinIt;
673}
674
676{
677 return mShowPartialLabels;
678}
A rtree spatial index for use in the pal labeling engine.
Definition: palrtree.h:36
void insert(T *data, const QgsRectangle &bounds)
Inserts new data into the spatial index, with the specified bounds.
Definition: palrtree.h:59
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
LabelPlacement
Placement modes which determine how label candidates are generated for a feature.
Definition: qgis.h:561
@ AllowOverlapAtNoCost
Labels may freely overlap other labels, at no cost.
@ AllowOverlapIfRequired
Avoids overlapping labels when possible, but permit overlaps if labels for features cannot otherwise ...
@ PreventOverlap
Do not allow labels to overlap other labels.
The QgsAbstractLabelProvider class is an interface class.
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
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:220
static GEOSContextHandle_t getGEOSHandler()
Definition: qgsgeos.cpp:3446
QgsFeedback subclass for granular reporting of labeling engine progress.
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)
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
Contains information about the context of a rendering operation.
QgsFeedback * feedback() const
Returns the feedback object that can be queried regularly during rendering to check if rendering shou...
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
FeaturePart * getSelfObstacle(int i)
Gets hole (inner ring) - considered as obstacle.
Definition: feature.h:308
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:63
QMutex mMutex
Definition: layer.h:345
std::deque< std::unique_ptr< FeaturePart > > mFeatureParts
List of feature parts.
Definition: layer.h:316
QList< FeaturePart * > mObstacleParts
List of obstacle parts.
Definition: layer.h:319
QString name() const
Returns the layer's name.
Definition: layer.h:162
bool active() const
Returns whether the layer is currently active.
Definition: layer.h:198
bool mergeConnectedLines() const
Returns whether connected lines will be merged before labeling.
Definition: layer.h:256
void joinConnectedFeatures()
Join connected features with the same label text.
Definition: layer.cpp:297
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
Definition: layer.cpp:365
void setPlacementVersion(QgsLabelingEngineSettings::PlacementEngineVersion placementVersion)
Sets the placement engine version, which dictates how the label placement problem is solved.
Definition: pal.cpp:644
QgsLabelingEngineSettings::PlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
Definition: pal.cpp:639
void setShowPartialLabels(bool show)
Sets whether partial labels show be allowed.
Definition: pal.cpp:634
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
void removeLayer(Layer *layer)
remove a layer
Definition: pal.cpp:62
bool candidatesAreConflicting(const LabelPosition *lp1, const LabelPosition *lp2) const
Returns true if a labelling candidate lp1 conflicts with lp2.
Definition: pal.cpp:649
bool(* FnIsCanceled)(void *ctx)
Definition: pal.h:120
bool showPartialLabels() const
Returns whether partial labels should be allowed.
Definition: pal.cpp:675
bool isCanceled()
Check whether the job has been canceled.
Definition: pal.h:126
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
void registerCancellationCallback(FnIsCanceled fnCanceled, void *context)
Register a function that returns whether this job has been canceled - PAL calls it during the computa...
Definition: pal.cpp:561
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
add a new layer
Definition: pal.cpp:80
QgsRectangle boundingBox() const
Returns the point set bounding box.
Definition: pointset.h:163
Representation of a labeling problem.
Definition: problem.h:73
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
void chainSearch(QgsRenderContext &context)
Test with very-large scale neighborhood.
Definition: problem.cpp:573
void reduce()
Definition: problem.cpp:66
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
Definition: qgsgeos.h:79
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.
Definition: qgsgeos.h:74