115  std::unique_ptr< QgsScopedRuntimeProfile > extractionProfile;
 
  118    extractionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( 
"Placing labels" ), QStringLiteral( 
"rendering" ) );
 
  129  std::vector< FeaturePart * > allObstacleParts;
 
  130  auto prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
 
  135  bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
 
  136  bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
 
  137  bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
 
  138  bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
 
  142  std::list< std::unique_ptr< Feats > > features;
 
  146  geos::prepared_unique_ptr mapBoundaryPrepared( GEOSPrepare_r( 
QgsGeosContext::get(), mapBoundaryGeos.get() ) );
 
  148  int obstacleCount = 0;
 
  152  std::size_t previousFeatureCount = 0;
 
  153  int previousObstacleCount = 0;
 
  155  QStringList layersWithFeaturesInBBox;
 
  157  QMutexLocker palLocker( &mMutex );
 
  159  double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
 
  161  std::unique_ptr< QgsScopedRuntimeProfile > candidateProfile;
 
  164    candidateProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( 
"Generating label candidates" ), QStringLiteral( 
"rendering" ) );
 
  167  for ( 
auto it = mLayers.rbegin(); it != mLayers.rend(); ++it )
 
  173    Layer *layer = it->second.get();
 
  185      feedback->emit candidateCreationAboutToBegin( it->first );
 
  187    std::unique_ptr< QgsScopedRuntimeProfile > layerProfile;
 
  190      layerProfile = std::make_unique< QgsScopedRuntimeProfile >( it->first->providerId(), QStringLiteral( 
"rendering" ) );
 
  205    QMutexLocker locker( &layer->
mMutex );
 
  208    std::size_t featureIndex = 0;
 
  210    for ( 
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
 
  213        feedback->
setProgress( index * step + featureIndex * featureStep );
 
  220      for ( 
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
 
  224        allObstacleParts.emplace_back( selfObstacle );
 
  226        if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
 
  233      std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates( 
this );
 
  241      candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared, &labelContext, 
this]( std::unique_ptr< LabelPosition > &candidate )
 
  243        if ( showPartialLabels() )
 
  245          if ( !candidate->intersects( mapBoundaryPrepared.get() ) )
 
  250          if ( !candidate->within( mapBoundaryPrepared.get() ) )
 
  256          if ( rule->candidateIsIllegal( candidate.get(), labelContext ) )
 
  262      } ), candidates.end() );
 
  267      if ( !candidates.empty() )
 
  269        for ( std::unique_ptr< LabelPosition > &candidate : candidates )
 
  271          candidate->insertIntoIndex( allCandidatesFirstRound, 
this );
 
  272          candidate->setGlobalId( mNextCandidateId++ );
 
  278        auto ft = std::make_unique< Feats >();
 
  279        ft->feature = featurePart.get();
 
  281        ft->candidates = std::move( candidates );
 
  282        ft->priority = featurePart->calculatePriority();
 
  283        features.emplace_back( std::move( ft ) );
 
  288        std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
 
  289        if ( !unplacedPosition )
 
  292        if ( featurePart->feature()->allowDegradedPlacement() )
 
  295          unplacedPosition->insertIntoIndex( allCandidatesFirstRound, 
this );
 
  296          unplacedPosition->setGlobalId( mNextCandidateId++ );
 
  297          candidates.emplace_back( std::move( unplacedPosition ) );
 
  300          auto ft = std::make_unique< Feats >();
 
  301          ft->feature = featurePart.get();
 
  303          ft->candidates = std::move( candidates );
 
  304          ft->priority = featurePart->calculatePriority();
 
  305          features.emplace_back( std::move( ft ) );
 
  310          prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
 
  318    for ( 
FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
 
  324      obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
 
  325      allObstacleParts.emplace_back( obstaclePart );
 
  334    if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
 
  336      layersWithFeaturesInBBox << layer->name();
 
  338    previousFeatureCount = features.size();
 
  339    previousObstacleCount = obstacleCount;
 
  342      feedback->emit candidateCreationFinished( it->first );
 
  345  candidateProfile.reset();
 
  352  prob->mLayerCount = layersWithFeaturesInBBox.size();
 
  353  prob->labelledLayersName = layersWithFeaturesInBBox;
 
  355  prob->mFeatureCount = features.size();
 
  356  prob->mTotalCandidates = 0;
 
  357  prob->mCandidateCountForFeature.resize( prob->mFeatureCount );
 
  358  prob->mFirstCandidateIndexForFeature.resize( prob->mFeatureCount );
 
  359  prob->mUnlabeledCostForFeature.resize( prob->mFeatureCount );
 
  361  if ( !features.empty() )
 
  364      feedback->emit obstacleCostingAboutToBegin();
 
  366    std::unique_ptr< QgsScopedRuntimeProfile > costingProfile;
 
  369      costingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( 
"Assigning label costs" ), QStringLiteral( 
"rendering" ) );
 
  373    for ( 
const auto &feature : features )
 
  375      for ( 
auto &candidate : feature->candidates )
 
  379          rule->alterCandidateCost( candidate.get(), labelContext );
 
  386    step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
 
  388    for ( 
FeaturePart *obstaclePart : allObstacleParts )
 
  392        feedback->setProgress( step * index );
 
  397      allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart, 
this]( 
const LabelPosition * candidatePosition ) -> 
bool 
  405        if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
 
  406             || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
 
  407             || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
 
  418      feedback->emit obstacleCostingFinished();
 
  419    costingProfile.reset();
 
  426    step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
 
  428      feedback->emit calculatingConflictsAboutToBegin();
 
  430    std::unique_ptr< QgsScopedRuntimeProfile > conflictProfile;
 
  433      conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( 
"Calculating conflicts" ), QStringLiteral( 
"rendering" ) );
 
  436    int currentLabelPositionIndex = 0;
 
  438    for ( std::size_t featureIndex = 0; featureIndex < prob->mFeatureCount; featureIndex++ )
 
  441        feedback->setProgress( 
static_cast< double >( featureIndex ) * step );
 
  443      std::unique_ptr< Feats > feat = std::move( features.front() );
 
  444      features.pop_front();
 
  446      prob->mFirstCandidateIndexForFeature[featureIndex] = currentLabelPositionIndex;
 
  447      prob->mUnlabeledCostForFeature[featureIndex] = std::pow( 2, 10 - 10 * feat->priority );
 
  449      std::size_t maxCandidates = 0;
 
  450      switch ( feat->feature->getGeosType() )
 
  454          maxCandidates = feat->feature->maximumPointCandidates();
 
  457        case GEOS_LINESTRING:
 
  458          maxCandidates = feat->feature->maximumLineCandidates();
 
  462          maxCandidates = std::max( 
static_cast< std::size_t 
>( 16 ), feat->feature->maximumPolygonCandidates() );
 
  469      auto pruneHardConflicts = [&]
 
  471        switch ( mPlacementVersion )
 
  482            feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
 
  484              if ( candidate->hasHardObstacleConflict() )
 
  489            } ), feat->candidates.end() );
 
  491            if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
 
  493              switch ( feat->feature->feature()->overlapHandling() )
 
  499                  prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
 
  500                  feat->candidates.clear();
 
  517      switch ( feat->feature->feature()->overlapHandling() )
 
  520          pruneHardConflicts();
 
  528      if ( feat->candidates.empty() )
 
  541      switch ( feat->feature->feature()->overlapHandling() )
 
  547          pruneHardConflicts();
 
  552      if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
 
  554        feat->candidates.resize( maxCandidates );
 
  561      prob->mCandidateCountForFeature[featureIndex] = 
static_cast< int >( feat->candidates.size() );
 
  562      prob->mTotalCandidates += 
static_cast< int >( feat->candidates.size() );
 
  565      for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
 
  567        candidate->insertIntoIndex( prob->allCandidatesIndex(), 
this );
 
  568        candidate->setProblemIds( 
static_cast< int >( featureIndex ), currentLabelPositionIndex++ );
 
  570      features.emplace_back( std::move( feat ) );
 
  574      feedback->emit calculatingConflictsFinished();
 
  576    conflictProfile.reset();
 
  581      feedback->emit finalizingCandidatesAboutToBegin();
 
  583    std::unique_ptr< QgsScopedRuntimeProfile > finalizingProfile;
 
  586      finalizingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( 
"Finalizing labels" ), QStringLiteral( 
"rendering" ) );
 
  590    step = !features.empty() ? 100.0 / features.size() : 1;
 
  591    while ( !features.empty() ) 
 
  595        feedback->setProgress( step * index );
 
  600      std::unique_ptr< Feats > feat = std::move( features.front() );
 
  601      features.pop_front();
 
  603      for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
 
  605        std::unique_ptr< LabelPosition > lp = std::move( candidate );
 
  607        lp->resetNumOverlaps();
 
  615        const QgsRectangle searchBounds = lp->boundingBoxForCandidateConflicts( 
this );
 
  618          if ( candidatesAreConflicting( lp.get(), lp2 ) )
 
  620            lp->incrementNumOverlaps();
 
  627        nbOverlaps += lp->getNumOverlaps();
 
  629        prob->addCandidatePosition( std::move( lp ) );
 
  637      feedback->emit finalizingCandidatesFinished();
 
  639    finalizingProfile.reset();
 
  642    prob->mAllNblp = prob->mTotalCandidates;
 
  643    prob->mNbOverlap = nbOverlaps;