122 std::unique_ptr< QgsScopedRuntimeProfile > extractionProfile;
125 extractionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Placing labels" ), u
"rendering"_s );
136 std::vector< FeaturePart * > allObstacleParts;
137 auto prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
142 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
143 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
144 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
145 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
149 std::list< std::unique_ptr< Feats > > features;
155 int obstacleCount = 0;
159 std::size_t previousFeatureCount = 0;
160 int previousObstacleCount = 0;
162 QStringList layersWithFeaturesInBBox;
164 QMutexLocker palLocker( &mMutex );
166 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
168 std::unique_ptr< QgsScopedRuntimeProfile > candidateProfile;
171 candidateProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Generating label candidates" ), u
"rendering"_s );
174 for (
auto it = mLayers.rbegin(); it != mLayers.rend(); ++it )
180 Layer *layer = it->second.get();
192 feedback->emit candidateCreationAboutToBegin( it->first );
194 std::unique_ptr< QgsScopedRuntimeProfile > layerProfile;
197 layerProfile = std::make_unique< QgsScopedRuntimeProfile >( it->first->providerId(), u
"rendering"_s );
212 QMutexLocker locker( &layer->
mMutex );
215 std::size_t featureIndex = 0;
217 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
220 feedback->
setProgress( index * step + featureIndex * featureStep );
227 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
229 FeaturePart *selfObstacle = featurePart->getSelfObstacle( i );
231 allObstacleParts.emplace_back( selfObstacle );
233 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
240 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
252 [&mapBoundaryPrepared, &labelContext,
this]( std::unique_ptr< LabelPosition > &candidate ) {
253 if ( showPartialLabels() )
255 if ( !candidate->intersects( mapBoundaryPrepared.get() ) )
260 if ( !candidate->within( mapBoundaryPrepared.get() ) )
266 if ( rule->candidateIsIllegal( candidate.get(), labelContext ) )
280 if ( !candidates.empty() )
282 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
284 candidate->insertIntoIndex( allCandidatesFirstRound,
this );
285 candidate->setGlobalId( mNextCandidateId++ );
291 auto ft = std::make_unique< Feats >();
292 ft->feature = featurePart.get();
294 ft->candidates = std::move( candidates );
295 ft->priority = featurePart->calculatePriority();
296 features.emplace_back( std::move( ft ) );
301 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
302 if ( !unplacedPosition )
305 if ( featurePart->feature()->allowDegradedPlacement() )
308 unplacedPosition->insertIntoIndex( allCandidatesFirstRound,
this );
309 unplacedPosition->setGlobalId( mNextCandidateId++ );
310 candidates.emplace_back( std::move( unplacedPosition ) );
313 auto ft = std::make_unique< Feats >();
314 ft->feature = featurePart.get();
316 ft->candidates = std::move( candidates );
317 ft->priority = featurePart->calculatePriority();
318 features.emplace_back( std::move( ft ) );
323 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
333 for (
FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
339 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
340 allObstacleParts.emplace_back( obstaclePart );
350 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
352 layersWithFeaturesInBBox << layer->name();
354 previousFeatureCount = features.size();
355 previousObstacleCount = obstacleCount;
358 feedback->emit candidateCreationFinished( it->first );
361 candidateProfile.reset();
368 prob->mLayerCount = layersWithFeaturesInBBox.size();
369 prob->labelledLayersName = layersWithFeaturesInBBox;
371 prob->mFeatureCount = features.size();
372 prob->mTotalCandidates = 0;
373 prob->mCandidateCountForFeature.resize( prob->mFeatureCount );
374 prob->mFirstCandidateIndexForFeature.resize( prob->mFeatureCount );
375 prob->mUnlabeledCostForFeature.resize( prob->mFeatureCount );
377 if ( !features.empty() )
382 feedback->emit obstacleCostingAboutToBegin();
384 std::unique_ptr< QgsScopedRuntimeProfile > costingProfile;
387 costingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Assigning label costs" ), u
"rendering"_s );
391 for (
const auto &feature : features )
393 for (
auto &candidate : feature->candidates )
397 rule->alterCandidateCost( candidate.get(), labelContext );
404 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
406 for (
FeaturePart *obstaclePart : allObstacleParts )
410 feedback->setProgress( step * index );
415 allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition *candidatePosition ) ->
bool {
422 if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
423 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
424 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
435 feedback->emit obstacleCostingFinished();
443 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
445 feedback->emit calculatingConflictsAboutToBegin();
447 std::unique_ptr< QgsScopedRuntimeProfile > conflictProfile;
450 conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Calculating conflicts" ), u
"rendering"_s );
453 int currentLabelPositionIndex = 0;
455 for ( std::size_t featureIndex = 0; featureIndex < prob->mFeatureCount; featureIndex++ )
458 feedback->setProgress(
static_cast< double >( featureIndex ) * step );
460 std::unique_ptr< Feats > feat = std::move( features.front() );
461 features.pop_front();
463 prob->mFirstCandidateIndexForFeature[featureIndex] = currentLabelPositionIndex;
464 prob->mUnlabeledCostForFeature[featureIndex] = std::pow( 2, 10 - 10 * feat->priority );
466 std::size_t maxCandidates = 0;
467 switch ( feat->feature->getGeosType() )
471 maxCandidates = feat->feature->maximumPointCandidates();
474 case GEOS_LINESTRING:
475 maxCandidates = feat->feature->maximumLineCandidates();
479 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
486 auto pruneHardConflicts = [&] {
487 switch ( mPlacementVersion )
498 feat->candidates.erase(
500 feat->candidates.begin() + 1,
501 feat->candidates.end(),
502 [&]( std::unique_ptr< LabelPosition > &candidate ) {
503 if ( candidate->hasHardObstacleConflict() )
510 feat->candidates.end()
513 if ( feat->candidates.size() == 1 && feat->candidates[0]->hasHardObstacleConflict() )
515 switch ( feat->feature->feature()->overlapHandling() )
521 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
522 feat->candidates.clear();
539 switch ( feat->feature->feature()->overlapHandling() )
544 pruneHardConflicts();
553 if ( feat->candidates.empty() )
566 switch ( feat->feature->feature()->overlapHandling() )
574 pruneHardConflicts();
580 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
582 feat->candidates.resize( maxCandidates );
589 prob->mCandidateCountForFeature[featureIndex] =
static_cast< int >( feat->candidates.size() );
590 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
593 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
595 candidate->insertIntoIndex( prob->allCandidatesIndex(),
this );
596 candidate->setProblemIds(
static_cast< int >( featureIndex ), currentLabelPositionIndex++ );
598 features.emplace_back( std::move( feat ) );
602 feedback->emit calculatingConflictsFinished();
604 conflictProfile.reset();
609 feedback->emit finalizingCandidatesAboutToBegin();
611 std::unique_ptr< QgsScopedRuntimeProfile > finalizingProfile;
614 finalizingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Finalizing labels" ), u
"rendering"_s );
618 step = !features.empty() ? 100.0 / features.size() : 1;
619 while ( !features.empty() )
623 feedback->setProgress( step * index );
628 std::unique_ptr< Feats > feat = std::move( features.front() );
629 features.pop_front();
631 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
633 std::unique_ptr< LabelPosition > lp = std::move( candidate );
635 lp->resetNumOverlaps();
645 const QgsRectangle searchBounds = lp->boundingBoxForCandidateConflicts(
this );
646 prob->allCandidatesIndex().intersects( searchBounds, [&lp,
this](
const LabelPosition *lp2 ) ->
bool {
647 if ( candidatesAreConflicting( lp.get(), lp2 ) )
649 lp->incrementNumOverlaps();
655 nbOverlaps += lp->getNumOverlaps();
658 prob->addCandidatePosition( std::move( lp ) );
666 feedback->emit finalizingCandidatesFinished();
668 finalizingProfile.reset();
671 prob->mAllNblp = prob->mTotalCandidates;
672 prob->mNbOverlap = nbOverlaps;