From b4311877f72bdb6516080011c7aa0c3c0d108307 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 23 Jul 2015 15:00:53 +1000 Subject: [PATCH] [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. --- python/core/qgspallabeling.sip | 6 ++++ src/app/qgslabelinggui.cpp | 8 +++++ src/core/pal/costcalculator.cpp | 5 ++- src/core/pal/feature.cpp | 1 + src/core/pal/feature.h | 16 +++++++++ src/core/qgspallabeling.cpp | 19 ++++++++++ src/core/qgspallabeling.h | 6 ++++ src/ui/qgslabelingguibase.ui | 64 +++++++++++++++++++++++++++++---- 8 files changed, 118 insertions(+), 7 deletions(-) diff --git a/python/core/qgspallabeling.sip b/python/core/qgspallabeling.sip index 16904516f11..8c75bd1a859 100644 --- a/python/core/qgspallabeling.sip +++ b/python/core/qgspallabeling.sip @@ -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 */ diff --git a/src/app/qgslabelinggui.cpp b/src/app/qgslabelinggui.cpp index 0ecfe1aefa0..ad4b9bfc67e 100644 --- a/src/app/qgslabelinggui.cpp +++ b/src/app/qgslabelinggui.cpp @@ -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 ) diff --git a/src/core/pal/costcalculator.cpp b/src/core/pal/costcalculator.cpp index 670806204ba..53e8a35d4ba 100644 --- a/src/core/pal/costcalculator.cpp +++ b/src/core/pal/costcalculator.cpp @@ -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 ); } diff --git a/src/core/pal/feature.cpp b/src/core/pal/feature.cpp index 32f27cf68d8..0c7a00471a4 100644 --- a/src/core/pal/feature.cpp +++ b/src/core/pal/feature.cpp @@ -76,6 +76,7 @@ namespace pal , alwaysShow( false ) , mFixedQuadrant( false ) , mIsObstacle( true ) + , mObstacleFactor( 1.0 ) , mPriority( -1.0 ) { assert( finite( lx ) && finite( ly ) ); diff --git a/src/core/pal/feature.h b/src/core/pal/feature.h index 3e9d0c1c163..ed05ad54708 100644 --- a/src/core/pal/feature.h +++ b/src/core/pal/feature.h @@ -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; diff --git a/src/core/qgspallabeling.cpp b/src/core/qgspallabeling.cpp index a25ea9ef363..5d52133f746 100644 --- a/src/core/qgspallabeling.cpp +++ b/src/core/qgspallabeling.cpp @@ -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( "RepeatDistanceUnit", -1 ) ); mDataDefinedNames.insert( Priority, QPair( "Priority", -1 ) ); mDataDefinedNames.insert( IsObstacle, QPair( "IsObstacle", -1 ) ); + mDataDefinedNames.insert( ObstacleFactor, QPair( "ObstacleFactor", -1 ) ); // (data defined only) mDataDefinedNames.insert( PositionX, QPair( "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 ) diff --git a/src/core/qgspallabeling.h b/src/core/qgspallabeling.h index c9969d45b36..8b017595189 100644 --- a/src/core/qgspallabeling.h +++ b/src/core/qgspallabeling.h @@ -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; diff --git a/src/ui/qgslabelingguibase.ui b/src/ui/qgslabelingguibase.ui index 29f5104d0d9..7b29356c8e4 100644 --- a/src/ui/qgslabelingguibase.ui +++ b/src/ui/qgslabelingguibase.ui @@ -3488,8 +3488,8 @@ font-style: italic; 0 - 0 - 427 + -466 + 578 829 @@ -4818,9 +4818,9 @@ font-style: italic; 0 - -361 + -401 578 - 724 + 764 @@ -5517,6 +5517,58 @@ font-style: italic; + + + + + 0 + + + + + Low priority + + + + + + + 0 + + + 100 + + + 50 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + 10 + + + + + + + High priority + + + + + + + ... + + + + + + @@ -5981,8 +6033,8 @@ font-style: italic; setCurrentIndex(int) - 47 - 524 + 34 + 599 633