Skip to content

Commit 388e404

Browse files
committedJul 15, 2015
[FEATURE][labeling] Add option for obstacle only layers
This allows users to set a layer as just an obstacle for other layer's labels without rendering any labels of its own. Ideas for UI improvements are welcome!
1 parent ea41b92 commit 388e404

File tree

6 files changed

+282
-502
lines changed

6 files changed

+282
-502
lines changed
 

‎python/core/qgspallabeling.sip

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,15 @@ class QgsPalLayerSettings
294294

295295
// whether to label this layer
296296
bool enabled;
297+
298+
/** Whether to draw labels for this layer. For some layers it may be desirable
299+
* to register their features as obstacles for other labels without requiring
300+
* labels to be drawn for the layer itself. In this case drawLabels can be set
301+
* to false and obstacle set to true, which will result in the layer acting
302+
* as an obstacle but having no labels of its own.
303+
* @note added in QGIS 2.12
304+
*/
305+
bool drawLabels;
297306

298307
//-- text style
299308

‎src/app/qgslabelinggui.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,6 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,
8484

8585
mRefFont = lblFontPreview->font();
8686

87-
// main layer label-enabling connections
88-
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), mFieldExpressionWidget, SLOT( setEnabled( bool ) ) );
89-
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), mLabelingFrame, SLOT( setEnabled( bool ) ) );
90-
9187
// connections for groupboxes with separate activation checkboxes (that need to honor data defined setting)
9288
connect( mBufferDrawChkBx, SIGNAL( toggled( bool ) ), this, SLOT( updateUi() ) );
9389
connect( mShapeDrawChkBx, SIGNAL( toggled( bool ) ), this, SLOT( updateUi() ) );
@@ -293,9 +289,16 @@ void QgsLabelingGui::init()
293289
blockInitSignals( true );
294290

295291
// enable/disable main options based upon whether layer is being labeled
296-
chkEnableLabeling->setChecked( lyr.enabled );
297-
mFieldExpressionWidget->setEnabled( chkEnableLabeling->isChecked() );
298-
mLabelingFrame->setEnabled( chkEnableLabeling->isChecked() );
292+
if ( !lyr.enabled )
293+
{
294+
mLabelModeComboBox->setCurrentIndex( 0 );
295+
}
296+
else
297+
{
298+
mLabelModeComboBox->setCurrentIndex( lyr.drawLabels ? 1 : 2 );
299+
}
300+
mFieldExpressionWidget->setEnabled( mLabelModeComboBox->currentIndex() == 1 );
301+
mLabelingFrame->setEnabled( mLabelModeComboBox->currentIndex() == 1 );
299302

300303
// set the current field or add the current expression to the bottom of the list
301304
mFieldExpressionWidget->setField( lyr.fieldName );
@@ -572,7 +575,8 @@ QgsPalLayerSettings QgsLabelingGui::layerSettings()
572575
{
573576
QgsPalLayerSettings lyr;
574577

575-
lyr.enabled = chkEnableLabeling->isChecked();
578+
lyr.enabled = mLabelModeComboBox->currentIndex() > 0;
579+
lyr.drawLabels = mLabelModeComboBox->currentIndex() == 1;
576580

577581
bool isExpression;
578582
lyr.fieldName = mFieldExpressionWidget->currentField( &isExpression );
@@ -1617,6 +1621,13 @@ void QgsLabelingGui::updateSvgWidgets( const QString& svgPath )
16171621
mShapeSVGUnitsLabel->setEnabled( validSVG && outlineWidthParam );
16181622
}
16191623

1624+
void QgsLabelingGui::on_mLabelModeComboBox_currentIndexChanged( int index )
1625+
{
1626+
bool labelsEnabled = ( index == 1 );
1627+
mFieldExpressionWidget->setEnabled( labelsEnabled );
1628+
mLabelingFrame->setEnabled( labelsEnabled );
1629+
}
1630+
16201631
void QgsLabelingGui::on_mShapeSVGSelectorBtn_clicked()
16211632
{
16221633
QgsSvgSelectorDialog svgDlg( this );

‎src/app/qgslabelinggui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class APP_EXPORT QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
5353
void updatePlacementWidgets();
5454
void updateSvgWidgets( const QString& svgPath );
5555

56+
void on_mLabelModeComboBox_currentIndexChanged( int index );
5657
void on_mPreviewSizeSlider_valueChanged( int i );
5758
void on_mFontSizeSpinBox_valueChanged( double d );
5859
void on_mFontCapitalsComboBox_currentIndexChanged( int index );

‎src/core/qgspallabeling.cpp

Lines changed: 123 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
9191
, expression( 0 )
9292
{
9393
enabled = false;
94+
drawLabels = true;
9495
isExpression = false;
9596
fieldIndex = 0;
9697

@@ -333,6 +334,7 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
333334
// copy only permanent stuff
334335

335336
enabled = s.enabled;
337+
drawLabels = s.drawLabels;
336338

337339
// text style
338340
fieldName = s.fieldName;
@@ -717,6 +719,7 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
717719
// NOTE: set defaults for newly added properties, for backwards compatibility
718720

719721
enabled = layer->labelsEnabled();
722+
drawLabels = layer->customProperty( "labeling/drawLabels", true ).toBool();
720723

721724
// text style
722725
fieldName = layer->customProperty( "labeling/fieldName" ).toString();
@@ -927,6 +930,7 @@ void QgsPalLayerSettings::writeToLayer( QgsVectorLayer* layer )
927930
layer->setCustomProperty( "labeling", "pal" );
928931

929932
layer->setCustomProperty( "labeling/enabled", enabled );
933+
layer->setCustomProperty( "labeling/drawLabels", drawLabels );
930934

931935
// text style
932936
layer->setCustomProperty( "labeling/fieldName", fieldName );
@@ -1410,6 +1414,15 @@ void QgsPalLayerSettings::calculateLabelSize( const QFontMetricsF* fm, QString t
14101414

14111415
void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
14121416
{
1417+
if ( !drawLabels )
1418+
{
1419+
if ( obstacle )
1420+
{
1421+
registerObstacleFeature( f, context, dxfLayer );
1422+
}
1423+
return;
1424+
}
1425+
14131426
QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
14141427
mCurFeat = &f;
14151428
// mCurFields = &layer->pendingFields();
@@ -2199,6 +2212,58 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
21992212
lbl->setIsPinned( labelIsPinned );
22002213
}
22012214

2215+
void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
2216+
{
2217+
mCurFeat = &f;
2218+
2219+
const QgsGeometry* geom = f.constGeometry();
2220+
if ( !geom )
2221+
{
2222+
return;
2223+
}
2224+
2225+
const GEOSGeometry* geos_geom = 0;
2226+
QScopedPointer<QgsGeometry> scopedPreparedGeom;
2227+
2228+
if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
2229+
{
2230+
scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
2231+
if ( !scopedPreparedGeom.data() )
2232+
return;
2233+
geos_geom = scopedPreparedGeom.data()->asGeos();
2234+
}
2235+
else
2236+
{
2237+
geos_geom = geom->asGeos();
2238+
}
2239+
2240+
if ( geos_geom == NULL )
2241+
return; // invalid geometry
2242+
2243+
GEOSGeometry* geos_geom_clone;
2244+
geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );
2245+
2246+
QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), QString(), geos_geom_clone );
2247+
2248+
lbl->setDxfLayer( dxfLayer );
2249+
2250+
// record the created geometry - it will be deleted at the end.
2251+
geometries.append( lbl );
2252+
2253+
// feature to the layer
2254+
try
2255+
{
2256+
if ( !palLayer->registerFeature( lbl->strId(), lbl, 0, 0 ) )
2257+
return;
2258+
}
2259+
catch ( std::exception &e )
2260+
{
2261+
Q_UNUSED( e );
2262+
QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
2263+
return;
2264+
}
2265+
}
2266+
22022267
bool QgsPalLayerSettings::dataDefinedValEval( const QString& valType,
22032268
QgsPalLayerSettings::DataDefinedProperties p,
22042269
QVariant& exprVal )
@@ -3136,26 +3201,29 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames,
31363201
QgsPalLayerSettings lyrTmp;
31373202
lyrTmp.readFromLayer( layer );
31383203

3139-
if ( lyrTmp.fieldName.isEmpty() )
3140-
{
3141-
return 0;
3142-
}
3143-
3144-
if ( lyrTmp.isExpression )
3204+
if ( lyrTmp.drawLabels )
31453205
{
3146-
QgsExpression exp( lyrTmp.fieldName );
3147-
if ( exp.hasEvalError() )
3206+
if ( lyrTmp.fieldName.isEmpty() )
31483207
{
3149-
QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
31503208
return 0;
31513209
}
3152-
}
3153-
else
3154-
{
3155-
// If we aren't an expression, we check to see if we can find the column.
3156-
if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 )
3210+
3211+
if ( lyrTmp.isExpression )
31573212
{
3158-
return 0;
3213+
QgsExpression exp( lyrTmp.fieldName );
3214+
if ( exp.hasEvalError() )
3215+
{
3216+
QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
3217+
return 0;
3218+
}
3219+
}
3220+
else
3221+
{
3222+
// If we aren't an expression, we check to see if we can find the column.
3223+
if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 )
3224+
{
3225+
return 0;
3226+
}
31593227
}
31603228
}
31613229

@@ -3166,52 +3234,55 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames,
31663234

31673235
lyr.mCurFields = &( layer->pendingFields() );
31683236

3169-
// add field indices for label's text, from expression or field
3170-
if ( lyr.isExpression )
3237+
if ( lyrTmp.drawLabels )
31713238
{
3172-
// prepare expression for use in QgsPalLayerSettings::registerFeature()
3173-
QgsExpression* exp = lyr.getLabelExpression();
3174-
exp->prepare( layer->pendingFields() );
3175-
if ( exp->hasEvalError() )
3239+
// add field indices for label's text, from expression or field
3240+
if ( lyr.isExpression )
31763241
{
3177-
QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
3242+
// prepare expression for use in QgsPalLayerSettings::registerFeature()
3243+
QgsExpression* exp = lyr.getLabelExpression();
3244+
exp->prepare( layer->pendingFields() );
3245+
if ( exp->hasEvalError() )
3246+
{
3247+
QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
3248+
}
3249+
foreach ( QString name, exp->referencedColumns() )
3250+
{
3251+
QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
3252+
attrNames.append( name );
3253+
}
31783254
}
3179-
foreach ( QString name, exp->referencedColumns() )
3255+
else
31803256
{
3181-
QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
3182-
attrNames.append( name );
3257+
attrNames.append( lyr.fieldName );
31833258
}
3184-
}
3185-
else
3186-
{
3187-
attrNames.append( lyr.fieldName );
3188-
}
31893259

3190-
// add field indices of data defined expression or field
3191-
QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
3192-
for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
3193-
{
3194-
QgsDataDefined* dd = dIt.value();
3195-
if ( !dd->isActive() )
3260+
// add field indices of data defined expression or field
3261+
QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
3262+
for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
31963263
{
3197-
continue;
3198-
}
3264+
QgsDataDefined* dd = dIt.value();
3265+
if ( !dd->isActive() )
3266+
{
3267+
continue;
3268+
}
31993269

3200-
// NOTE: the following also prepares any expressions for later use
3270+
// NOTE: the following also prepares any expressions for later use
32013271

3202-
// store parameters for data defined expressions
3203-
QMap<QString, QVariant> exprParams;
3204-
exprParams.insert( "scale", ctx.rendererScale() );
3272+
// store parameters for data defined expressions
3273+
QMap<QString, QVariant> exprParams;
3274+
exprParams.insert( "scale", ctx.rendererScale() );
32053275

3206-
dd->setExpressionParams( exprParams );
3276+
dd->setExpressionParams( exprParams );
32073277

3208-
// this will return columns for expressions or field name, depending upon what is set to be used
3209-
QStringList cols = dd->referencedColumns( layer ); // <-- prepares any expressions, too
3278+
// this will return columns for expressions or field name, depending upon what is set to be used
3279+
QStringList cols = dd->referencedColumns( layer ); // <-- prepares any expressions, too
32103280

3211-
//QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
3212-
foreach ( QString name, cols )
3213-
{
3214-
attrNames.append( name );
3281+
//QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
3282+
foreach ( QString name, cols )
3283+
{
3284+
attrNames.append( name );
3285+
}
32153286
}
32163287
}
32173288

@@ -3233,7 +3304,7 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames,
32333304

32343305
Layer* l = mPal->addLayer( layer->id().toUtf8().data(),
32353306
arrangement,
3236-
priority, lyr.obstacle, true, true,
3307+
priority, lyr.obstacle, true, lyr.drawLabels,
32373308
lyr.displayAll );
32383309

32393310
if ( lyr.placementFlags )
@@ -4187,6 +4258,8 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
41874258
}
41884259

41894260
const QgsPalLayerSettings& lyr = layer( layerName );
4261+
if ( !lyr.drawLabels )
4262+
continue;
41904263

41914264
// Copy to temp, editable layer settings
41924265
// these settings will be changed by any data defined values, then used for rendering label components

‎src/core/qgspallabeling.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,15 @@ class CORE_EXPORT QgsPalLayerSettings
270270
// whether to label this layer
271271
bool enabled;
272272

273+
/** Whether to draw labels for this layer. For some layers it may be desirable
274+
* to register their features as obstacles for other labels without requiring
275+
* labels to be drawn for the layer itself. In this case drawLabels can be set
276+
* to false and obstacle set to true, which will result in the layer acting
277+
* as an obstacle but having no labels of its own.
278+
* @note added in QGIS 2.12
279+
*/
280+
bool drawLabels;
281+
273282
//-- text style
274283

275284
QString fieldName;
@@ -561,6 +570,9 @@ class CORE_EXPORT QgsPalLayerSettings
561570
@return true if above size, false if below*/
562571
bool checkMinimumSizeMM( const QgsRenderContext& ct, const QgsGeometry* geom, double minSize ) const;
563572

573+
/** Registers a feature as an obstacle only (no label rendered)
574+
*/
575+
void registerObstacleFeature( QgsFeature &f, const QgsRenderContext &context, QString dxfLayer );
564576

565577
QMap<DataDefinedProperties, QVariant> dataDefinedValues;
566578
QgsExpression* expression;

0 commit comments

Comments
 (0)
Please sign in to comment.