76 for (
auto it = mLayers.begin(); it != mLayers.end(); ++it )
78 if ( it->second.get() == layer )
91 Q_ASSERT( mLayers.find( provider ) == mLayers.end() );
93 std::unique_ptr< Layer > layer = std::make_unique< Layer >( provider, layerName, arrangement, defaultPriority, active, toLabel,
this );
94 Layer *res = layer.get();
106 std::unique_ptr< QgsScopedRuntimeProfile > extractionProfile;
109 extractionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Placing labels" ), QStringLiteral(
"rendering" ) );
120 std::vector< FeaturePart * > allObstacleParts;
121 std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
126 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
127 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
128 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
129 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
133 std::list< std::unique_ptr< Feats > > features;
139 int obstacleCount = 0;
143 std::size_t previousFeatureCount = 0;
144 int previousObstacleCount = 0;
146 QStringList layersWithFeaturesInBBox;
148 QMutexLocker palLocker( &mMutex );
150 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
152 std::unique_ptr< QgsScopedRuntimeProfile > candidateProfile;
155 candidateProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Generating label candidates" ), QStringLiteral(
"rendering" ) );
158 for (
const auto &it : mLayers )
164 Layer *layer = it.second.get();
176 feedback->emit candidateCreationAboutToBegin( it.first );
178 std::unique_ptr< QgsScopedRuntimeProfile > layerProfile;
181 layerProfile = std::make_unique< QgsScopedRuntimeProfile >( it.first->providerId(), QStringLiteral(
"rendering" ) );
196 QMutexLocker locker( &layer->
mMutex );
199 std::size_t featureIndex = 0;
201 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
204 feedback->
setProgress( index * step + featureIndex * featureStep );
211 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
215 allObstacleParts.emplace_back( selfObstacle );
217 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
224 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
230 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared,
this]( std::unique_ptr< LabelPosition > &candidate )
232 if ( showPartialLabels() )
233 return !candidate->intersects( mapBoundaryPrepared.get() );
235 return !candidate->within( mapBoundaryPrepared.get() );
236 } ), candidates.end() );
241 if ( !candidates.empty() )
243 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
245 candidate->insertIntoIndex( allCandidatesFirstRound );
246 candidate->setGlobalId( mNextCandidateId++ );
252 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
253 ft->feature = featurePart.get();
255 ft->candidates = std::move( candidates );
256 ft->priority = featurePart->calculatePriority();
257 features.emplace_back( std::move( ft ) );
262 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
263 if ( !unplacedPosition )
266 if ( featurePart->feature()->allowDegradedPlacement() )
269 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
270 unplacedPosition->setGlobalId( mNextCandidateId++ );
271 candidates.emplace_back( std::move( unplacedPosition ) );
274 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
275 ft->feature = featurePart.get();
277 ft->candidates = std::move( candidates );
278 ft->priority = featurePart->calculatePriority();
279 features.emplace_back( std::move( ft ) );
284 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
298 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
299 allObstacleParts.emplace_back( obstaclePart );
308 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
310 layersWithFeaturesInBBox << layer->
name();
312 previousFeatureCount = features.size();
313 previousObstacleCount = obstacleCount;
316 feedback->emit candidateCreationFinished( it.first );
319 candidateProfile.reset();
326 prob->mLayerCount = layersWithFeaturesInBBox.size();
327 prob->labelledLayersName = layersWithFeaturesInBBox;
329 prob->mFeatureCount = features.size();
330 prob->mTotalCandidates = 0;
331 prob->mFeatNbLp.resize( prob->mFeatureCount );
332 prob->mFeatStartId.resize( prob->mFeatureCount );
333 prob->mInactiveCost.resize( prob->mFeatureCount );
335 if ( !features.empty() )
338 feedback->emit obstacleCostingAboutToBegin();
340 std::unique_ptr< QgsScopedRuntimeProfile > costingProfile;
343 costingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Assigning label costs" ), QStringLiteral(
"rendering" ) );
348 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
350 for (
FeaturePart *obstaclePart : allObstacleParts )
359 allCandidatesFirstRound.
intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
367 if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
368 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
369 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
381 feedback->emit obstacleCostingFinished();
382 costingProfile.reset();
389 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
391 feedback->emit calculatingConflictsAboutToBegin();
393 std::unique_ptr< QgsScopedRuntimeProfile > conflictProfile;
396 conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Calculating conflicts" ), QStringLiteral(
"rendering" ) );
400 for ( std::size_t i = 0; i < prob->mFeatureCount; i++ )
405 std::unique_ptr< Feats > feat = std::move( features.front() );
406 features.pop_front();
408 prob->mFeatStartId[i] = idlp;
409 prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
411 std::size_t maxCandidates = 0;
412 switch ( feat->feature->getGeosType() )
416 maxCandidates = feat->feature->maximumPointCandidates();
419 case GEOS_LINESTRING:
420 maxCandidates = feat->feature->maximumLineCandidates();
424 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
431 auto pruneHardConflicts = [&]
433 switch ( mPlacementVersion )
444 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
446 if ( candidate->hasHardObstacleConflict() )
451 } ), feat->candidates.end() );
453 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
455 switch ( feat->feature->feature()->overlapHandling() )
461 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
462 feat->candidates.clear();
479 switch ( feat->feature->feature()->overlapHandling() )
482 pruneHardConflicts();
490 if ( feat->candidates.empty() )
503 switch ( feat->feature->feature()->overlapHandling() )
509 pruneHardConflicts();
514 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
516 feat->candidates.resize( maxCandidates );
523 prob->mFeatNbLp[i] =
static_cast< int >( feat->candidates.size() );
524 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
527 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
529 candidate->insertIntoIndex( prob->allCandidatesIndex() );
530 candidate->setProblemIds(
static_cast< int >( i ), idlp++ );
532 features.emplace_back( std::move( feat ) );
536 feedback->emit calculatingConflictsFinished();
538 conflictProfile.reset();
546 feedback->emit finalizingCandidatesAboutToBegin();
548 std::unique_ptr< QgsScopedRuntimeProfile > finalizingProfile;
551 finalizingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Finalizing labels" ), QStringLiteral(
"rendering" ) );
555 step = !features.empty() ? 100.0 / features.size() : 1;
556 while ( !features.empty() )
560 feedback->setProgress( step * index );
565 std::unique_ptr< Feats > feat = std::move( features.front() );
566 features.pop_front();
568 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
570 std::unique_ptr< LabelPosition > lp = std::move( candidate );
572 lp->resetNumOverlaps();
580 lp->getBoundingBox( amin, amax );
581 prob->allCandidatesIndex().intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
583 if ( candidatesAreConflicting( lp.get(), lp2 ) )
585 lp->incrementNumOverlaps();
592 nbOverlaps += lp->getNumOverlaps();
594 prob->addCandidatePosition( std::move( lp ) );
602 feedback->emit finalizingCandidatesFinished();
604 finalizingProfile.reset();
607 prob->mAllNblp = prob->mTotalCandidates;
608 prob->mNbOverlap = nbOverlaps;
616 fnIsCanceled = fnCanceled;
617 fnIsCanceledContext = context;
626 return QList<LabelPosition *>();
629 std::unique_ptr< QgsScopedRuntimeProfile > calculatingProfile;
632 calculatingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Calculating optimal labeling" ), QStringLiteral(
"rendering" ) );
636 feedback->emit reductionAboutToBegin();
639 std::unique_ptr< QgsScopedRuntimeProfile > reductionProfile;
642 reductionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Reducing labeling" ), QStringLiteral(
"rendering" ) );
649 feedback->emit reductionFinished();
652 feedback->emit solvingPlacementAboutToBegin();
655 std::unique_ptr< QgsScopedRuntimeProfile > solvingProfile;
658 solvingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Solving labeling" ), QStringLiteral(
"rendering" ) );
666 return QList<LabelPosition *>();
671 feedback->emit solvingPlacementFinished();
676void Pal::setMinIt(
int min_it )
682void Pal::setMaxIt(
int max_it )
688void Pal::setPopmusicR(
int r )
694void Pal::setEjChainDeg(
int degree )
696 this->mEjChainDeg = degree;
699void Pal::setTenure(
int tenure )
701 this->mTenure = tenure;
704void Pal::setCandListSize(
double fact )
706 this->mCandListSize = fact;
711 this->mShowPartialLabels = show;
716 return mPlacementVersion;
731 auto it = mCandidateConflicts.constFind( key );
732 if ( it != mCandidateConflicts.constEnd() )
736 mCandidateConflicts.insert( key, res );
740int Pal::getMinIt()
const
745int Pal::getMaxIt()
const
752 return mShowPartialLabels;
A rtree spatial index for use in the pal labeling engine.
void insert(T *data, const QgsRectangle &bounds)
Inserts new data into the spatial index, with the specified bounds.
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.
LabelPlacement
Placement modes which determine how label candidates are generated for a feature.
@ RecordProfile
Enable run-time profiling while rendering (since QGIS 3.34)
LabelPlacementEngineVersion
Labeling placement engine version.
@ Version2
Version 2 (default for new projects since QGIS 3.12)
@ Version1
Version 1, matches placement from QGIS <= 3.10.1.
@ 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.
A geometry is the spatial representation of a feature.
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...
static GEOSContextHandle_t getGEOSHandler()
QgsFeedback subclass for granular reporting of labeling engine progress.
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
double width() const
Returns the width of the rectangle.
double xMaximum() const
Returns the x maximum value (right side of rectangle).
double yMaximum() const
Returns the y maximum value (top side of rectangle).
double height() const
Returns the height of the rectangle.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
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...
Qgis::RenderContextFlags flags() const
Returns combination of flags used for rendering.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
An integer settings entry.
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.
FeaturePart * getSelfObstacle(int i)
Gets hole (inner ring) - considered as obstacle.
Thrown when trying to access an empty data set.
LabelPosition is a candidate feature label position.
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.
std::deque< std::unique_ptr< FeaturePart > > mFeatureParts
List of feature parts.
QList< FeaturePart * > mObstacleParts
List of obstacle parts.
QString name() const
Returns the layer's name.
bool active() const
Returns whether the layer is currently active.
bool mergeConnectedLines() const
Returns whether connected lines will be merged before labeling.
void joinConnectedFeatures()
Join connected features with the same label text.
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
void setPlacementVersion(Qgis::LabelPlacementEngineVersion placementVersion)
Sets the placement engine version, which dictates how the label placement problem is solved.
void setShowPartialLabels(bool show)
Sets whether partial labels show be allowed.
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...
Qgis::LabelPlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
void removeLayer(Layer *layer)
remove a layer
bool candidatesAreConflicting(const LabelPosition *lp1, const LabelPosition *lp2) const
Returns true if a labelling candidate lp1 conflicts with lp2.
bool(* FnIsCanceled)(void *ctx)
bool showPartialLabels() const
Returns whether partial labels should be allowed.
static const QgsSettingsEntryInteger * settingsRenderingLabelCandidatesLimitLines
static const QgsSettingsEntryInteger * settingsRenderingLabelCandidatesLimitPoints
static const QgsSettingsEntryInteger * settingsRenderingLabelCandidatesLimitPolygons
bool isCanceled()
Check whether the job has been canceled.
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...
void registerCancellationCallback(FnIsCanceled fnCanceled, void *context)
Register a function that returns whether this job has been canceled - PAL calls it during the computa...
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, Qgis::LabelPlacement arrangement, double defaultPriority, bool active, bool toLabel)
add a new layer
QgsRectangle boundingBox() const
Returns the point set bounding box.
Representation of a labeling problem.
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...
void chainSearch(QgsRenderContext &context)
Test with very-large scale neighborhood.
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.