[FEATURE][labeling] Add priority control for obstacles

Allows you to make labels prefer to overlap features from certain
layers rather than others. Can also be data defined, so that certain
features are more likely to be covered than others.
This commit is contained in:
Nyall Dawson 2015-07-23 15:00:53 +10:00
parent c97733ea27
commit b4311877f7
8 changed files with 118 additions and 7 deletions

View File

@ -299,6 +299,7 @@ class QgsPalLayerSettings
FontMinPixel,
FontMaxPixel,
IsObstacle,
ObstacleFactor,
// (data defined only)
Show,
@ -469,6 +470,11 @@ class QgsPalLayerSettings
double minFeatureSize; // minimum feature size to be labelled (in mm)
bool obstacle; // whether features for layer are obstacles to labels of other layers
/** Obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels,
* > 1.0 less likely to be covered
*/
double obstacleFactor;
/** Controls how features act as obstacles for labels
*/

View File

@ -361,7 +361,10 @@ void QgsLabelingGui::init()
mPrioritySlider->setValue( lyr.priority );
mChkNoObstacle->setChecked( lyr.obstacle );
mObstacleFactorSlider->setValue( lyr.obstacleFactor * 50 );
mObstacleTypeComboBox->setCurrentIndex( mObstacleTypeComboBox->findData( lyr.obstacleType ) );
mPolygonObstacleTypeFrame->setEnabled( lyr.obstacle );
mObstaclePriorityFrame->setEnabled( lyr.obstacle );
chkLabelPerFeaturePart->setChecked( lyr.labelPerPart );
mPalShowAllLabelsForLayerChkBx->setChecked( lyr.displayAll );
chkMergeLines->setChecked( lyr.mergeLines );
@ -657,6 +660,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
lyr.priority = mPrioritySlider->value();
lyr.obstacle = mChkNoObstacle->isChecked() || mLabelModeComboBox->currentIndex() == 2;
lyr.obstacleFactor = mObstacleFactorSlider->value() / 50.0;
lyr.obstacleType = ( QgsPalLayerSettings::ObstacleType )mObstacleTypeComboBox->itemData( mObstacleTypeComboBox->currentIndex() ).toInt();
lyr.labelPerPart = chkLabelPerFeaturePart->isChecked();
lyr.displayAll = mPalShowAllLabelsForLayerChkBx->isChecked();
@ -857,6 +861,7 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
setDataDefinedProperty( mShowLabelDDBtn, QgsPalLayerSettings::Show, lyr );
setDataDefinedProperty( mAlwaysShowDDBtn, QgsPalLayerSettings::AlwaysShow, lyr );
setDataDefinedProperty( mIsObstacleDDBtn, QgsPalLayerSettings::IsObstacle, lyr );
setDataDefinedProperty( mObstacleFactorDDBtn, QgsPalLayerSettings::ObstacleFactor, lyr );
return lyr;
}
@ -1125,6 +1130,8 @@ void QgsLabelingGui::populateDataDefinedButtons( QgsPalLayerSettings& s )
mIsObstacleDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::IsObstacle ),
QgsDataDefinedButton::AnyType, QgsDataDefinedButton::boolDesc() );
mObstacleFactorDDBtn->init( mLayer, s.dataDefinedProperty( QgsPalLayerSettings::ObstacleFactor ),
QgsDataDefinedButton::AnyType, tr( "double [0.0-10.0]" ) );
}
void QgsLabelingGui::changeTextColor( const QColor &color )
@ -1714,6 +1721,7 @@ void QgsLabelingGui::on_mDirectSymbRightToolBtn_clicked()
void QgsLabelingGui::on_mChkNoObstacle_toggled( bool active )
{
mPolygonObstacleTypeFrame->setEnabled( active );
mObstaclePriorityFrame->setEnabled( active );
}
void QgsLabelingGui::showBackgroundRadius( bool show )

View File

@ -72,8 +72,11 @@ namespace pal
break;
}
//scale cost by obstacle's factor
double obstacleCost = obstacle->getFeature()->obstacleFactor() * double( n );
// label cost is penalized
lp->setCost( lp->getCost() + double( n ) );
lp->setCost( lp->getCost() + obstacleCost );
}

View File

@ -76,6 +76,7 @@ namespace pal
, alwaysShow( false )
, mFixedQuadrant( false )
, mIsObstacle( true )
, mObstacleFactor( 1.0 )
, mPriority( -1.0 )
{
assert( finite( lx ) && finite( ly ) );

View File

@ -120,6 +120,21 @@ namespace pal
*/
double isObstacle() const { return mIsObstacle; }
/** Sets the obstacle factor for the feature. The factor controls the penalty
* for labels overlapping this feature.
* @param factor larger factors ( > 1.0 ) will result in labels
* which are less likely to cover this feature, smaller factors ( < 1.0 ) mean labels
* are more likely to cover this feature (where required)
* @see obstacleFactor
*/
void setObstacleFactor( double factor ) { mObstacleFactor = factor; }
/** Returns the obstacle factor for the feature. The factor controls the penalty
* for labels overlapping this feature.
* @see setObstacleFactor
*/
double obstacleFactor() const { return mObstacleFactor; }
/** Sets the priority for labeling the feature.
* @param priority feature's priority, as a value between 0 (highest priority)
* and 1 (lowest priority). Set to -1.0 to use the layer's default priority
@ -174,6 +189,7 @@ namespace pal
bool mFixedQuadrant;
bool mIsObstacle;
double mObstacleFactor;
//-1 if layer priority should be used
double mPriority;

View File

@ -200,6 +200,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
limitNumLabels = false;
maxNumLabels = 2000;
obstacle = true;
obstacleFactor = 1.0;
obstacleType = PolygonInterior;
// scale factors
@ -295,6 +296,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
mDataDefinedNames.insert( RepeatDistanceUnit, QPair<QString, int>( "RepeatDistanceUnit", -1 ) );
mDataDefinedNames.insert( Priority, QPair<QString, int>( "Priority", -1 ) );
mDataDefinedNames.insert( IsObstacle, QPair<QString, int>( "IsObstacle", -1 ) );
mDataDefinedNames.insert( ObstacleFactor, QPair<QString, int>( "ObstacleFactor", -1 ) );
// (data defined only)
mDataDefinedNames.insert( PositionX, QPair<QString, int>( "PositionX", 9 ) );
@ -415,6 +417,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
limitNumLabels = s.limitNumLabels;
maxNumLabels = s.maxNumLabels;
obstacle = s.obstacle;
obstacleFactor = s.obstacleFactor;
obstacleType = s.obstacleType;
// shape background
@ -924,6 +927,7 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
limitNumLabels = layer->customProperty( "labeling/limitNumLabels", QVariant( false ) ).toBool();
maxNumLabels = layer->customProperty( "labeling/maxNumLabels", QVariant( 2000 ) ).toInt();
obstacle = layer->customProperty( "labeling/obstacle", QVariant( true ) ).toBool();
obstacleFactor = layer->customProperty( "labeling/obstacleFactor", QVariant( 1.0 ) ).toDouble();
obstacleType = ( ObstacleType )layer->customProperty( "labeling/obstacleType", QVariant( PolygonInterior ) ).toUInt();
readDataDefinedPropertyMap( layer, dataDefinedProperties );
@ -1077,6 +1081,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
layer->setCustomProperty( "labeling/limitNumLabels", limitNumLabels );
layer->setCustomProperty( "labeling/maxNumLabels", maxNumLabels );
layer->setCustomProperty( "labeling/obstacle", obstacle );
layer->setCustomProperty( "labeling/obstacleFactor", obstacleFactor );
layer->setCustomProperty( "labeling/obstacleType", ( unsigned int )obstacleType );
writeDataDefinedPropertyMap( layer, dataDefinedProperties );
@ -2214,6 +2219,20 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
feat->setIsObstacle( exprVal.toBool() );
}
double featObstacleFactor = obstacleFactor;
if ( dataDefinedEvaluate( QgsPalLayerSettings::ObstacleFactor, exprVal ) )
{
bool ok;
double factorD = exprVal.toDouble( &ok );
if ( ok )
{
factorD = qBound( 0.0, factorD, 10.0 );
factorD = factorD / 5.0 + 0.0001; // convert 0 -> 10 to 0.0001 -> 2.0
featObstacleFactor = factorD;
}
}
feat->setObstacleFactor( featObstacleFactor );
//add parameters for data defined labeling to QgsPalGeometry
QMap< DataDefinedProperties, QVariant >::const_iterator dIt = dataDefinedValues.constBegin();
for ( ; dIt != dataDefinedValues.constEnd(); ++dIt )

View File

@ -274,6 +274,7 @@ class CORE_EXPORT QgsPalLayerSettings
FontMinPixel = 25,
FontMaxPixel = 26,
IsObstacle = 88,
ObstacleFactor = 89,
// (data defined only)
Show = 15,
@ -445,6 +446,11 @@ class CORE_EXPORT QgsPalLayerSettings
double minFeatureSize; // minimum feature size to be labelled (in mm)
bool obstacle; // whether features for layer are obstacles to labels of other layers
/** Obstacle factor, where 1.0 = default, < 1.0 more likely to be covered by labels,
* > 1.0 less likely to be covered
*/
double obstacleFactor;
/** Controls how features act as obstacles for labels
*/
ObstacleType obstacleType;

View File

@ -3488,8 +3488,8 @@ font-style: italic;</string>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>427</width>
<y>-466</y>
<width>578</width>
<height>829</height>
</rect>
</property>
@ -4818,9 +4818,9 @@ font-style: italic;</string>
<property name="geometry">
<rect>
<x>0</x>
<y>-361</y>
<y>-401</y>
<width>578</width>
<height>724</height>
<height>764</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
@ -5517,6 +5517,58 @@ font-style: italic;</string>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="mObstaclePriorityFrame">
<layout class="QHBoxLayout" name="horizontalLayout_18">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_40">
<property name="text">
<string>Low priority</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="mObstacleFactorSlider">
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>10</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_41">
<property name="text">
<string>High priority</string>
</property>
</widget>
</item>
<item>
<widget class="QgsDataDefinedButton" name="mObstacleFactorDDBtn">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QFrame" name="mPolygonObstacleTypeFrame">
<property name="frameShape">
@ -5981,8 +6033,8 @@ font-style: italic;</string>
<slot>setCurrentIndex(int)</slot>
<hints>
<hint type="sourcelabel">
<x>47</x>
<y>524</y>
<x>34</x>
<y>599</y>
</hint>
<hint type="destinationlabel">
<x>633</x>