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();
112 std::vector< FeaturePart * > allObstacleParts;
113 std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
118 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
119 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
120 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
121 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
125 std::list< std::unique_ptr< Feats > > features;
131 int obstacleCount = 0;
135 std::size_t previousFeatureCount = 0;
136 int previousObstacleCount = 0;
138 QStringList layersWithFeaturesInBBox;
140 QMutexLocker palLocker( &mMutex );
142 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
144 for (
const auto &it : mLayers )
150 Layer *layer = it.second.get();
162 feedback->emit candidateCreationAboutToBegin( it.first );
176 QMutexLocker locker( &layer->
mMutex );
179 std::size_t featureIndex = 0;
181 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
184 feedback->
setProgress( index * step + featureIndex * featureStep );
191 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
195 allObstacleParts.emplace_back( selfObstacle );
197 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
204 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
210 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared,
this]( std::unique_ptr< LabelPosition > &candidate )
212 if ( showPartialLabels() )
213 return !candidate->intersects( mapBoundaryPrepared.get() );
215 return !candidate->within( mapBoundaryPrepared.get() );
216 } ), candidates.end() );
221 if ( !candidates.empty() )
223 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
225 candidate->insertIntoIndex( allCandidatesFirstRound );
226 candidate->setGlobalId( mNextCandidateId++ );
232 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
233 ft->feature = featurePart.get();
235 ft->candidates = std::move( candidates );
236 ft->priority = featurePart->calculatePriority();
237 features.emplace_back( std::move( ft ) );
242 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
243 if ( !unplacedPosition )
246 if ( featurePart->feature()->allowDegradedPlacement() )
249 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
250 unplacedPosition->setGlobalId( mNextCandidateId++ );
251 candidates.emplace_back( std::move( unplacedPosition ) );
254 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
255 ft->feature = featurePart.get();
257 ft->candidates = std::move( candidates );
258 ft->priority = featurePart->calculatePriority();
259 features.emplace_back( std::move( ft ) );
264 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
278 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
279 allObstacleParts.emplace_back( obstaclePart );
288 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
290 layersWithFeaturesInBBox << layer->
name();
292 previousFeatureCount = features.size();
293 previousObstacleCount = obstacleCount;
296 feedback->emit candidateCreationFinished( it.first );
303 prob->mLayerCount = layersWithFeaturesInBBox.size();
304 prob->labelledLayersName = layersWithFeaturesInBBox;
306 prob->mFeatureCount = features.size();
307 prob->mTotalCandidates = 0;
308 prob->mFeatNbLp.resize( prob->mFeatureCount );
309 prob->mFeatStartId.resize( prob->mFeatureCount );
310 prob->mInactiveCost.resize( prob->mFeatureCount );
312 if ( !features.empty() )
315 feedback->emit obstacleCostingAboutToBegin();
318 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
320 for (
FeaturePart *obstaclePart : allObstacleParts )
329 allCandidatesFirstRound.
intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
337 if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
338 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
339 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
351 feedback->emit obstacleCostingFinished();
358 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
360 feedback->emit calculatingConflictsAboutToBegin();
363 for ( std::size_t i = 0; i < prob->mFeatureCount; i++ )
368 std::unique_ptr< Feats > feat = std::move( features.front() );
369 features.pop_front();
371 prob->mFeatStartId[i] = idlp;
372 prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
374 std::size_t maxCandidates = 0;
375 switch ( feat->feature->getGeosType() )
379 maxCandidates = feat->feature->maximumPointCandidates();
382 case GEOS_LINESTRING:
383 maxCandidates = feat->feature->maximumLineCandidates();
387 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
394 auto pruneHardConflicts = [&]
396 switch ( mPlacementVersion )
398 case Qgis::LabelPlacementEngineVersion::Version1:
401 case Qgis::LabelPlacementEngineVersion::Version2:
407 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
409 if ( candidate->hasHardObstacleConflict() )
414 } ), feat->candidates.end() );
416 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
418 switch ( feat->feature->feature()->overlapHandling() )
424 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
425 feat->candidates.clear();
442 switch ( feat->feature->feature()->overlapHandling() )
445 pruneHardConflicts();
453 if ( feat->candidates.empty() )
466 switch ( feat->feature->feature()->overlapHandling() )
472 pruneHardConflicts();
477 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
479 feat->candidates.resize( maxCandidates );
486 prob->mFeatNbLp[i] =
static_cast< int >( feat->candidates.size() );
487 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
490 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
492 candidate->insertIntoIndex( prob->allCandidatesIndex() );
493 candidate->setProblemIds(
static_cast< int >( i ), idlp++ );
495 features.emplace_back( std::move( feat ) );
499 feedback->emit calculatingConflictsFinished();
507 feedback->emit finalizingCandidatesAboutToBegin();
510 step = !features.empty() ? 100.0 / features.size() : 1;
511 while ( !features.empty() )
515 feedback->setProgress( step * index );
520 std::unique_ptr< Feats > feat = std::move( features.front() );
521 features.pop_front();
523 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
525 std::unique_ptr< LabelPosition > lp = std::move( candidate );
527 lp->resetNumOverlaps();
535 lp->getBoundingBox( amin, amax );
536 prob->allCandidatesIndex().intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
538 if ( candidatesAreConflicting( lp.get(), lp2 ) )
540 lp->incrementNumOverlaps();
547 nbOverlaps += lp->getNumOverlaps();
549 prob->addCandidatePosition( std::move( lp ) );
557 feedback->emit finalizingCandidatesFinished();
560 prob->mAllNblp = prob->mTotalCandidates;
561 prob->mNbOverlap = nbOverlaps;
569 fnIsCanceled = fnCanceled;
570 fnIsCanceledContext = context;
579 return QList<LabelPosition *>();
582 feedback->emit reductionAboutToBegin();
587 feedback->emit reductionFinished();
590 feedback->emit solvingPlacementAboutToBegin();
598 return QList<LabelPosition *>();
602 feedback->emit solvingPlacementFinished();
607void Pal::setMinIt(
int min_it )
613void Pal::setMaxIt(
int max_it )
619void Pal::setPopmusicR(
int r )
625void Pal::setEjChainDeg(
int degree )
627 this->mEjChainDeg = degree;
630void Pal::setTenure(
int tenure )
632 this->mTenure = tenure;
635void Pal::setCandListSize(
double fact )
637 this->mCandListSize = fact;
642 this->mShowPartialLabels = show;
647 return mPlacementVersion;
662 auto it = mCandidateConflicts.constFind( key );
663 if ( it != mCandidateConflicts.constEnd() )
667 mCandidateConflicts.insert( key, res );
671int Pal::getMinIt()
const
676int Pal::getMaxIt()
const
683 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.