Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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.
  • Loading branch information
nyalldawson committed Jul 23, 2015
1 parent c97733e commit b431187
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 7 deletions.
6 changes: 6 additions & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -299,6 +299,7 @@ class QgsPalLayerSettings
FontMinPixel,
FontMaxPixel,
IsObstacle,
ObstacleFactor,

// (data defined only)
Show,
Expand Down Expand Up @@ -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
*/
Expand Down
8 changes: 8 additions & 0 deletions src/app/qgslabelinggui.cpp
Expand Up @@ -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 );
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 )
Expand Down Expand Up @@ -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 )
Expand Down
5 changes: 4 additions & 1 deletion src/core/pal/costcalculator.cpp
Expand Up @@ -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 );
}


Expand Down
1 change: 1 addition & 0 deletions src/core/pal/feature.cpp
Expand Up @@ -76,6 +76,7 @@ namespace pal
, alwaysShow( false )
, mFixedQuadrant( false )
, mIsObstacle( true )
, mObstacleFactor( 1.0 )
, mPriority( -1.0 )
{
assert( finite( lx ) && finite( ly ) );
Expand Down
16 changes: 16 additions & 0 deletions src/core/pal/feature.h
Expand Up @@ -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
Expand Down Expand Up @@ -174,6 +189,7 @@ namespace pal

bool mFixedQuadrant;
bool mIsObstacle;
double mObstacleFactor;

//-1 if layer priority should be used
double mPriority;
Expand Down
19 changes: 19 additions & 0 deletions src/core/qgspallabeling.cpp
Expand Up @@ -200,6 +200,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
limitNumLabels = false;
maxNumLabels = 2000;
obstacle = true;
obstacleFactor = 1.0;
obstacleType = PolygonInterior;

// scale factors
Expand Down Expand Up @@ -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 ) );
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 )
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgspallabeling.h
Expand Up @@ -274,6 +274,7 @@ class CORE_EXPORT QgsPalLayerSettings
FontMinPixel = 25,
FontMaxPixel = 26,
IsObstacle = 88,
ObstacleFactor = 89,

// (data defined only)
Show = 15,
Expand Down Expand Up @@ -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;
Expand Down
64 changes: 58 additions & 6 deletions src/ui/qgslabelingguibase.ui
Expand Up @@ -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>
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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">
Expand Down Expand Up @@ -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>
Expand Down

0 comments on commit b431187

Please sign in to comment.