106 std::unique_ptr< QgsScopedRuntimeProfile > extractionProfile;
109 extractionProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Placing labels" ), QStringLiteral(
"rendering" ) );
120 std::vector< FeaturePart * > allObstacleParts;
121 std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
126 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
127 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
128 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
129 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
133 std::list< std::unique_ptr< Feats > > features;
139 int obstacleCount = 0;
143 std::size_t previousFeatureCount = 0;
144 int previousObstacleCount = 0;
146 QStringList layersWithFeaturesInBBox;
148 QMutexLocker palLocker( &mMutex );
150 double step = !mLayers.empty() ? 100.0 / mLayers.size() : 1;
152 std::unique_ptr< QgsScopedRuntimeProfile > candidateProfile;
155 candidateProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Generating label candidates" ), QStringLiteral(
"rendering" ) );
158 for (
const auto &it : mLayers )
164 Layer *layer = it.second.get();
176 feedback->emit candidateCreationAboutToBegin( it.first );
178 std::unique_ptr< QgsScopedRuntimeProfile > layerProfile;
181 layerProfile = std::make_unique< QgsScopedRuntimeProfile >( it.first->providerId(), QStringLiteral(
"rendering" ) );
196 QMutexLocker locker( &layer->
mMutex );
199 std::size_t featureIndex = 0;
201 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
204 feedback->
setProgress( index * step + featureIndex * featureStep );
211 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
215 allObstacleParts.emplace_back( selfObstacle );
217 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
224 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->createCandidates(
this );
230 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared,
this]( std::unique_ptr< LabelPosition > &candidate )
232 if ( showPartialLabels() )
233 return !candidate->intersects( mapBoundaryPrepared.get() );
235 return !candidate->within( mapBoundaryPrepared.get() );
236 } ), candidates.end() );
241 if ( !candidates.empty() )
243 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
245 candidate->insertIntoIndex( allCandidatesFirstRound );
246 candidate->setGlobalId( mNextCandidateId++ );
252 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
253 ft->feature = featurePart.get();
255 ft->candidates = std::move( candidates );
256 ft->priority = featurePart->calculatePriority();
257 features.emplace_back( std::move( ft ) );
262 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
263 if ( !unplacedPosition )
266 if ( featurePart->feature()->allowDegradedPlacement() )
269 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
270 unplacedPosition->setGlobalId( mNextCandidateId++ );
271 candidates.emplace_back( std::move( unplacedPosition ) );
274 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
275 ft->feature = featurePart.get();
277 ft->candidates = std::move( candidates );
278 ft->priority = featurePart->calculatePriority();
279 features.emplace_back( std::move( ft ) );
284 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
298 obstacles.
insert( obstaclePart, obstaclePart->boundingBox() );
299 allObstacleParts.emplace_back( obstaclePart );
308 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
310 layersWithFeaturesInBBox << layer->
name();
312 previousFeatureCount = features.size();
313 previousObstacleCount = obstacleCount;
316 feedback->emit candidateCreationFinished( it.first );
319 candidateProfile.reset();
326 prob->mLayerCount = layersWithFeaturesInBBox.size();
327 prob->labelledLayersName = layersWithFeaturesInBBox;
329 prob->mFeatureCount = features.size();
330 prob->mTotalCandidates = 0;
331 prob->mFeatNbLp.resize( prob->mFeatureCount );
332 prob->mFeatStartId.resize( prob->mFeatureCount );
333 prob->mInactiveCost.resize( prob->mFeatureCount );
335 if ( !features.empty() )
338 feedback->emit obstacleCostingAboutToBegin();
340 std::unique_ptr< QgsScopedRuntimeProfile > costingProfile;
343 costingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Assigning label costs" ), QStringLiteral(
"rendering" ) );
348 step = !allObstacleParts.empty() ? 100.0 / allObstacleParts.size() : 1;
350 for (
FeaturePart *obstaclePart : allObstacleParts )
359 allCandidatesFirstRound.
intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
368 || ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
369 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs(
dynamic_cast< FeaturePart *
>( obstaclePart->getHoleOf() ) ) ) )
381 feedback->emit obstacleCostingFinished();
382 costingProfile.reset();
389 step = prob->mFeatureCount != 0 ? 100.0 / prob->mFeatureCount : 1;
391 feedback->emit calculatingConflictsAboutToBegin();
393 std::unique_ptr< QgsScopedRuntimeProfile > conflictProfile;
396 conflictProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Calculating conflicts" ), QStringLiteral(
"rendering" ) );
400 for ( std::size_t i = 0; i < prob->mFeatureCount; i++ )
405 std::unique_ptr< Feats > feat = std::move( features.front() );
406 features.pop_front();
408 prob->mFeatStartId[i] = idlp;
409 prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
411 std::size_t maxCandidates = 0;
412 switch ( feat->feature->getGeosType() )
416 maxCandidates = feat->feature->maximumPointCandidates();
419 case GEOS_LINESTRING:
420 maxCandidates = feat->feature->maximumLineCandidates();
424 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
431 auto pruneHardConflicts = [&]
433 switch ( mPlacementVersion )
444 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
446 if ( candidate->hasHardObstacleConflict() )
451 } ), feat->candidates.end() );
453 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() )
455 switch ( feat->feature->feature()->overlapHandling() )
461 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
462 feat->candidates.clear();
479 switch ( feat->feature->feature()->overlapHandling() )
482 pruneHardConflicts();
490 if ( feat->candidates.empty() )
503 switch ( feat->feature->feature()->overlapHandling() )
509 pruneHardConflicts();
514 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
516 feat->candidates.resize( maxCandidates );
523 prob->mFeatNbLp[i] =
static_cast< int >( feat->candidates.size() );
524 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
527 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
529 candidate->insertIntoIndex( prob->allCandidatesIndex() );
530 candidate->setProblemIds(
static_cast< int >( i ), idlp++ );
532 features.emplace_back( std::move( feat ) );
536 feedback->emit calculatingConflictsFinished();
538 conflictProfile.reset();
546 feedback->emit finalizingCandidatesAboutToBegin();
548 std::unique_ptr< QgsScopedRuntimeProfile > finalizingProfile;
551 finalizingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr(
"Finalizing labels" ), QStringLiteral(
"rendering" ) );
555 step = !features.empty() ? 100.0 / features.size() : 1;
556 while ( !features.empty() )
565 std::unique_ptr< Feats > feat = std::move( features.front() );
566 features.pop_front();
568 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
570 std::unique_ptr< LabelPosition > lp = std::move( candidate );
572 lp->resetNumOverlaps();
580 lp->getBoundingBox( amin, amax );
581 prob->allCandidatesIndex().intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
585 lp->incrementNumOverlaps();
592 nbOverlaps += lp->getNumOverlaps();
594 prob->addCandidatePosition( std::move( lp ) );
602 feedback->emit finalizingCandidatesFinished();
604 finalizingProfile.reset();
607 prob->mAllNblp = prob->mTotalCandidates;
608 prob->mNbOverlap = nbOverlaps;