44 #include "qgssettings.h"
53 mGlobalCandidatesLimitPoint = settings.value( QStringLiteral(
"rendering/label_candidates_limit_points" ), 0, QgsSettings::Core ).toInt();
54 mGlobalCandidatesLimitLine = settings.value( QStringLiteral(
"rendering/label_candidates_limit_lines" ), 0, QgsSettings::Core ).toInt();
55 mGlobalCandidatesLimitPolygon = settings.value( QStringLiteral(
"rendering/label_candidates_limit_polygons" ), 0, QgsSettings::Core ).toInt();
67 for (
auto it = mLayers.begin(); it != mLayers.end(); ++it )
69 if ( it->second.get() == layer )
82 Q_ASSERT( mLayers.find( provider ) == mLayers.end() );
84 std::unique_ptr< Layer > layer = std::make_unique< Layer >( provider, layerName, arrangement, defaultPriority, active, toLabel,
this, displayAll );
85 Layer *res = layer.get();
102 std::vector< FeaturePart * > allObstacleParts;
103 std::unique_ptr< Problem > prob = std::make_unique< Problem >( maxCoordinateExtentForSpatialIndices );
108 bbx[0] = bbx[3] = prob->mMapExtentBounds[0] = extent.
xMinimum();
109 bby[0] = bby[1] = prob->mMapExtentBounds[1] = extent.
yMinimum();
110 bbx[1] = bbx[2] = prob->mMapExtentBounds[2] = extent.
xMaximum();
111 bby[2] = bby[3] = prob->mMapExtentBounds[3] = extent.
yMaximum();
115 std::list< std::unique_ptr< Feats > > features;
121 int obstacleCount = 0;
125 std::size_t previousFeatureCount = 0;
126 int previousObstacleCount = 0;
128 QStringList layersWithFeaturesInBBox;
130 QMutexLocker palLocker( &mMutex );
131 for (
const auto &it : mLayers )
133 Layer *layer = it.second.get();
156 QMutexLocker locker( &layer->
mMutex );
159 for (
const std::unique_ptr< FeaturePart > &featurePart : std::as_const( layer->
mFeatureParts ) )
165 for (
int i = 0; i < featurePart->getNumSelfObstacles(); i++ )
168 obstacles.insert( selfObstacle, selfObstacle->
boundingBox() );
169 allObstacleParts.emplace_back( selfObstacle );
171 if ( !featurePart->getSelfObstacle( i )->getHoleOf() )
178 std::vector< std::unique_ptr< LabelPosition > > candidates = featurePart->
createCandidates(
this );
184 candidates.erase( std::remove_if( candidates.begin(), candidates.end(), [&mapBoundaryPrepared,
this]( std::unique_ptr< LabelPosition > &candidate )
186 if ( showPartialLabels() )
187 return !candidate->intersects( mapBoundaryPrepared.get() );
189 return !candidate->within( mapBoundaryPrepared.get() );
190 } ), candidates.end() );
195 if ( !candidates.empty() )
197 for ( std::unique_ptr< LabelPosition > &candidate : candidates )
199 candidate->insertIntoIndex( allCandidatesFirstRound );
200 candidate->setGlobalId( mNextCandidateId++ );
206 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
207 ft->feature = featurePart.get();
209 ft->candidates = std::move( candidates );
210 ft->priority = featurePart->calculatePriority();
211 features.emplace_back( std::move( ft ) );
216 std::unique_ptr< LabelPosition > unplacedPosition = featurePart->createCandidatePointOnSurface( featurePart.get() );
217 if ( !unplacedPosition )
223 unplacedPosition->insertIntoIndex( allCandidatesFirstRound );
224 unplacedPosition->setGlobalId( mNextCandidateId++ );
225 candidates.emplace_back( std::move( unplacedPosition ) );
228 std::unique_ptr< Feats > ft = std::make_unique< Feats >();
229 ft->feature = featurePart.get();
231 ft->candidates = std::move( candidates );
232 ft->priority = featurePart->calculatePriority();
233 features.emplace_back( std::move( ft ) );
238 prob->positionsWithNoCandidates()->emplace_back( std::move( unplacedPosition ) );
252 obstacles.insert( obstaclePart, obstaclePart->boundingBox() );
253 allObstacleParts.emplace_back( obstaclePart );
262 if ( features.size() - previousFeatureCount > 0 || obstacleCount > previousObstacleCount )
264 layersWithFeaturesInBBox << layer->
name();
266 previousFeatureCount = features.size();
267 previousObstacleCount = obstacleCount;
274 prob->mLayerCount = layersWithFeaturesInBBox.size();
275 prob->labelledLayersName = layersWithFeaturesInBBox;
277 prob->mFeatureCount = features.size();
278 prob->mTotalCandidates = 0;
279 prob->mFeatNbLp.resize( prob->mFeatureCount );
280 prob->mFeatStartId.resize( prob->mFeatureCount );
281 prob->mInactiveCost.resize( prob->mFeatureCount );
283 if ( !features.empty() )
286 for (
FeaturePart *obstaclePart : allObstacleParts )
291 allCandidatesFirstRound.intersects( obstaclePart->boundingBox(), [obstaclePart,
this](
const LabelPosition * candidatePosition ) ->
bool
298 if ( ( !obstaclePart->getHoleOf() && candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( obstaclePart ) )
299 || ( obstaclePart->getHoleOf() && !candidatePosition->getFeaturePart()->hasSameLabelFeatureAs( dynamic_cast< FeaturePart * >( obstaclePart->getHoleOf() ) ) ) )
316 for ( std::size_t i = 0; i < prob->mFeatureCount; i++ )
318 std::unique_ptr< Feats > feat = std::move( features.front() );
319 features.pop_front();
321 prob->mFeatStartId[i] = idlp;
322 prob->mInactiveCost[i] = std::pow( 2, 10 - 10 * feat->priority );
324 std::size_t maxCandidates = 0;
325 switch ( feat->feature->getGeosType() )
329 maxCandidates = feat->feature->maximumPointCandidates();
332 case GEOS_LINESTRING:
333 maxCandidates = feat->feature->maximumLineCandidates();
337 maxCandidates = std::max(
static_cast< std::size_t
>( 16 ), feat->feature->maximumPolygonCandidates() );
344 auto pruneHardConflicts = [&]
346 switch ( mPlacementVersion )
357 feat->candidates.erase( std::remove_if( feat->candidates.begin() + 1, feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
359 if ( candidate->hasHardObstacleConflict() )
364 } ), feat->candidates.end() );
366 if ( feat->candidates.size() == 1 && feat->candidates[ 0 ]->hasHardObstacleConflict() && !feat->feature->layer()->displayAll() )
370 prob->positionsWithNoCandidates()->emplace_back( std::move( feat->candidates.front() ) );
371 feat->candidates.clear();
379 if ( !feat->feature->layer()->displayAll() )
381 pruneHardConflicts();
384 if ( feat->candidates.empty() )
397 if ( feat->feature->layer()->displayAll() )
399 pruneHardConflicts();
404 if ( maxCandidates > 0 && feat->candidates.size() > maxCandidates )
406 feat->candidates.resize( maxCandidates );
413 prob->mFeatNbLp[i] =
static_cast< int >( feat->candidates.size() );
414 prob->mTotalCandidates +=
static_cast< int >( feat->candidates.size() );
417 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
419 candidate->insertIntoIndex( prob->allCandidatesIndex() );
420 candidate->setProblemIds(
static_cast< int >( i ), idlp++ );
422 features.emplace_back( std::move( feat ) );
429 while ( !features.empty() )
434 std::unique_ptr< Feats > feat = std::move( features.front() );
435 features.pop_front();
437 for ( std::unique_ptr< LabelPosition > &candidate : feat->candidates )
439 std::unique_ptr< LabelPosition > lp = std::move( candidate );
441 lp->resetNumOverlaps();
449 lp->getBoundingBox( amin, amax );
450 prob->allCandidatesIndex().intersects(
QgsRectangle( amin[0], amin[1], amax[0], amax[1] ), [&lp,
this](
const LabelPosition * lp2 )->
bool
452 if ( candidatesAreConflicting( lp.get(), lp2 ) )
454 lp->incrementNumOverlaps();
461 nbOverlaps += lp->getNumOverlaps();
463 prob->addCandidatePosition( std::move( lp ) );
470 prob->mAllNblp = prob->mTotalCandidates;
471 prob->mNbOverlap = nbOverlaps;
477 void Pal::registerCancellationCallback( Pal::FnIsCanceled fnCanceled,
void *context )
479 fnIsCanceled = fnCanceled;
480 fnIsCanceledContext = context;
485 return extract( extent, mapBoundary );
491 return QList<LabelPosition *>();
501 return QList<LabelPosition *>();
507 void Pal::setMinIt(
int min_it )
513 void Pal::setMaxIt(
int max_it )
519 void Pal::setPopmusicR(
int r )
525 void Pal::setEjChainDeg(
int degree )
527 this->mEjChainDeg = degree;
530 void Pal::setTenure(
int tenure )
532 this->mTenure = tenure;
535 void Pal::setCandListSize(
double fact )
537 this->mCandListSize = fact;
542 this->mShowPartialLabels = show;
547 return mPlacementVersion;
562 auto it = mCandidateConflicts.constFind( key );
563 if ( it != mCandidateConflicts.constEnd() )
567 mCandidateConflicts.insert( key, res );
583 return mShowPartialLabels;
A rtree spatial index for use in the pal labeling engine.
The QgsAbstractLabelProvider class is an interface class.
A geometry is the spatial representation of a feature.
static geos::unique_ptr asGeos(const QgsGeometry &geometry, double precision=0)
Returns a geos geometry - caller takes ownership of the object (should be deleted with GEOSGeom_destr...
static GEOSContextHandle_t getGEOSHandler()
PlacementEngineVersion
Placement engine version.
@ PlacementEngineVersion1
Version 1, matches placement from QGIS <= 3.10.1.
@ PlacementEngineVersion2
Version 2 (default for new projects since QGIS 3.12)
Placement
Placement modes which determine how label candidates are generated for a feature.
A rectangle specified with double values.
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
QgsRectangle buffered(double width) const
Gets rectangle enlarged by buffer.
static void addObstacleCostPenalty(pal::LabelPosition *lp, pal::FeaturePart *obstacle, Pal *pal)
Increase candidate's cost according to its collision with passed feature.
static void finalizeCandidatesCosts(Feats *feat, double bbx[4], double bby[4])
Sort candidates by costs, skip the worse ones, evaluate polygon candidates.
static bool candidateSortGrow(const std::unique_ptr< pal::LabelPosition > &c1, const std::unique_ptr< pal::LabelPosition > &c2)
Sorts label candidates in ascending order of cost.
Main class to handle feature.
std::vector< std::unique_ptr< LabelPosition > > createCandidates(Pal *pal)
Generates a list of candidate positions for labels for this feature.
FeaturePart * getSelfObstacle(int i)
Gets hole (inner ring) - considered as obstacle.
Thrown when trying to access an empty data set.
LabelPosition is a candidate feature label position.
bool isInConflict(const LabelPosition *ls) const
Check whether or not this overlap with another labelPosition.
unsigned int globalId() const
Returns the global ID for the candidate, which is unique for a single run of the pal labelling engine...
A set of features which influence the labeling process.
std::deque< std::unique_ptr< FeaturePart > > mFeatureParts
List of feature parts.
QList< FeaturePart * > mObstacleParts
List of obstacle parts.
QString name() const
Returns the layer's name.
bool active() const
Returns whether the layer is currently active.
bool mergeConnectedLines() const
Returns whether connected lines will be merged before labeling.
void joinConnectedFeatures()
Join connected features with the same label text.
void chopFeaturesAtRepeatDistance()
Chop layer features at the repeat distance.
void setPlacementVersion(QgsLabelingEngineSettings::PlacementEngineVersion placementVersion)
Sets the placement engine version, which dictates how the label placement problem is solved.
QgsLabelingEngineSettings::PlacementEngineVersion placementVersion() const
Returns the placement engine version, which dictates how the label placement problem is solved.
void setShowPartialLabels(bool show)
Sets whether partial labels show be allowed.
Layer * addLayer(QgsAbstractLabelProvider *provider, const QString &layerName, QgsPalLayerSettings::Placement arrangement, double defaultPriority, bool active, bool toLabel, bool displayAll=false)
add a new layer
std::unique_ptr< Problem > extractProblem(const QgsRectangle &extent, const QgsGeometry &mapBoundary)
Extracts the labeling problem for the specified map extent - only features within this extent will be...
void removeLayer(Layer *layer)
remove a layer
bool candidatesAreConflicting(const LabelPosition *lp1, const LabelPosition *lp2) const
Returns true if a labelling candidate lp1 conflicts with lp2.
QList< LabelPosition * > solveProblem(Problem *prob, bool displayAll, QList< pal::LabelPosition * > *unlabeled=nullptr)
Solves the labeling problem, selecting the best candidate locations for all labels and returns a list...
bool showPartialLabels() const
Returns whether partial labels should be allowed.
Pal()
Create an new pal instance.
bool isCanceled()
Check whether the job has been canceled.
QgsRectangle boundingBox() const
Returns the point set bounding box.
Representation of a labeling problem.
QList< LabelPosition * > getSolution(bool returnInactive, QList< LabelPosition * > *unlabeled=nullptr)
Solves the labeling problem, selecting the best candidate locations for all labels and returns a list...
void chain_search()
Test with very-large scale neighborhood.
std::unique_ptr< const GEOSPreparedGeometry, GeosDeleter > prepared_unique_ptr
Scoped GEOS prepared geometry pointer.
std::unique_ptr< GEOSGeometry, GeosDeleter > unique_ptr
Scoped GEOS pointer.