117 std::unique_ptr< QgsScopedRuntimeProfile > extractionProfile;
120 extractionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Placing labels" ), QStringLiteral(
"rendering" ) );
131 std::vector< FeaturePart * > allObstacleParts;
132 auto prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
137 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
138 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
139 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
140 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
144 std::list< std::unique_ptr< Feats > > features;
150 int obstacleCount = 0;
154 std::size_t previousFeatureCount = 0;
155 int previousObstacleCount = 0;
157 QStringList layersWithFeaturesInBBox;
159 QMutexLocker palLocker( &mMutex );
161 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
163 std::unique_ptr< QgsScopedRuntimeProfile > candidateProfile;
166 candidateProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Generating label candidates" ), QStringLiteral(
"rendering" ) );
169 for (
auto it = mLayers.rbegin(); it != mLayers.rend(); ++it )
175 Layer *layer = it->second.get();
187 feedback->emit candidateCreationAboutToBegin( it->first );
189 std::unique_ptr< QgsScopedRuntimeProfile > layerProfile;
192 layerProfile = std::make_unique< QgsScopedRuntimeProfile >( it->first->providerId(), QStringLiteral(
"rendering" ) );
207 QMutexLocker locker( &layer->
mMutex );
210 std::size_t featureIndex = 0;
212 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
215 feedback->
setProgress( index * step + featureIndex * featureStep );
222 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
224 FeaturePart *selfObstacle = featurePart->getSelfObstacle( i );
226 allObstacleParts.emplace_back( selfObstacle );
228 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
235 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
243 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared, &labelContext,
this]( std::unique_ptr< LabelPosition > &candidate )
245 if ( showPartialLabels() )
247 if ( !candidate->intersects( mapBoundaryPrepared.get() ) )
252 if ( !candidate->within( mapBoundaryPrepared.get() ) )
258 if ( rule->candidateIsIllegal( candidate.get(), labelContext ) )
264 } ), candidates.end() );
269 if ( !candidates.empty() )
271 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
273 candidate->insertIntoIndex( allCandidatesFirstRound,
this );
274 candidate->setGlobalId( mNextCandidateId++ );
280 auto ft = std::make_unique< Feats >();
281 ft->feature = featurePart.get();
283 ft->candidates = std::move( candidates );
284 ft->priority = featurePart->calculatePriority();
285 features.emplace_back( std::move( ft ) );
290 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
291 if ( !unplacedPosition )
294 if ( featurePart->feature()->allowDegradedPlacement() )
297 unplacedPosition->insertIntoIndex( allCandidatesFirstRound,
this );
298 unplacedPosition->setGlobalId( mNextCandidateId++ );
299 candidates.emplace_back( std::move( unplacedPosition ) );
302 auto ft = std::make_unique< Feats >();
303 ft->feature = featurePart.get();
305 ft->candidates = std::move( candidates );
306 ft->priority = featurePart->calculatePriority();
307 features.emplace_back( std::move( ft ) );
312 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
320 for (
FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
326 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
327 allObstacleParts.emplace_back( obstaclePart );
336 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
338 layersWithFeaturesInBBox << layer->name();
340 previousFeatureCount = features.size();
341 previousObstacleCount = obstacleCount;
344 feedback->emit candidateCreationFinished( it->first );
347 candidateProfile.reset();
354 prob->mLayerCount = layersWithFeaturesInBBox.size();
355 prob->labelledLayersName = layersWithFeaturesInBBox;
357 prob->mFeatureCount = features.size();
358 prob->mTotalCandidates = 0;
359 prob->mCandidateCountForFeature.resize( prob->mFeatureCount );
360 prob->mFirstCandidateIndexForFeature.resize( prob->mFeatureCount );
361 prob->mUnlabeledCostForFeature.resize( prob->mFeatureCount );
363 if ( !features.empty() )
366 feedback->emit obstacleCostingAboutToBegin();
368 std::unique_ptr< QgsScopedRuntimeProfile > costingProfile;
371 costingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Assigning label costs" ), QStringLiteral(
"rendering" ) );
375 for (
const auto &feature : features )
377 for (
auto &candidate : feature->candidates )
381 rule->alterCandidateCost( candidate.get(), labelContext );
388 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
390 for (
FeaturePart *obstaclePart : allObstacleParts )
394 feedback->setProgress( step * index );
399 allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
407 if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
408 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
409 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
420 feedback->emit obstacleCostingFinished();
421 costingProfile.reset();
428 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
430 feedback->emit calculatingConflictsAboutToBegin();
432 std::unique_ptr< QgsScopedRuntimeProfile > conflictProfile;
435 conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Calculating conflicts" ), QStringLiteral(
"rendering" ) );
438 int currentLabelPositionIndex = 0;
440 for ( std::size_t featureIndex = 0; featureIndex < prob->mFeatureCount; featureIndex++ )
443 feedback->setProgress(
static_cast< double >( featureIndex ) * step );
445 std::unique_ptr< Feats > feat = std::move( features.front() );
446 features.pop_front();
448 prob->mFirstCandidateIndexForFeature[featureIndex] = currentLabelPositionIndex;
449 prob->mUnlabeledCostForFeature[featureIndex] = std::pow( 2, 10 - 10 * feat->priority );
451 std::size_t maxCandidates = 0;
452 switch ( feat->feature->getGeosType() )
456 maxCandidates = feat->feature->maximumPointCandidates();
459 case GEOS_LINESTRING:
460 maxCandidates = feat->feature->maximumLineCandidates();
464 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
471 auto pruneHardConflicts = [&]
473 switch ( mPlacementVersion )
484 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
486 if ( candidate->hasHardObstacleConflict() )
491 } ), feat->candidates.end() );
493 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
495 switch ( feat->feature->feature()->overlapHandling() )
501 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
502 feat->candidates.clear();
519 switch ( feat->feature->feature()->overlapHandling() )
522 pruneHardConflicts();
530 if ( feat->candidates.empty() )
543 switch ( feat->feature->feature()->overlapHandling() )
549 pruneHardConflicts();
554 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
556 feat->candidates.resize( maxCandidates );
563 prob->mCandidateCountForFeature[featureIndex] =
static_cast< int >( feat->candidates.size() );
564 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
567 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
569 candidate->insertIntoIndex( prob->allCandidatesIndex(),
this );
570 candidate->setProblemIds(
static_cast< int >( featureIndex ), currentLabelPositionIndex++ );
572 features.emplace_back( std::move( feat ) );
576 feedback->emit calculatingConflictsFinished();
578 conflictProfile.reset();
583 feedback->emit finalizingCandidatesAboutToBegin();
585 std::unique_ptr< QgsScopedRuntimeProfile > finalizingProfile;
588 finalizingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Finalizing labels" ), QStringLiteral(
"rendering" ) );
592 step = !features.empty() ? 100.0 / features.size() : 1;
593 while ( !features.empty() )
597 feedback->setProgress( step * index );
602 std::unique_ptr< Feats > feat = std::move( features.front() );
603 features.pop_front();
605 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
607 std::unique_ptr< LabelPosition > lp = std::move( candidate );
609 lp->resetNumOverlaps();
617 const QgsRectangle searchBounds = lp->boundingBoxForCandidateConflicts(
this );
618 prob->allCandidatesIndex().intersects( searchBounds, [&lp,
this](
const LabelPosition * lp2 )->
bool
620 if ( candidatesAreConflicting( lp.get(), lp2 ) )
622 lp->incrementNumOverlaps();
629 nbOverlaps += lp->getNumOverlaps();
631 prob->addCandidatePosition( std::move( lp ) );
639 feedback->emit finalizingCandidatesFinished();
641 finalizingProfile.reset();
644 prob->mAllNblp = prob->mTotalCandidates;
645 prob->mNbOverlap = nbOverlaps;