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();
69 for (
auto it = mLayers.begin(); it != mLayers.end(); ++it )
71 if ( it->second.get() == layer )
84 Q_ASSERT( mLayers.find( provider ) == mLayers.end() );
86 std::unique_ptr< Layer > layer = std::make_unique< Layer >( provider, layerName, arrangement, defaultPriority, active, toLabel,
this );
87 Layer *res = layer.get();
106 std::vector< FeaturePart * > allObstacleParts;
107 std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
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();
119 std::list< std::unique_ptr< Feats > > features;
125 int obstacleCount = 0;
129 std::size_t previousFeatureCount = 0;
130 int previousObstacleCount = 0;
132 QStringList layersWithFeaturesInBBox;
134 QMutexLocker palLocker( &mMutex );
136 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
138 for (
const auto &it : mLayers )
144 Layer *layer = it.second.get();
156 feedback->emit candidateCreationAboutToBegin( it.first );
170 QMutexLocker locker( &layer->
mMutex );
173 std::size_t featureIndex = 0;
175 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
178 feedback->
setProgress( index * step + featureIndex * featureStep );
185 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
189 allObstacleParts.emplace_back( selfObstacle );
191 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
198 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
204 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared,
this]( std::unique_ptr< LabelPosition > &candidate )
206 if ( showPartialLabels() )
207 return !candidate->intersects( mapBoundaryPrepared.get() );
209 return !candidate->within( mapBoundaryPrepared.get() );
210 } ), candidates.end() );
215 if ( !candidates.empty() )
217 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
219 candidate->insertIntoIndex( allCandidatesFirstRound );
220 candidate->setGlobalId( mNextCandidateId++ );
226 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
227 ft->feature = featurePart.get();
229 ft->candidates = std::move( candidates );
230 ft->priority = featurePart->calculatePriority();
231 features.emplace_back( std::move( ft ) );
236 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
237 if ( !unplacedPosition )
240 if ( featurePart->feature()->allowDegradedPlacement() )
243 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
244 unplacedPosition->setGlobalId( mNextCandidateId++ );
245 candidates.emplace_back( std::move( unplacedPosition ) );
248 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
249 ft->feature = featurePart.get();
251 ft->candidates = std::move( candidates );
252 ft->priority = featurePart->calculatePriority();
253 features.emplace_back( std::move( ft ) );
258 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
272 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
273 allObstacleParts.emplace_back( obstaclePart );
282 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
284 layersWithFeaturesInBBox << layer->
name();
286 previousFeatureCount = features.size();
287 previousObstacleCount = obstacleCount;
290 feedback->emit candidateCreationFinished( it.first );
297 prob->mLayerCount = layersWithFeaturesInBBox.size();
298 prob->labelledLayersName = layersWithFeaturesInBBox;
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 );
306 if ( !features.empty() )
309 feedback->emit obstacleCostingAboutToBegin();
312 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
314 for (
FeaturePart *obstaclePart : allObstacleParts )
323 allCandidatesFirstRound.
intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
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() ) ) ) )
345 feedback->emit obstacleCostingFinished();
352 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
354 feedback->emit calculatingConflictsAboutToBegin();
357 for ( std::size_t i = 0; i < prob->mFeatureCount; i++ )
362 std::unique_ptr< Feats > feat = std::move( features.front() );
363 features.pop_front();
365 prob->mFeatStartId[i] = idlp;
366 prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
368 std::size_t maxCandidates = 0;
369 switch ( feat->feature->getGeosType() )
373 maxCandidates = feat->feature->maximumPointCandidates();
376 case GEOS_LINESTRING:
377 maxCandidates = feat->feature->maximumLineCandidates();
381 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
388 auto pruneHardConflicts = [&]
390 switch ( mPlacementVersion )
401 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
403 if ( candidate->hasHardObstacleConflict() )
408 } ), feat->candidates.end() );
410 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
412 switch ( feat->feature->feature()->overlapHandling() )
418 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
419 feat->candidates.clear();
436 switch ( feat->feature->feature()->overlapHandling() )
439 pruneHardConflicts();
447 if ( feat->candidates.empty() )
460 switch ( feat->feature->feature()->overlapHandling() )
466 pruneHardConflicts();
471 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
473 feat->candidates.resize( maxCandidates );
480 prob->mFeatNbLp[i] =
static_cast< int >( feat->candidates.size() );
481 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
484 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
486 candidate->insertIntoIndex( prob->allCandidatesIndex() );
487 candidate->setProblemIds(
static_cast< int >( i ), idlp++ );
489 features.emplace_back( std::move( feat ) );
493 feedback->emit calculatingConflictsFinished();
501 feedback->emit finalizingCandidatesAboutToBegin();
504 step = !features.empty() ? 100.0 / features.size() : 1;
505 while ( !features.empty() )
509 feedback->setProgress( step * index );
514 std::unique_ptr< Feats > feat = std::move( features.front() );
515 features.pop_front();
517 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
519 std::unique_ptr< LabelPosition > lp = std::move( candidate );
521 lp->resetNumOverlaps();
529 lp->getBoundingBox( amin, amax );
530 prob->allCandidatesIndex().intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
532 if ( candidatesAreConflicting( lp.get(), lp2 ) )
534 lp->incrementNumOverlaps();
541 nbOverlaps += lp->getNumOverlaps();
543 prob->addCandidatePosition( std::move( lp ) );
551 feedback->emit finalizingCandidatesFinished();
554 prob->mAllNblp = prob->mTotalCandidates;
555 prob->mNbOverlap = nbOverlaps;
563 fnIsCanceled = fnCanceled;
564 fnIsCanceledContext = context;
573 return QList<LabelPosition *>();
576 feedback->emit reductionAboutToBegin();
581 feedback->emit reductionFinished();
584 feedback->emit solvingPlacementAboutToBegin();
592 return QList<LabelPosition *>();
596 feedback->emit solvingPlacementFinished();
601void Pal::setMinIt(
int min_it )
607void Pal::setMaxIt(
int max_it )
613void Pal::setPopmusicR(
int r )
619void Pal::setEjChainDeg(
int degree )
621 this->mEjChainDeg = degree;
624void Pal::setTenure(
int tenure )
626 this->mTenure = tenure;
629void Pal::setCandListSize(
double fact )
631 this->mCandListSize = fact;
636 this->mShowPartialLabels = show;
641 return mPlacementVersion;
656 auto it = mCandidateConflicts.constFind( key );
657 if ( it != mCandidateConflicts.constEnd() )
661 mCandidateConflicts.insert( key, res );
665int Pal::getMinIt()
const
670int Pal::getMaxIt()
const
677 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.
@ 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.
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.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width 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...
This class is a composition of two QSettings instances:
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.
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(QgsLabelingEngineSettings::PlacementEngineVersion placementVersion)
Sets the placement engine version, which dictates how the label placement problem is solved.
QgsLabelingEngineSettings::PlacementEngineVersion placementVersion() const
Returns 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...
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.
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.