mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-12 00:02:25 -04:00
[needs-docs] When running in label engine v2 mode, discard any candidates where
they collide with an obstacle feature of greater weight when compared to the label's priority Previously, obstacle weight was used ONLY to rank a features' label candidates relative to each other, but was never used to actually prune candidates completely. This meant that the labeling obstacle functionality was confusing and frustrating for users to work with -- because despite setting layers as the maximum possible blocking weight, you'd still see labels being placed over these features (e.g. where the labeling engine had no other choice). Now, (when a project is set to v2 labeling engine mode), labels will NEVER be placed over obstacles of greater weight. This means that labels will potentially be omitted if the only choice is to place them over a high weighting obstacle. But ultimately, that's much more understandable for users -- they've manually set a particular layer to a high obstacle factor, so we should respect that and never place labels on these features. In the end, this change makes the labeling placement much simpler to understand for users, and should give power users a much nicer experience all round. Funded by the QGIS grants program
This commit is contained in:
parent
bee6ab846e
commit
19b8d43388
@ -35,7 +35,7 @@ bool CostCalculator::candidateSortShrink( const std::unique_ptr< LabelPosition >
|
|||||||
return c1->cost() > c2->cost();
|
return c1->cost() > c2->cost();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CostCalculator::addObstacleCostPenalty( LabelPosition *lp, FeaturePart *obstacle )
|
void CostCalculator::addObstacleCostPenalty( LabelPosition *lp, FeaturePart *obstacle, Pal *pal )
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
double dist;
|
double dist;
|
||||||
@ -83,11 +83,30 @@ void CostCalculator::addObstacleCostPenalty( LabelPosition *lp, FeaturePart *obs
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//scale cost by obstacle's factor
|
||||||
|
double obstacleCost = obstacle->obstacleFactor() * double( n );
|
||||||
if ( n > 0 )
|
if ( n > 0 )
|
||||||
lp->setConflictsWithObstacle( true );
|
lp->setConflictsWithObstacle( true );
|
||||||
|
|
||||||
//scale cost by obstacle's factor
|
switch ( pal->placementVersion() )
|
||||||
double obstacleCost = obstacle->obstacleFactor() * double( n );
|
{
|
||||||
|
case QgsLabelingEngineSettings::PlacementEngineVersion1:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsLabelingEngineSettings::PlacementEngineVersion2:
|
||||||
|
{
|
||||||
|
// obstacle factor is from 0 -> 2, label priority is from 1 -> 0. argh!
|
||||||
|
const double priority = 2 * ( 1 - lp->feature->calculatePriority() );
|
||||||
|
const double obstaclePriority = obstacle->obstacleFactor();
|
||||||
|
|
||||||
|
// if feature priority is < obstaclePriorty, there's a hard conflict...
|
||||||
|
if ( n > 0 && ( priority < obstaclePriority && !qgsDoubleNear( priority, obstaclePriority, 0.001 ) ) )
|
||||||
|
{
|
||||||
|
lp->setHasHardObstacleConflict( true );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// label cost is penalized
|
// label cost is penalized
|
||||||
lp->setCost( lp->cost() + obstacleCost );
|
lp->setCost( lp->cost() + obstacleCost );
|
||||||
@ -185,6 +204,7 @@ std::size_t CostCalculator::finalizeCandidatesCosts( Feats *feat, std::size_t ma
|
|||||||
}
|
}
|
||||||
while ( stop == 0 && discrim < feat->candidates.back()->cost() + 2.0 );
|
while ( stop == 0 && discrim < feat->candidates.back()->cost() + 2.0 );
|
||||||
|
|
||||||
|
// THIS LOOKS SUSPICIOUS -- it clamps all costs to a fixed value??
|
||||||
if ( discrim > 1.5 )
|
if ( discrim > 1.5 )
|
||||||
{
|
{
|
||||||
for ( std::size_t k = 0; k < stop; k++ )
|
for ( std::size_t k = 0; k < stop; k++ )
|
||||||
|
@ -29,6 +29,7 @@ namespace pal
|
|||||||
{
|
{
|
||||||
class Feats;
|
class Feats;
|
||||||
class LabelPosition;
|
class LabelPosition;
|
||||||
|
class Pal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \ingroup core
|
* \ingroup core
|
||||||
@ -37,7 +38,7 @@ namespace pal
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//! Increase candidate's cost according to its collision with passed feature
|
//! Increase candidate's cost according to its collision with passed feature
|
||||||
static void addObstacleCostPenalty( LabelPosition *lp, pal::FeaturePart *obstacle );
|
static void addObstacleCostPenalty( LabelPosition *lp, pal::FeaturePart *obstacle, Pal *pal );
|
||||||
|
|
||||||
//! Calculates the costs for polygon label candidates
|
//! Calculates the costs for polygon label candidates
|
||||||
static void setPolygonCandidatesCost( std::size_t nblp, std::vector<std::unique_ptr<pal::LabelPosition> > &lPos, RTree<pal::FeaturePart *, double, 2, double> *obstacles, double bbx[4], double bby[4] );
|
static void setPolygonCandidatesCost( std::size_t nblp, std::vector<std::unique_ptr<pal::LabelPosition> > &lPos, RTree<pal::FeaturePart *, double, 2, double> *obstacles, double bbx[4], double bby[4] );
|
||||||
|
@ -407,6 +407,13 @@ void LabelPosition::setConflictsWithObstacle( bool conflicts )
|
|||||||
nextPart->setConflictsWithObstacle( conflicts );
|
nextPart->setConflictsWithObstacle( conflicts );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LabelPosition::setHasHardObstacleConflict( bool conflicts )
|
||||||
|
{
|
||||||
|
mHasHardConflict = conflicts;
|
||||||
|
if ( nextPart )
|
||||||
|
nextPart->setHasHardObstacleConflict( conflicts );
|
||||||
|
}
|
||||||
|
|
||||||
bool LabelPosition::polygonObstacleCallback( FeaturePart *obstacle, void *ctx )
|
bool LabelPosition::polygonObstacleCallback( FeaturePart *obstacle, void *ctx )
|
||||||
{
|
{
|
||||||
PolygonCostCalculator *pCost = reinterpret_cast< PolygonCostCalculator * >( ctx );
|
PolygonCostCalculator *pCost = reinterpret_cast< PolygonCostCalculator * >( ctx );
|
||||||
@ -453,7 +460,7 @@ bool LabelPosition::pruneCallback( LabelPosition *candidatePosition, void *ctx )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CostCalculator::addObstacleCostPenalty( candidatePosition, obstaclePart );
|
CostCalculator::addObstacleCostPenalty( candidatePosition, obstaclePart, ( reinterpret_cast< PruneCtx * >( ctx ) )->pal );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -217,6 +217,22 @@ namespace pal
|
|||||||
*/
|
*/
|
||||||
bool conflictsWithObstacle() const { return mHasObstacleConflict; }
|
bool conflictsWithObstacle() const { return mHasObstacleConflict; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether the position is marked as having a hard conflict with an obstacle feature.
|
||||||
|
* A hard conflict means that the placement should (usually) not be considered, because the candidate
|
||||||
|
* conflicts with a obstacle of sufficient weight.
|
||||||
|
* \see hasHardObstacleConflict()
|
||||||
|
*/
|
||||||
|
void setHasHardObstacleConflict( bool conflicts );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the position is marked as having a hard conflict with an obstacle feature.
|
||||||
|
* A hard conflict means that the placement should (usually) not be considered, because the candidate
|
||||||
|
* conflicts with a obstacle of sufficient weight.
|
||||||
|
* \see setHasHardObstacleConflict()
|
||||||
|
*/
|
||||||
|
bool hasHardObstacleConflict() const { return mHasHardConflict; }
|
||||||
|
|
||||||
//! Make sure the cost is less than 1
|
//! Make sure the cost is less than 1
|
||||||
void validateCost();
|
void validateCost();
|
||||||
|
|
||||||
@ -329,6 +345,7 @@ namespace pal
|
|||||||
private:
|
private:
|
||||||
double mCost;
|
double mCost;
|
||||||
bool mHasObstacleConflict;
|
bool mHasObstacleConflict;
|
||||||
|
bool mHasHardConflict = false;
|
||||||
int mUpsideDownCharCount;
|
int mUpsideDownCharCount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -380,6 +380,27 @@ std::unique_ptr<Problem> Pal::extract( const QgsRectangle &extent, const QgsGeom
|
|||||||
feat->candidates.pop_back();
|
feat->candidates.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch ( mPlacementVersion )
|
||||||
|
{
|
||||||
|
case QgsLabelingEngineSettings::PlacementEngineVersion1:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QgsLabelingEngineSettings::PlacementEngineVersion2:
|
||||||
|
{
|
||||||
|
// v2 placement rips out candidates where the candidate cost is too high when compared to
|
||||||
|
// their inactive cost
|
||||||
|
feat->candidates.erase( std::remove_if( feat->candidates.begin(), feat->candidates.end(), [ & ]( std::unique_ptr< LabelPosition > &candidate )
|
||||||
|
{
|
||||||
|
if ( candidate->hasHardObstacleConflict() )
|
||||||
|
{
|
||||||
|
feat->candidates.back()->removeFromIndex( prob->mAllCandidatesIndex );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} ), feat->candidates.end() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( isCanceled() )
|
if ( isCanceled() )
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user