109 std::unique_ptr< QgsScopedRuntimeProfile > extractionProfile;
112 extractionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Placing labels" ), QStringLiteral(
"rendering" ) );
123 std::vector< FeaturePart * > allObstacleParts;
124 std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
129 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
130 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
131 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
132 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
136 std::list< std::unique_ptr< Feats > > features;
140 geos::prepared_unique_ptr mapBoundaryPrepared( GEOSPrepare_r(
QgsGeosContext::get(), mapBoundaryGeos.get() ) );
142 int obstacleCount = 0;
146 std::size_t previousFeatureCount = 0;
147 int previousObstacleCount = 0;
149 QStringList layersWithFeaturesInBBox;
151 QMutexLocker palLocker( &mMutex );
153 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
155 std::unique_ptr< QgsScopedRuntimeProfile > candidateProfile;
158 candidateProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Generating label candidates" ), QStringLiteral(
"rendering" ) );
161 for (
const auto &it : mLayers )
167 Layer *layer = it.second.get();
179 feedback->emit candidateCreationAboutToBegin( it.first );
181 std::unique_ptr< QgsScopedRuntimeProfile > layerProfile;
184 layerProfile = std::make_unique< QgsScopedRuntimeProfile >( it.first->providerId(), QStringLiteral(
"rendering" ) );
199 QMutexLocker locker( &layer->
mMutex );
202 std::size_t featureIndex = 0;
204 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
207 feedback->
setProgress( index * step + featureIndex * featureStep );
214 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
218 allObstacleParts.emplace_back( selfObstacle );
220 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
227 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
235 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared, &labelContext,
this]( std::unique_ptr< LabelPosition > &candidate )
237 if ( showPartialLabels() )
239 if ( !candidate->intersects( mapBoundaryPrepared.get() ) )
244 if ( !candidate->within( mapBoundaryPrepared.get() ) )
250 if ( rule->candidateIsIllegal( candidate.get(), labelContext ) )
256 } ), candidates.end() );
261 if ( !candidates.empty() )
263 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
265 candidate->insertIntoIndex( allCandidatesFirstRound );
266 candidate->setGlobalId( mNextCandidateId++ );
272 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
273 ft->feature = featurePart.get();
275 ft->candidates = std::move( candidates );
276 ft->priority = featurePart->calculatePriority();
277 features.emplace_back( std::move( ft ) );
282 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
283 if ( !unplacedPosition )
286 if ( featurePart->feature()->allowDegradedPlacement() )
289 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
290 unplacedPosition->setGlobalId( mNextCandidateId++ );
291 candidates.emplace_back( std::move( unplacedPosition ) );
294 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
295 ft->feature = featurePart.get();
297 ft->candidates = std::move( candidates );
298 ft->priority = featurePart->calculatePriority();
299 features.emplace_back( std::move( ft ) );
304 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
312 for (
FeaturePart *obstaclePart : std::as_const( layer->mObstacleParts ) )
318 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
319 allObstacleParts.emplace_back( obstaclePart );
328 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
330 layersWithFeaturesInBBox << layer->name();
332 previousFeatureCount = features.size();
333 previousObstacleCount = obstacleCount;
336 feedback->emit candidateCreationFinished( it.first );
339 candidateProfile.reset();
346 prob->mLayerCount = layersWithFeaturesInBBox.size();
347 prob->labelledLayersName = layersWithFeaturesInBBox;
349 prob->mFeatureCount = features.size();
350 prob->mTotalCandidates = 0;
351 prob->mCandidateCountForFeature.resize( prob->mFeatureCount );
352 prob->mFirstCandidateIndexForFeature.resize( prob->mFeatureCount );
353 prob->mUnlabeledCostForFeature.resize( prob->mFeatureCount );
355 if ( !features.empty() )
358 feedback->emit obstacleCostingAboutToBegin();
360 std::unique_ptr< QgsScopedRuntimeProfile > costingProfile;
363 costingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Assigning label costs" ), QStringLiteral(
"rendering" ) );
367 for (
const auto &feature : features )
369 for (
auto &candidate : feature->candidates )
373 rule->alterCandidateCost( candidate.get(), labelContext );
380 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
382 for (
FeaturePart *obstaclePart : allObstacleParts )
386 feedback->setProgress( step * index );
391 allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
399 if ( candidatePosition->getFeaturePart()->feature()->overlapHandling() == Qgis::LabelOverlapHandling::AllowOverlapAtNoCost
400 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
401 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
412 feedback->emit obstacleCostingFinished();
413 costingProfile.reset();
420 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
422 feedback->emit calculatingConflictsAboutToBegin();
424 std::unique_ptr< QgsScopedRuntimeProfile > conflictProfile;
427 conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Calculating conflicts" ), QStringLiteral(
"rendering" ) );
430 int currentLabelPositionIndex = 0;
432 for ( std::size_t featureIndex = 0; featureIndex < prob->mFeatureCount; featureIndex++ )
435 feedback->setProgress(
static_cast< double >( featureIndex ) * step );
437 std::unique_ptr< Feats > feat = std::move( features.front() );
438 features.pop_front();
440 prob->mFirstCandidateIndexForFeature[featureIndex] = currentLabelPositionIndex;
441 prob->mUnlabeledCostForFeature[featureIndex] = std::pow( 2, 10 - 10 * feat->priority );
443 std::size_t maxCandidates = 0;
444 switch ( feat->feature->getGeosType() )
448 maxCandidates = feat->feature->maximumPointCandidates();
451 case GEOS_LINESTRING:
452 maxCandidates = feat->feature->maximumLineCandidates();
456 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
463 auto pruneHardConflicts = [&]
465 switch ( mPlacementVersion )
476 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
478 if ( candidate->hasHardObstacleConflict() )
483 } ), feat->candidates.end() );
485 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
487 switch ( feat->feature->feature()->overlapHandling() )
493 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
494 feat->candidates.clear();
511 switch ( feat->feature->feature()->overlapHandling() )
514 pruneHardConflicts();
522 if ( feat->candidates.empty() )
535 switch ( feat->feature->feature()->overlapHandling() )
541 pruneHardConflicts();
546 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
548 feat->candidates.resize( maxCandidates );
555 prob->mCandidateCountForFeature[featureIndex] =
static_cast< int >( feat->candidates.size() );
556 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
559 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
561 candidate->insertIntoIndex( prob->allCandidatesIndex() );
562 candidate->setProblemIds(
static_cast< int >( featureIndex ), currentLabelPositionIndex++ );
564 features.emplace_back( std::move( feat ) );
568 feedback->emit calculatingConflictsFinished();
570 conflictProfile.reset();
575 feedback->emit finalizingCandidatesAboutToBegin();
577 std::unique_ptr< QgsScopedRuntimeProfile > finalizingProfile;
580 finalizingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Finalizing labels" ), QStringLiteral(
"rendering" ) );
584 step = !features.empty() ? 100.0 / features.size() : 1;
585 while ( !features.empty() )
589 feedback->setProgress( step * index );
594 std::unique_ptr< Feats > feat = std::move( features.front() );
595 features.pop_front();
597 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
599 std::unique_ptr< LabelPosition > lp = std::move( candidate );
601 lp->resetNumOverlaps();
609 const QgsRectangle searchBounds = lp->boundingBoxForCandidateConflicts(
this );
612 if ( candidatesAreConflicting( lp.get(), lp2 ) )
614 lp->incrementNumOverlaps();
621 nbOverlaps += lp->getNumOverlaps();
623 prob->addCandidatePosition( std::move( lp ) );
631 feedback->emit finalizingCandidatesFinished();
633 finalizingProfile.reset();
636 prob->mAllNblp = prob->mTotalCandidates;
637 prob->mNbOverlap = nbOverlaps;