Skip to content

Commit

Permalink
[FEATURE][labeling] Add option for obstacle only layers
Browse files Browse the repository at this point in the history
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!
  • Loading branch information
nyalldawson committed Jul 15, 2015
1 parent ea41b92 commit 388e404
Show file tree
Hide file tree
Showing 6 changed files with 282 additions and 502 deletions.
9 changes: 9 additions & 0 deletions python/core/qgspallabeling.sip
Expand Up @@ -294,6 +294,15 @@ class QgsPalLayerSettings

// whether to label this layer
bool enabled;

/** Whether to draw labels for this layer. For some layers it may be desirable
* to register their features as obstacles for other labels without requiring
* labels to be drawn for the layer itself. In this case drawLabels can be set
* to false and obstacle set to true, which will result in the layer acting
* as an obstacle but having no labels of its own.
* @note added in QGIS 2.12
*/
bool drawLabels;

//-- text style

Expand Down
27 changes: 19 additions & 8 deletions src/app/qgslabelinggui.cpp
Expand Up @@ -84,10 +84,6 @@ QgsLabelingGui::QgsLabelingGui( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas,

mRefFont = lblFontPreview->font();

// main layer label-enabling connections
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), mFieldExpressionWidget, SLOT( setEnabled( bool ) ) );
connect( chkEnableLabeling, SIGNAL( toggled( bool ) ), mLabelingFrame, SLOT( setEnabled( bool ) ) );

// connections for groupboxes with separate activation checkboxes (that need to honor data defined setting)
connect( mBufferDrawChkBx, SIGNAL( toggled( bool ) ), this, SLOT( updateUi() ) );
connect( mShapeDrawChkBx, SIGNAL( toggled( bool ) ), this, SLOT( updateUi() ) );
Expand Down Expand Up @@ -293,9 +289,16 @@ void QgsLabelingGui::init()
blockInitSignals( true );

// enable/disable main options based upon whether layer is being labeled
chkEnableLabeling->setChecked( lyr.enabled );
mFieldExpressionWidget->setEnabled( chkEnableLabeling->isChecked() );
mLabelingFrame->setEnabled( chkEnableLabeling->isChecked() );
if ( !lyr.enabled )
{
mLabelModeComboBox->setCurrentIndex( 0 );
}
else
{
mLabelModeComboBox->setCurrentIndex( lyr.drawLabels ? 1 : 2 );
}
mFieldExpressionWidget->setEnabled( mLabelModeComboBox->currentIndex() == 1 );
mLabelingFrame->setEnabled( mLabelModeComboBox->currentIndex() == 1 );

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

lyr.enabled = chkEnableLabeling->isChecked();
lyr.enabled = mLabelModeComboBox->currentIndex() > 0;
lyr.drawLabels = mLabelModeComboBox->currentIndex() == 1;

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

void QgsLabelingGui::on_mLabelModeComboBox_currentIndexChanged( int index )
{
bool labelsEnabled = ( index == 1 );
mFieldExpressionWidget->setEnabled( labelsEnabled );
mLabelingFrame->setEnabled( labelsEnabled );
}

void QgsLabelingGui::on_mShapeSVGSelectorBtn_clicked()
{
QgsSvgSelectorDialog svgDlg( this );
Expand Down
1 change: 1 addition & 0 deletions src/app/qgslabelinggui.h
Expand Up @@ -53,6 +53,7 @@ class APP_EXPORT QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
void updatePlacementWidgets();
void updateSvgWidgets( const QString& svgPath );

void on_mLabelModeComboBox_currentIndexChanged( int index );
void on_mPreviewSizeSlider_valueChanged( int i );
void on_mFontSizeSpinBox_valueChanged( double d );
void on_mFontCapitalsComboBox_currentIndexChanged( int index );
Expand Down
173 changes: 123 additions & 50 deletions src/core/qgspallabeling.cpp
Expand Up @@ -91,6 +91,7 @@ QgsPalLayerSettings::QgsPalLayerSettings()
, expression( 0 )
{
enabled = false;
drawLabels = true;
isExpression = false;
fieldIndex = 0;

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

enabled = s.enabled;
drawLabels = s.drawLabels;

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

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

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

layer->setCustomProperty( "labeling/enabled", enabled );
layer->setCustomProperty( "labeling/drawLabels", drawLabels );

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

void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
{
if ( !drawLabels )
{
if ( obstacle )
{
registerObstacleFeature( f, context, dxfLayer );
}
return;
}

QVariant exprVal; // value() is repeatedly nulled on data defined evaluation and replaced when successful
mCurFeat = &f;
// mCurFields = &layer->pendingFields();
Expand Down Expand Up @@ -2199,6 +2212,58 @@ void QgsPalLayerSettings::registerFeature( QgsFeature& f, const QgsRenderContext
lbl->setIsPinned( labelIsPinned );
}

void QgsPalLayerSettings::registerObstacleFeature( QgsFeature& f, const QgsRenderContext& context, QString dxfLayer )
{
mCurFeat = &f;

const QgsGeometry* geom = f.constGeometry();
if ( !geom )
{
return;
}

const GEOSGeometry* geos_geom = 0;
QScopedPointer<QgsGeometry> scopedPreparedGeom;

if ( QgsPalLabeling::geometryRequiresPreparation( geom, context, ct, extentGeom ) )
{
scopedPreparedGeom.reset( QgsPalLabeling::prepareGeometry( geom, context, ct, extentGeom ) );
if ( !scopedPreparedGeom.data() )
return;
geos_geom = scopedPreparedGeom.data()->asGeos();
}
else
{
geos_geom = geom->asGeos();
}

if ( geos_geom == NULL )
return; // invalid geometry

GEOSGeometry* geos_geom_clone;
geos_geom_clone = GEOSGeom_clone_r( QgsGeometry::getGEOSHandler(), geos_geom );

QgsPalGeometry* lbl = new QgsPalGeometry( f.id(), QString(), geos_geom_clone );

lbl->setDxfLayer( dxfLayer );

// record the created geometry - it will be deleted at the end.
geometries.append( lbl );

// feature to the layer
try
{
if ( !palLayer->registerFeature( lbl->strId(), lbl, 0, 0 ) )
return;
}
catch ( std::exception &e )
{
Q_UNUSED( e );
QgsDebugMsgLevel( QString( "Ignoring feature %1 due PAL exception:" ).arg( f.id() ) + QString::fromLatin1( e.what() ), 4 );
return;
}
}

bool QgsPalLayerSettings::dataDefinedValEval( const QString& valType,
QgsPalLayerSettings::DataDefinedProperties p,
QVariant& exprVal )
Expand Down Expand Up @@ -3136,26 +3201,29 @@ int QgsPalLabeling::prepareLayer( QgsVectorLayer* layer, QStringList& attrNames,
QgsPalLayerSettings lyrTmp;
lyrTmp.readFromLayer( layer );

if ( lyrTmp.fieldName.isEmpty() )
{
return 0;
}

if ( lyrTmp.isExpression )
if ( lyrTmp.drawLabels )
{
QgsExpression exp( lyrTmp.fieldName );
if ( exp.hasEvalError() )
if ( lyrTmp.fieldName.isEmpty() )
{
QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
return 0;
}
}
else
{
// If we aren't an expression, we check to see if we can find the column.
if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 )

if ( lyrTmp.isExpression )
{
return 0;
QgsExpression exp( lyrTmp.fieldName );
if ( exp.hasEvalError() )
{
QgsDebugMsgLevel( "Prepare error:" + exp.evalErrorString(), 4 );
return 0;
}
}
else
{
// If we aren't an expression, we check to see if we can find the column.
if ( layer->fieldNameIndex( lyrTmp.fieldName ) == -1 )
{
return 0;
}
}
}

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

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

// add field indices for label's text, from expression or field
if ( lyr.isExpression )
if ( lyrTmp.drawLabels )
{
// prepare expression for use in QgsPalLayerSettings::registerFeature()
QgsExpression* exp = lyr.getLabelExpression();
exp->prepare( layer->pendingFields() );
if ( exp->hasEvalError() )
// add field indices for label's text, from expression or field
if ( lyr.isExpression )
{
QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
// prepare expression for use in QgsPalLayerSettings::registerFeature()
QgsExpression* exp = lyr.getLabelExpression();
exp->prepare( layer->pendingFields() );
if ( exp->hasEvalError() )
{
QgsDebugMsgLevel( "Prepare error:" + exp->evalErrorString(), 4 );
}
foreach ( QString name, exp->referencedColumns() )
{
QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
attrNames.append( name );
}
}
foreach ( QString name, exp->referencedColumns() )
else
{
QgsDebugMsgLevel( "REFERENCED COLUMN = " + name, 4 );
attrNames.append( name );
attrNames.append( lyr.fieldName );
}
}
else
{
attrNames.append( lyr.fieldName );
}

// add field indices of data defined expression or field
QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
{
QgsDataDefined* dd = dIt.value();
if ( !dd->isActive() )
// add field indices of data defined expression or field
QMap< QgsPalLayerSettings::DataDefinedProperties, QgsDataDefined* >::const_iterator dIt = lyr.dataDefinedProperties.constBegin();
for ( ; dIt != lyr.dataDefinedProperties.constEnd(); ++dIt )
{
continue;
}
QgsDataDefined* dd = dIt.value();
if ( !dd->isActive() )
{
continue;
}

// NOTE: the following also prepares any expressions for later use
// NOTE: the following also prepares any expressions for later use

// store parameters for data defined expressions
QMap<QString, QVariant> exprParams;
exprParams.insert( "scale", ctx.rendererScale() );
// store parameters for data defined expressions
QMap<QString, QVariant> exprParams;
exprParams.insert( "scale", ctx.rendererScale() );

dd->setExpressionParams( exprParams );
dd->setExpressionParams( exprParams );

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

//QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
foreach ( QString name, cols )
{
attrNames.append( name );
//QgsDebugMsgLevel( QString( "Data defined referenced columns:" ) + cols.join( "," ), 4 );
foreach ( QString name, cols )
{
attrNames.append( name );
}
}
}

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

Layer* l = mPal->addLayer( layer->id().toUtf8().data(),
arrangement,
priority, lyr.obstacle, true, true,
priority, lyr.obstacle, true, lyr.drawLabels,
lyr.displayAll );

if ( lyr.placementFlags )
Expand Down Expand Up @@ -4187,6 +4258,8 @@ void QgsPalLabeling::drawLabeling( QgsRenderContext& context )
}

const QgsPalLayerSettings& lyr = layer( layerName );
if ( !lyr.drawLabels )
continue;

// Copy to temp, editable layer settings
// these settings will be changed by any data defined values, then used for rendering label components
Expand Down
12 changes: 12 additions & 0 deletions src/core/qgspallabeling.h
Expand Up @@ -270,6 +270,15 @@ class CORE_EXPORT QgsPalLayerSettings
// whether to label this layer
bool enabled;

/** Whether to draw labels for this layer. For some layers it may be desirable
* to register their features as obstacles for other labels without requiring
* labels to be drawn for the layer itself. In this case drawLabels can be set
* to false and obstacle set to true, which will result in the layer acting
* as an obstacle but having no labels of its own.
* @note added in QGIS 2.12
*/
bool drawLabels;

//-- text style

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

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

QMap<DataDefinedProperties, QVariant> dataDefinedValues;
QgsExpression* expression;
Expand Down

0 comments on commit 388e404

Please sign in to comment.