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, displayAll );
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 )
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
330 if ( ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
331 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
343 feedback->emit obstacleCostingFinished();
350 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
352 feedback->emit calculatingConflictsAboutToBegin();
355 for ( std::size_t i = 0; i < prob->mFeatureCount; i++ )
360 std::unique_ptr< Feats > feat = std::move( features.front() );
361 features.pop_front();
363 prob->mFeatStartId[i] = idlp;
364 prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
366 std::size_t maxCandidates = 0;
367 switch ( feat->feature->getGeosType() )
371 maxCandidates = feat->feature->maximumPointCandidates();
374 case GEOS_LINESTRING:
375 maxCandidates = feat->feature->maximumLineCandidates();
379 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
386 auto pruneHardConflicts = [&]
388 switch ( mPlacementVersion )
399 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
401 if ( candidate->hasHardObstacleConflict() )
406 } ), feat->candidates.end() );
408 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() && !feat->feature->layer()->displayAll() )
412 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
413 feat->candidates.clear();
421 if ( !feat->feature->layer()->displayAll() )
423 pruneHardConflicts();
426 if ( feat->candidates.empty() )
439 if ( feat->feature->layer()->displayAll() )
441 pruneHardConflicts();
446 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
448 feat->candidates.resize( maxCandidates );
455 prob->mFeatNbLp[i] =
static_cast< int >( feat->candidates.size() );
456 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
459 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
461 candidate->insertIntoIndex( prob->allCandidatesIndex() );
462 candidate->setProblemIds(
static_cast< int >( i ), idlp++ );
464 features.emplace_back( std::move( feat ) );
468 feedback->emit calculatingConflictsFinished();
476 feedback->emit finalizingCandidatesAboutToBegin();
479 step = !features.empty() ? 100.0 / features.size() : 1;
480 while ( !features.empty() )
484 feedback->setProgress( step * index );
489 std::unique_ptr< Feats > feat = std::move( features.front() );
490 features.pop_front();
492 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
494 std::unique_ptr< LabelPosition > lp = std::move( candidate );
496 lp->resetNumOverlaps();
504 lp->getBoundingBox( amin, amax );
505 prob->allCandidatesIndex().intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
507 if ( candidatesAreConflicting( lp.get(), lp2 ) )
509 lp->incrementNumOverlaps();
516 nbOverlaps += lp->getNumOverlaps();
518 prob->addCandidatePosition( std::move( lp ) );
526 feedback->emit finalizingCandidatesFinished();
529 prob->mAllNblp = prob->mTotalCandidates;
530 prob->mNbOverlap = nbOverlaps;
536 void Pal::registerCancellationCallback( Pal::FnIsCanceled fnCanceled,
void *context )
538 fnIsCanceled = fnCanceled;
539 fnIsCanceledContext = context;
548 return QList<LabelPosition *>();
551 feedback->emit reductionAboutToBegin();
556 feedback->emit reductionFinished();
559 feedback->emit solvingPlacementAboutToBegin();
567 return QList<LabelPosition *>();
571 feedback->emit solvingPlacementFinished();
576 void Pal::setMinIt(
int min_it )
582 void Pal::setMaxIt(
int max_it )
588 void Pal::setPopmusicR(
int r )
594 void Pal::setEjChainDeg(
int degree )
596 this->mEjChainDeg = degree;
599 void Pal::setTenure(
int tenure )
601 this->mTenure = tenure;
604 void Pal::setCandListSize(
double fact )
606 this->mCandListSize = fact;
611 this->mShowPartialLabels = show;
616 return mPlacementVersion;
631 auto it = mCandidateConflicts.constFind( key );
632 if ( it != mCandidateConflicts.constEnd() )
636 mCandidateConflicts.insert( key, res );
652 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.
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)
Placement
Placement modes which determine how label candidates are generated for a feature.
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...
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, QgsPalLayerSettings::Placement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll=false)
add a new layer
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 showPartialLabels() const
Returns whether partial labels should be allowed.
Pal()
Create an new pal instance.
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...
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.