Skip to content

Commit

Permalink
[25d] Improve convertability to other layers
Browse files Browse the repository at this point in the history
 * Move height and angle expressions for 2.5D renderer to layer
 * Apply color based on main symbol color

This makes the transition to other renderers easy.

Fixes #14132
  • Loading branch information
m-kuhn committed Jan 21, 2016
1 parent aca4999 commit 8d72f13
Show file tree
Hide file tree
Showing 25 changed files with 287 additions and 97 deletions.
12 changes: 12 additions & 0 deletions python/core/qgsexpressioncontext.sip
Expand Up @@ -391,6 +391,8 @@ class QgsExpressionContext
static const QString EXPR_FIELDS;
static const QString EXPR_FEATURE;
static const QString EXPR_ORIGINAL_VALUE;
static const QString EXPR_SYMBOL_COLOR;
static const QString EXPR_SYMBOL_ANGLE;
};

/** \ingroup core
Expand Down Expand Up @@ -488,6 +490,16 @@ class QgsExpressionContextUtils
*/
static QgsExpressionContextScope* mapSettingsScope( const QgsMapSettings &mapSettings ) /Factory/;

/**
* Updates a symbol scope related to a QgsSymbolV2 to an expression context. If there is no existing scope
* provided, a new one will be generated and returned.
* @param symbol symbol to extract properties from
* @param symbolScope optional pointer to an existing scope to update
* @note added in QGIS 2.14
*/
static QgsExpressionContextScope* updateSymbolScope( const QgsSymbolV2* symbol, QgsExpressionContextScope* symbolScope = nullptr ) /Factory/;


/** Creates a new scope which contains variables and functions relating to a QgsComposition.
* For instance, number of pages and page sizes.
* @param composition source composition
Expand Down
28 changes: 10 additions & 18 deletions python/core/symbology-ng/qgs25drenderer.sip
Expand Up @@ -39,24 +39,6 @@ class Qgs25DRenderer : QgsFeatureRendererV2
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature, QgsRenderContext& context );
virtual QgsSymbolV2List symbols( QgsRenderContext& context );

/**
* Get the field or expression used to determine the height of extrusion
*/
QgsDataDefined height() const;
/**
* Set the field or expression used to determine the height of extrusion
*/
void setHeight( const QgsDataDefined& height );

/**
* Get the angle for the extrusion effect
*/
int angle() const;
/**
* Set the angle for the extrusion effect
*/
void setAngle( int angle );

/**
* Get the roof color
*/
Expand All @@ -77,6 +59,16 @@ class Qgs25DRenderer : QgsFeatureRendererV2
*/
void setWallColor( const QColor& wallColor );

/**
* Set wall shading enabled
*/
void setWallShadingEnabled( bool enabled );

/**
* Get wall shading enabled
*/
bool wallShadingEnabled();

/**
* Get the shadow's color
*/
Expand Down
4 changes: 4 additions & 0 deletions python/gui/symbology-ng/qgs25drendererwidget.sip
Expand Up @@ -35,4 +35,8 @@ class Qgs25DRendererWidget : QgsRendererV2Widget
Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );

QgsFeatureRendererV2* renderer();

private:
void apply();

};
8 changes: 8 additions & 0 deletions python/gui/symbology-ng/qgsrendererv2propertiesdialog.sip
Expand Up @@ -15,6 +15,14 @@ class QgsRendererV2PropertiesDialog : QDialog
*/
void setMapCanvas( QgsMapCanvas* canvas );

signals:
/**
* Emitted when expression context variables on the associated
* vector layers have been changed. Will request the parent dialog
* to re-synchronize with the variables.
*/
void layerVariablesChanged();

public slots:
//! called when user changes renderer type
void rendererChanged();
Expand Down
20 changes: 20 additions & 0 deletions python/gui/symbology-ng/qgsrendererv2widget.sip
Expand Up @@ -34,6 +34,19 @@ class QgsRendererV2Widget : QWidget
*/
const QgsVectorLayer* vectorLayer() const;

/**
* This method should be called whenever the renderer is actually set on the layer.
*/
void applyChanges();

signals:
/**
* Emitted when expression context variables on the associated
* vector layers have been changed. Will request the parent dialog
* to re-synchronize with the variables.
*/
void layerVariablesChanged();

protected:
/** Subclasses may provide the capability of changing multiple symbols at once by implementing the following two methods
and by connecting the slot contextMenuViewCategories(const QPoint&)*/
Expand All @@ -58,6 +71,13 @@ class QgsRendererV2Widget : QWidget
virtual void copy();
virtual void paste();

private:
/**
* This will be called whenever the renderer is set on a layer.
* This can be overwritten in subclasses.
*/
virtual void apply();

};


Expand Down
4 changes: 3 additions & 1 deletion src/app/qgslabelinggui.cpp
Expand Up @@ -50,9 +50,11 @@ static QgsExpressionContext _getExpressionContext( const void* context )
if ( layer )
expContext << QgsExpressionContextUtils::layerScope( layer );

expContext << QgsExpressionContextUtils::updateSymbolScope( nullptr );

//TODO - show actual value
expContext.setOriginalValueVariable( QVariant() );
expContext.setHighlightedVariables( QStringList() << QgsExpressionContext::EXPR_ORIGINAL_VALUE );
expContext.setHighlightedVariables( QStringList() << QgsExpressionContext::EXPR_ORIGINAL_VALUE << QgsExpressionContext::EXPR_SYMBOL_COLOR );

return expContext;
}
Expand Down
4 changes: 3 additions & 1 deletion src/app/qgsvectorlayerproperties.cpp
Expand Up @@ -1263,10 +1263,12 @@ void QgsVectorLayerProperties::updateSymbologyPage()
mRendererDialog = new QgsRendererV2PropertiesDialog( layer, QgsStyleV2::defaultStyle(), true );
mRendererDialog->setMapCanvas( QgisApp::instance()->mapCanvas() );

connect( mRendererDialog, SIGNAL( layerVariablesChanged() ), this, SLOT( updateVariableEditor() ) );

// display the menu to choose the output format (fix #5136)
mActionSaveStyleAs->setText( tr( "Save Style" ) );
mActionSaveStyleAs->setMenu( mSaveAsMenu );
QObject::disconnect( mActionSaveStyleAs, SIGNAL( triggered() ), this, SLOT( saveStyleAs_clicked() ) );
disconnect( mActionSaveStyleAs, SIGNAL( triggered() ), this, SLOT( saveStyleAs_clicked() ) );
}
else
{
Expand Down
3 changes: 3 additions & 0 deletions src/core/qgsexpression.cpp
Expand Up @@ -4546,6 +4546,9 @@ void QgsExpression::initVariableHelp()
//symbol variables
gVariableHelpTexts.insert( "geometry_part_count", QCoreApplication::translate( "variable_help", "Number of parts in rendered feature's geometry." ) );
gVariableHelpTexts.insert( "geometry_part_num", QCoreApplication::translate( "variable_help", "Current geometry part number for feature being rendered." ) );

gVariableHelpTexts.insert( "symbol_color", QCoreApplication::translate( "symbol_color", "Color of symbol used to render the feature." ) );
gVariableHelpTexts.insert( "symbol_angle", QCoreApplication::translate( "symbol_angle", "Angle of symbol used to render the feature (valid for marker symbols only)." ) );
}

QString QgsExpression::variableHelpText( const QString &variableName, bool showValue, const QVariant &value )
Expand Down
20 changes: 20 additions & 0 deletions src/core/qgsexpressioncontext.cpp
Expand Up @@ -33,6 +33,8 @@
const QString QgsExpressionContext::EXPR_FIELDS( "_fields_" );
const QString QgsExpressionContext::EXPR_FEATURE( "_feature_" );
const QString QgsExpressionContext::EXPR_ORIGINAL_VALUE( "value" );
const QString QgsExpressionContext::EXPR_SYMBOL_COLOR( "symbol_color" );
const QString QgsExpressionContext::EXPR_SYMBOL_ANGLE( "symbol_angle" );

//
// QgsExpressionContextScope
Expand Down Expand Up @@ -716,6 +718,24 @@ QgsExpressionContextScope* QgsExpressionContextUtils::mapSettingsScope( const Qg
return scope;
}

QgsExpressionContextScope* QgsExpressionContextUtils::updateSymbolScope( const QgsSymbolV2* symbol, QgsExpressionContextScope* symbolScope )
{
if ( !symbolScope )
symbolScope = new QgsExpressionContextScope( QObject::tr( "Symbol Scope" ) );

symbolScope->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_SYMBOL_COLOR, symbol ? symbol->color() : QColor(), true ) );

double angle = 0.0;
const QgsMarkerSymbolV2* markerSymbol = dynamic_cast< const QgsMarkerSymbolV2* >( symbol );
if ( markerSymbol )
{
angle = markerSymbol->angle();
}
symbolScope->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_SYMBOL_ANGLE, angle, true ) );

return symbolScope;
}

QgsExpressionContextScope *QgsExpressionContextUtils::compositionScope( const QgsComposition *composition )
{
QgsExpressionContextScope* scope = new QgsExpressionContextScope( QObject::tr( "Composition" ) );
Expand Down
13 changes: 13 additions & 0 deletions src/core/qgsexpressioncontext.h
Expand Up @@ -28,6 +28,7 @@ class QgsComposition;
class QgsComposerItem;
class QgsAtlasComposition;
class QgsMapSettings;
class QgsSymbolV2;

/** \ingroup core
* \class QgsScopedExpressionFunction
Expand Down Expand Up @@ -425,6 +426,8 @@ class CORE_EXPORT QgsExpressionContext
static const QString EXPR_FIELDS;
static const QString EXPR_FEATURE;
static const QString EXPR_ORIGINAL_VALUE;
static const QString EXPR_SYMBOL_COLOR;
static const QString EXPR_SYMBOL_ANGLE;

private:

Expand Down Expand Up @@ -519,6 +522,16 @@ class CORE_EXPORT QgsExpressionContextUtils
*/
static QgsExpressionContextScope* mapSettingsScope( const QgsMapSettings &mapSettings );

/**
* Updates a symbol scope related to a QgsSymbolV2 to an expression context. If there is no existing scope
* provided, a new one will be generated and returned.
* @param symbol symbol to extract properties from
* @param symbolScope optional pointer to an existing scope to update
* @note added in QGIS 2.14
*/
static QgsExpressionContextScope* updateSymbolScope( const QgsSymbolV2* symbol, QgsExpressionContextScope* symbolScope = nullptr );


/** Creates a new scope which contains variables and functions relating to a QgsComposition.
* For instance, number of pages and page sizes.
* @param composition source composition
Expand Down
27 changes: 19 additions & 8 deletions src/core/qgsvectorlayerlabelprovider.cpp
Expand Up @@ -281,19 +281,33 @@ QList<QgsLabelFeature*> QgsVectorLayerLabelProvider::labelFeatures( QgsRenderCon
request.setSubsetOfAttributes( attrNames, mFields );
QgsFeatureIterator fit = mSource->getFeatures( request );

QgsExpressionContextScope* symbolScope = nullptr;
QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
QScopedPointer<QgsGeometry> obstacleGeometry;
if ( fet.constGeometry()->type() == QGis::Point )
if ( mRenderer )
{
//point feature, use symbol bounds as obstacle
obstacleGeometry.reset( getPointObstacleGeometry( fet, ctx, mRenderer ) );
QgsSymbolV2List symbols = mRenderer->originalSymbolsForFeature( fet, ctx );
if ( !symbols.isEmpty() && fet.constGeometry()->type() == QGis::Point )
{
//point feature, use symbol bounds as obstacle
obstacleGeometry.reset( QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, ctx, symbols ) );
}
if ( !symbols.isEmpty() )
{
symbolScope = QgsExpressionContextUtils::updateSymbolScope( symbols.at( 0 ), symbolScope );
if ( !ctx.expressionContext().scopes().contains( symbolScope ) )
ctx.expressionContext().appendScope( symbolScope );
}
}
ctx.expressionContext().setFeature( fet );
registerFeature( fet, ctx, obstacleGeometry.data() );
}

if ( ctx.expressionContext().lastScope() == symbolScope )
ctx.expressionContext().popScope();

if ( mRenderer )
mRenderer->stopRender( ctx );

Expand All @@ -308,14 +322,11 @@ void QgsVectorLayerLabelProvider::registerFeature( QgsFeature& feature, QgsRende
mLabels << label;
}

QgsGeometry* QgsVectorLayerLabelProvider::getPointObstacleGeometry( QgsFeature& fet, QgsRenderContext& context, QgsFeatureRendererV2* renderer )
QgsGeometry* QgsVectorLayerLabelProvider::getPointObstacleGeometry( QgsFeature& fet, QgsRenderContext& context, const QgsSymbolV2List& symbols )
{
if ( !fet.constGeometry() || fet.constGeometry()->isEmpty() || fet.constGeometry()->type() != QGis::Point || !renderer )
if ( !fet.constGeometry() || fet.constGeometry()->isEmpty() || fet.constGeometry()->type() != QGis::Point )
return nullptr;

//calculate bounds for symbols for feature
QgsSymbolV2List symbols = renderer->originalSymbolsForFeature( fet, context );

bool isMultiPoint = fet.constGeometry()->geometry()->nCoordinates() > 1;
QgsAbstractGeometryV2* obstacleGeom = nullptr;
if ( isMultiPoint )
Expand Down
6 changes: 4 additions & 2 deletions src/core/qgsvectorlayerlabelprovider.h
Expand Up @@ -17,9 +17,11 @@
#define QGSVECTORLAYERLABELPROVIDER_H

#include "qgslabelingenginev2.h"
#include "qgsrendererv2.h"

class QgsAbstractFeatureSource;
class QgsFeatureRendererV2;
class QgsSymbolV2;

/**
* @brief The QgsVectorLayerLabelProvider class implements a label provider
Expand Down Expand Up @@ -80,10 +82,10 @@ class CORE_EXPORT QgsVectorLayerLabelProvider : public QgsAbstractLabelProvider
* point, and ensures that labels will not overlap large or offset points.
* @param fet point feature
* @param context render context
* @param renderer renderer used for layer, required to determine symbols rendered for point feature
* @param symbols symbols rendered for point feature
* @note added in QGIS 2.14
*/
static QgsGeometry* getPointObstacleGeometry( QgsFeature& fet, QgsRenderContext& context, QgsFeatureRendererV2* renderer );
static QgsGeometry* getPointObstacleGeometry( QgsFeature& fet, QgsRenderContext& context, const QgsSymbolV2List& symbols );

protected:
//! initialization method - called from constructors
Expand Down
35 changes: 31 additions & 4 deletions src/core/qgsvectorlayerrenderer.cpp
Expand Up @@ -286,6 +286,9 @@ void QgsVectorLayerRenderer::setGeometryCachePointer( QgsGeometryCache* cache )

void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit )
{
QgsExpressionContextScope* symbolScope = QgsExpressionContextUtils::updateSymbolScope( nullptr );
mContext.expressionContext().appendScope( symbolScope );

QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
Expand Down Expand Up @@ -332,10 +335,18 @@ void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit )
if ( mContext.labelingEngineV2() )
{
QScopedPointer<QgsGeometry> obstacleGeometry;
if ( fet.constGeometry()->type() == QGis::Point )
QgsSymbolV2List symbols = mRendererV2->originalSymbolsForFeature( fet, mContext );

if ( !symbols.isEmpty() && fet.constGeometry()->type() == QGis::Point )
{
obstacleGeometry.reset( QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, mContext, mRendererV2 ) );
obstacleGeometry.reset( QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, mContext, symbols ) );
}

if ( !symbols.isEmpty() )
{
QgsExpressionContextUtils::updateSymbolScope( symbols.at( 0 ), symbolScope );
}

if ( mLabelProvider )
{
mLabelProvider->registerFeature( fet, mContext, obstacleGeometry.data() );
Expand All @@ -355,6 +366,8 @@ void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit )
}
}

mContext.expressionContext().popScope();

stopRendererV2( nullptr );
}

Expand All @@ -371,6 +384,9 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
selRenderer->startRender( mContext, mFields );
}

QgsExpressionContextScope* symbolScope = QgsExpressionContextUtils::updateSymbolScope( nullptr );
mContext.expressionContext().appendScope( symbolScope );

// 1. fetch features
QgsFeature fet;
while ( fit.nextFeature( fet ) )
Expand All @@ -382,6 +398,7 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
{
qDebug( "rendering stop!" );
stopRendererV2( selRenderer );
mContext.expressionContext().popScope();
return;
}

Expand Down Expand Up @@ -420,10 +437,18 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
if ( mContext.labelingEngineV2() )
{
QScopedPointer<QgsGeometry> obstacleGeometry;
if ( fet.constGeometry()->type() == QGis::Point )
QgsSymbolV2List symbols = mRendererV2->originalSymbolsForFeature( fet, mContext );

if ( !symbols.isEmpty() && fet.constGeometry()->type() == QGis::Point )
{
obstacleGeometry.reset( QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, mContext, mRendererV2 ) );
obstacleGeometry.reset( QgsVectorLayerLabelProvider::getPointObstacleGeometry( fet, mContext, symbols ) );
}

if ( !symbols.isEmpty() )
{
QgsExpressionContextUtils::updateSymbolScope( symbols.at( 0 ), symbolScope );
}

if ( mLabelProvider )
{
mLabelProvider->registerFeature( fet, mContext, obstacleGeometry.data() );
Expand All @@ -435,6 +460,8 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
}
}

mContext.expressionContext().popScope();

// find out the order
QgsSymbolV2LevelOrder levels;
QgsSymbolV2List symbols = mRendererV2->symbols( mContext );
Expand Down

2 comments on commit 8d72f13

@nirvn
Copy link
Contributor

@nirvn nirvn commented on 8d72f13 Jan 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-kuhn working great.

Applying a gradient fill of type radial with coord mode set to viewport is real nice :)
radial-gradient png

@m-kuhn
Copy link
Member Author

@m-kuhn m-kuhn commented on 8d72f13 Jan 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nirvn hint: use the right hand rule to digitize (or find a toll that converts the data for you)

Please sign in to comment.