114 std::unique_ptr< QgsScopedRuntimeProfile > extractionProfile;
117 extractionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Placing labels" ), QStringLiteral(
"rendering" ) );
128 std::vector< FeaturePart * > allObstacleParts;
129 auto prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
134 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
135 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
136 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
137 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
141 std::list< std::unique_ptr< Feats > > features;
145 geos::prepared_unique_ptr mapBoundaryPrepared( GEOSPrepare_r(
QgsGeosContext::get(), mapBoundaryGeos.get() ) );
147 int obstacleCount = 0;
151 std::size_t previousFeatureCount = 0;
152 int previousObstacleCount = 0;
154 QStringList layersWithFeaturesInBBox;
156 QMutexLocker palLocker( &mMutex );
158 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
160 std::unique_ptr< QgsScopedRuntimeProfile > candidateProfile;
163 candidateProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Generating label candidates" ), QStringLiteral(
"rendering" ) );
166 for (
auto it = mLayers.rbegin(); it != mLayers.rend(); ++it )
172 Layer *layer = it->second.get();
184 feedback->emit candidateCreationAboutToBegin( it->first );
186 std::unique_ptr< QgsScopedRuntimeProfile > layerProfile;
189 layerProfile = std::make_unique< QgsScopedRuntimeProfile >( it->first->providerId(), QStringLiteral(
"rendering" ) );
204 QMutexLocker locker( &layer->
mMutex );
207 std::size_t featureIndex = 0;
209 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
212 feedback->
setProgress( index * step + featureIndex * featureStep );
219 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
223 allObstacleParts.emplace_back( selfObstacle );
225 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
232 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
240 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared, &labelContext,
this]( std::unique_ptr< LabelPosition > &candidate )
242 if ( showPartialLabels() )
244 if ( !candidate->intersects( mapBoundaryPrepared.get() ) )
249 if ( !candidate->within( mapBoundaryPrepared.get() ) )
255 if ( rule->candidateIsIllegal( candidate.get(), labelContext ) )
261 } ), candidates.end() );
266 if ( !candidates.empty() )
268 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
270 candidate->insertIntoIndex( allCandidatesFirstRound );
271 candidate->setGlobalId( mNextCandidateId++ );
277 auto ft = std::make_unique< Feats >();
278 ft->feature = featurePart.get();
280 ft->candidates = std::move( candidates );
281 ft->priority = featurePart->calculatePriority();
282 features.emplace_back( std::move( ft ) );
287 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
288 if ( !unplacedPosition )
291 if ( featurePart->feature()->allowDegradedPlacement() )
294 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
295 unplacedPosition->setGlobalId( mNextCandidateId++ );
296 candidates.emplace_back( std::move( unplacedPosition ) );
299 auto ft = std::make_unique< Feats >();
300 ft->feature = featurePart.get();
302 ft->candidates = std::move( candidates );
303 ft->priority = featurePart->calculatePriority();
304 features.emplace_back( std::move( ft ) );
309 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
317 for (
FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
323 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
324 allObstacleParts.emplace_back( obstaclePart );
333 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
335 layersWithFeaturesInBBox << layer->name();
337 previousFeatureCount = features.size();
338 previousObstacleCount = obstacleCount;
341 feedback->emit candidateCreationFinished( it->first );
344 candidateProfile.reset();
351 prob->mLayerCount = layersWithFeaturesInBBox.size();
352 prob->labelledLayersName = layersWithFeaturesInBBox;
354 prob->mFeatureCount = features.size();
355 prob->mTotalCandidates = 0;
356 prob->mCandidateCountForFeature.resize( prob->mFeatureCount );
357 prob->mFirstCandidateIndexForFeature.resize( prob->mFeatureCount );
358 prob->mUnlabeledCostForFeature.resize( prob->mFeatureCount );
360 if ( !features.empty() )
363 feedback->emit obstacleCostingAboutToBegin();
365 std::unique_ptr< QgsScopedRuntimeProfile > costingProfile;
368 costingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Assigning label costs" ), QStringLiteral(
"rendering" ) );
372 for (
const auto &feature : features )
374 for (
auto &candidate : feature->candidates )
378 rule->alterCandidateCost( candidate.get(), labelContext );
385 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
387 for (
FeaturePart *obstaclePart : allObstacleParts )
391 feedback->setProgress( step * index );
396 allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
404 if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
405 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
406 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
417 feedback->emit obstacleCostingFinished();
418 costingProfile.reset();
425 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
427 feedback->emit calculatingConflictsAboutToBegin();
429 std::unique_ptr< QgsScopedRuntimeProfile > conflictProfile;
432 conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Calculating conflicts" ), QStringLiteral(
"rendering" ) );
435 int currentLabelPositionIndex = 0;
437 for ( std::size_t featureIndex = 0; featureIndex < prob->mFeatureCount; featureIndex++ )
440 feedback->setProgress(
static_cast< double >( featureIndex ) * step );
442 std::unique_ptr< Feats > feat = std::move( features.front() );
443 features.pop_front();
445 prob->mFirstCandidateIndexForFeature[featureIndex] = currentLabelPositionIndex;
446 prob->mUnlabeledCostForFeature[featureIndex] = std::pow( 2, 10 - 10 * feat->priority );
448 std::size_t maxCandidates = 0;
449 switch ( feat->feature->getGeosType() )
453 maxCandidates = feat->feature->maximumPointCandidates();
456 case GEOS_LINESTRING:
457 maxCandidates = feat->feature->maximumLineCandidates();
461 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
468 auto pruneHardConflicts = [&]
470 switch ( mPlacementVersion )
481 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
483 if ( candidate->hasHardObstacleConflict() )
488 } ), feat->candidates.end() );
490 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
492 switch ( feat->feature->feature()->overlapHandling() )
498 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
499 feat->candidates.clear();
516 switch ( feat->feature->feature()->overlapHandling() )
519 pruneHardConflicts();
527 if ( feat->candidates.empty() )
540 switch ( feat->feature->feature()->overlapHandling() )
546 pruneHardConflicts();
551 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
553 feat->candidates.resize( maxCandidates );
560 prob->mCandidateCountForFeature[featureIndex] =
static_cast< int >( feat->candidates.size() );
561 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
564 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
566 candidate->insertIntoIndex( prob->allCandidatesIndex() );
567 candidate->setProblemIds(
static_cast< int >( featureIndex ), currentLabelPositionIndex++ );
569 features.emplace_back( std::move( feat ) );
573 feedback->emit calculatingConflictsFinished();
575 conflictProfile.reset();
580 feedback->emit finalizingCandidatesAboutToBegin();
582 std::unique_ptr< QgsScopedRuntimeProfile > finalizingProfile;
585 finalizingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Finalizing labels" ), QStringLiteral(
"rendering" ) );
589 step = !features.empty() ? 100.0 / features.size() : 1;
590 while ( !features.empty() )
594 feedback->setProgress( step * index );
599 std::unique_ptr< Feats > feat = std::move( features.front() );
600 features.pop_front();
602 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
604 std::unique_ptr< LabelPosition > lp = std::move( candidate );
606 lp->resetNumOverlaps();
614 const QgsRectangle searchBounds = lp->boundingBoxForCandidateConflicts(
this );
617 if ( candidatesAreConflicting( lp.get(), lp2 ) )
619 lp->incrementNumOverlaps();
626 nbOverlaps += lp->getNumOverlaps();
628 prob->addCandidatePosition( std::move( lp ) );
636 feedback->emit finalizingCandidatesFinished();
638 finalizingProfile.reset();
641 prob->mAllNblp = prob->mTotalCandidates;
642 prob->mNbOverlap = nbOverlaps;