75 for (
auto it = mLayers.begin(); it != mLayers.end(); ++it )
77 if ( it->second.get() == layer )
90 Q_ASSERT( mLayers.find( provider ) == mLayers.end() );
92 std::unique_ptr< Layer > layer = std::make_unique< Layer >( provider, layerName, arrangement, defaultPriority, active, toLabel,
this );
93 Layer *res = layer.get();
113 std::vector< FeaturePart * > allObstacleParts;
114 std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
119 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
120 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
121 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
122 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
126 std::list< std::unique_ptr< Feats > > features;
132 int obstacleCount = 0;
136 std::size_t previousFeatureCount = 0;
137 int previousObstacleCount = 0;
139 QStringList layersWithFeaturesInBBox;
141 QMutexLocker palLocker( &mMutex );
143 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
145 for (
const auto &it : mLayers )
151 Layer *layer = it.second.get();
163 feedback->emit candidateCreationAboutToBegin( it.first );
177 QMutexLocker locker( &layer->
mMutex );
180 std::size_t featureIndex = 0;
182 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
185 feedback->
setProgress( index * step + featureIndex * featureStep );
192 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
196 allObstacleParts.emplace_back( selfObstacle );
198 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
205 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
211 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared,
this]( std::unique_ptr< LabelPosition > &candidate )
213 if ( showPartialLabels() )
214 return !candidate->intersects( mapBoundaryPrepared.get() );
216 return !candidate->within( mapBoundaryPrepared.get() );
217 } ), candidates.end() );
222 if ( !candidates.empty() )
224 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
226 candidate->insertIntoIndex( allCandidatesFirstRound );
227 candidate->setGlobalId( mNextCandidateId++ );
233 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
234 ft->feature = featurePart.get();
236 ft->candidates = std::move( candidates );
237 ft->priority = featurePart->calculatePriority();
238 features.emplace_back( std::move( ft ) );
243 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
244 if ( !unplacedPosition )
247 if ( featurePart->feature()->allowDegradedPlacement() )
250 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
251 unplacedPosition->setGlobalId( mNextCandidateId++ );
252 candidates.emplace_back( std::move( unplacedPosition ) );
255 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
256 ft->feature = featurePart.get();
258 ft->candidates = std::move( candidates );
259 ft->priority = featurePart->calculatePriority();
260 features.emplace_back( std::move( ft ) );
265 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
279 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
280 allObstacleParts.emplace_back( obstaclePart );
289 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
291 layersWithFeaturesInBBox << layer->
name();
293 previousFeatureCount = features.size();
294 previousObstacleCount = obstacleCount;
297 feedback->emit candidateCreationFinished( it.first );
304 prob->mLayerCount = layersWithFeaturesInBBox.size();
305 prob->labelledLayersName = layersWithFeaturesInBBox;
307 prob->mFeatureCount = features.size();
308 prob->mTotalCandidates = 0;
309 prob->mFeatNbLp.resize( prob->mFeatureCount );
310 prob->mFeatStartId.resize( prob->mFeatureCount );
311 prob->mInactiveCost.resize( prob->mFeatureCount );
313 if ( !features.empty() )
316 feedback->emit obstacleCostingAboutToBegin();
319 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
321 for (
FeaturePart *obstaclePart : allObstacleParts )
330 allCandidatesFirstRound.
intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
338 if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
339 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
340 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
352 feedback->emit obstacleCostingFinished();
359 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
361 feedback->emit calculatingConflictsAboutToBegin();
364 for ( std::size_t i = 0; i < prob->mFeatureCount; i++ )
369 std::unique_ptr< Feats > feat = std::move( features.front() );
370 features.pop_front();
372 prob->mFeatStartId[i] = idlp;
373 prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
375 std::size_t maxCandidates = 0;
376 switch ( feat->feature->getGeosType() )
380 maxCandidates = feat->feature->maximumPointCandidates();
383 case GEOS_LINESTRING:
384 maxCandidates = feat->feature->maximumLineCandidates();
388 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
395 auto pruneHardConflicts = [&]
397 switch ( mPlacementVersion )
399 case Qgis::LabelPlacementEngineVersion::Version1:
402 case Qgis::LabelPlacementEngineVersion::Version2:
408 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
410 if ( candidate->hasHardObstacleConflict() )
415 } ), feat->candidates.end() );
417 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
419 switch ( feat->feature->feature()->overlapHandling() )
425 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
426 feat->candidates.clear();
443 switch ( feat->feature->feature()->overlapHandling() )
446 pruneHardConflicts();
454 if ( feat->candidates.empty() )
467 switch ( feat->feature->feature()->overlapHandling() )
473 pruneHardConflicts();
478 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
480 feat->candidates.resize( maxCandidates );
487 prob->mFeatNbLp[i] =
static_cast< int >( feat->candidates.size() );
488 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
491 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
493 candidate->insertIntoIndex( prob->allCandidatesIndex() );
494 candidate->setProblemIds(
static_cast< int >( i ), idlp++ );
496 features.emplace_back( std::move( feat ) );
500 feedback->emit calculatingConflictsFinished();
508 feedback->emit finalizingCandidatesAboutToBegin();
511 step = !features.empty() ? 100.0 / features.size() : 1;
512 while ( !features.empty() )
516 feedback->setProgress( step * index );
521 std::unique_ptr< Feats > feat = std::move( features.front() );
522 features.pop_front();
524 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
526 std::unique_ptr< LabelPosition > lp = std::move( candidate );
528 lp->resetNumOverlaps();
536 lp->getBoundingBox( amin, amax );
537 prob->allCandidatesIndex().intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
539 if ( candidatesAreConflicting( lp.get(), lp2 ) )
541 lp->incrementNumOverlaps();
548 nbOverlaps += lp->getNumOverlaps();
550 prob->addCandidatePosition( std::move( lp ) );
558 feedback->emit finalizingCandidatesFinished();
561 prob->mAllNblp = prob->mTotalCandidates;
562 prob->mNbOverlap = nbOverlaps;
570 fnIsCanceled = fnCanceled;
571 fnIsCanceledContext = context;
580 return QList<LabelPosition *>();
583 feedback->emit reductionAboutToBegin();
588 feedback->emit reductionFinished();
591 feedback->emit solvingPlacementAboutToBegin();
599 return QList<LabelPosition *>();
603 feedback->emit solvingPlacementFinished();
608void Pal::setMinIt(
int min_it )
614void Pal::setMaxIt(
int max_it )
620void Pal::setPopmusicR(
int r )
626void Pal::setEjChainDeg(
int degree )
628 this->mEjChainDeg = degree;
631void Pal::setTenure(
int tenure )
633 this->mTenure = tenure;
636void Pal::setCandListSize(
double fact )
638 this->mCandListSize = fact;
643 this->mShowPartialLabels = show;
648 return mPlacementVersion;
663 auto it = mCandidateConflicts.constFind( key );
664 if ( it != mCandidateConflicts.constEnd() )
668 mCandidateConflicts.insert( key, res );
672int Pal::getMinIt()
const
677int Pal::getMaxIt()
const
684 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.
LabelPlacementEngineVersion
Labeling placement engine version.
@ 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 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...
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.