[25d] Improve convertability to other layers

* 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
This commit is contained in:
Matthias Kuhn 2016-01-20 08:21:32 +01:00
parent aca49992a9
commit 8d72f13a57
25 changed files with 287 additions and 97 deletions

View File

@ -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
@ -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

View File

@ -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
*/
@ -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
*/

View File

@ -35,4 +35,8 @@ class Qgs25DRendererWidget : QgsRendererV2Widget
Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );
QgsFeatureRendererV2* renderer();
private:
void apply();
};

View File

@ -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();

View File

@ -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&)*/
@ -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();
};

View File

@ -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;
}

View File

@ -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
{

View File

@ -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 )

View File

@ -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
@ -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" ) );

View File

@ -28,6 +28,7 @@ class QgsComposition;
class QgsComposerItem;
class QgsAtlasComposition;
class QgsMapSettings;
class QgsSymbolV2;
/** \ingroup core
* \class QgsScopedExpressionFunction
@ -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:
@ -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

View File

@ -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 );
@ -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 )

View File

@ -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
@ -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

View File

@ -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 ) )
{
@ -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() );
@ -355,6 +366,8 @@ void QgsVectorLayerRenderer::drawRendererV2( QgsFeatureIterator& fit )
}
}
mContext.expressionContext().popScope();
stopRendererV2( nullptr );
}
@ -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 ) )
@ -382,6 +398,7 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
{
qDebug( "rendering stop!" );
stopRendererV2( selRenderer );
mContext.expressionContext().popScope();
return;
}
@ -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() );
@ -435,6 +460,8 @@ void QgsVectorLayerRenderer::drawRendererV2Levels( QgsFeatureIterator& fit )
}
}
mContext.expressionContext().popScope();
// find out the order
QgsSymbolV2LevelOrder levels;
QgsSymbolV2List symbols = mRendererV2->symbols( mContext );

View File

@ -48,6 +48,16 @@
" )" \
")"
#define WALL_SHADING_EXPRESSION \
"set_color_part( " \
" @symbol_color," \
" 'value'," \
" 40 + 20 * abs( $pi - azimuth( " \
" point_n( geometry_n($geometry, @geometry_part_num) , 1 ), " \
" point_n( geometry_n($geometry, @geometry_part_num) , 2 )" \
" ) ) " \
")"
Qgs25DRenderer::Qgs25DRenderer()
: QgsFeatureRendererV2( "25dRenderer" )
{
@ -67,6 +77,8 @@ Qgs25DRenderer::Qgs25DRenderer()
roofProperties.insert( "symbolType", "Fill" );
QgsSymbolLayerV2* roof = QgsGeometryGeneratorSymbolLayerV2::create( roofProperties );
floor->setLocked( true );
mSymbol->appendSymbolLayer( floor );
mSymbol->appendSymbolLayer( walls );
mSymbol->appendSymbolLayer( roof );
@ -81,12 +93,11 @@ Qgs25DRenderer::Qgs25DRenderer()
setRoofColor( QColor( "#fdbf6f" ) );
setWallColor( QColor( "#777777" ) );
wallLayer()->setDataDefinedProperty( "color", new QgsDataDefined( QString( WALL_SHADING_EXPRESSION ) ) );
setShadowSpread( 4 );
setShadowColor( QColor( "#1111111" ) );
setHeight( QString( "20" ) );
setAngle( 40 );
QgsFeatureRequest::OrderBy orderBy;
orderBy << QgsFeatureRequest::OrderByClause(
ORDER_BY_EXPRESSION,
@ -100,8 +111,6 @@ QDomElement Qgs25DRenderer::save( QDomDocument& doc )
QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
rendererElem.setAttribute( "type", "25dRenderer" );
rendererElem.setAttribute( "height", mHeight.expressionOrField() );
rendererElem.setAttribute( "angle", mAngle );
QDomElement symbolElem = QgsSymbolLayerV2Utils::saveSymbol( "symbol", mSymbol.data(), doc );
@ -112,19 +121,14 @@ QDomElement Qgs25DRenderer::save( QDomDocument& doc )
QgsFeatureRendererV2* Qgs25DRenderer::create( QDomElement& element )
{
Q_UNUSED( element )
Qgs25DRenderer* renderer = new Qgs25DRenderer();
renderer->mHeight.setField( element.attribute( "height" ) );
renderer->mAngle = element.attribute( "angle", "45" ).toInt();
return renderer;
}
void Qgs25DRenderer::startRender( QgsRenderContext& context, const QgsFields& fields )
{
QgsExpressionContextScope* scope = new QgsExpressionContextScope( "2.5D Renderer" );
scope->setVariable( "qgis_25d_height", mHeight.expressionOrField() );
scope->setVariable( "qgis_25d_angle", mAngle );
context.expressionContext().appendScope( scope );
mSymbol->startRender( context, &fields );
}
@ -142,8 +146,6 @@ QgsFeatureRendererV2* Qgs25DRenderer::clone() const
{
Qgs25DRenderer* c = new Qgs25DRenderer();
c->mSymbol.reset( mSymbol->clone() );
c->mAngle = mAngle;
c->mHeight = mHeight;
return c;
}
@ -162,26 +164,6 @@ QgsSymbolV2List Qgs25DRenderer::symbols( QgsRenderContext& context )
return lst;
}
QgsDataDefined Qgs25DRenderer::height() const
{
return mHeight;
}
void Qgs25DRenderer::setHeight( const QgsDataDefined& height )
{
mHeight = height;
}
int Qgs25DRenderer::angle() const
{
return mAngle;
}
void Qgs25DRenderer::setAngle( int angle )
{
mAngle = angle;
}
QgsFillSymbolLayerV2* Qgs25DRenderer::roofLayer() const
{
return static_cast<QgsFillSymbolLayerV2*>( mSymbol->symbolLayer( 2 )->subSymbol()->symbolLayer( 0 ) );
@ -238,6 +220,16 @@ void Qgs25DRenderer::setWallColor( const QColor& wallColor )
wallLayer()->setOutlineColor( wallColor );
}
void Qgs25DRenderer::setWallShadingEnabled( bool enabled )
{
wallLayer()->getDataDefinedProperty( "color" )->setActive( enabled );
}
bool Qgs25DRenderer::wallShadingEnabled()
{
return wallLayer()->getDataDefinedProperty( "color" )->isActive();
}
QColor Qgs25DRenderer::roofColor() const
{
return roofLayer()->fillColor();

View File

@ -43,24 +43,6 @@ class CORE_EXPORT Qgs25DRenderer : public QgsFeatureRendererV2
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature, QgsRenderContext& context ) override;
virtual QgsSymbolV2List symbols( QgsRenderContext& context ) override;
/**
* 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
*/
@ -81,6 +63,16 @@ class CORE_EXPORT Qgs25DRenderer : public 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
*/
@ -123,9 +115,6 @@ class CORE_EXPORT Qgs25DRenderer : public QgsFeatureRendererV2
QgsOuterGlowEffect* glowEffect() const;
QScopedPointer<QgsSymbolV2> mSymbol;
QgsDataDefined mHeight;
int mAngle;
};
#endif // QGS25DRENDERER_H

View File

@ -441,7 +441,7 @@ void QgsSymbolV2::startRender( QgsRenderContext& context, const QgsFields* field
QgsSymbolV2RenderContext symbolContext( context, outputUnit(), mAlpha, false, mRenderHints, nullptr, fields, mapUnitScale() );
QgsExpressionContextScope* scope = new QgsExpressionContextScope( QApplication::translate( "QgsSymbolV2", "Symbol Scope" ) );
QgsExpressionContextScope* scope = QgsExpressionContextUtils::updateSymbolScope( this );
mSymbolRenderContext->setExpressionContextScope( scope );
@ -714,6 +714,7 @@ void QgsSymbolV2::renderFeature( const QgsFeature& feature, QgsRenderContext& co
}
context.expressionContext().appendScope( mSymbolRenderContext->expressionContextScope() );
QgsExpressionContextUtils::updateSymbolScope( this, mSymbolRenderContext->expressionContextScope() );
mSymbolRenderContext->expressionContextScope()->setVariable( "geometry_part_count", segmentizedGeometry->geometry()->partCount() );
mSymbolRenderContext->expressionContextScope()->setVariable( "geometry_part_num", 1 );

View File

@ -15,6 +15,8 @@
***************************************************************************/
#include "qgs25drendererwidget.h"
#include "qgsmaplayerstylemanager.h"
Qgs25DRendererWidget::Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )
: QgsRendererV2Widget( layer, style )
, mRenderer( nullptr )
@ -42,13 +44,19 @@ Qgs25DRendererWidget::Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* s
}
mHeightWidget->setLayer( layer );
mHeightWidget->setField( mRenderer->height().expressionOrField() );
mAngleWidget->setValue( mRenderer->angle() );
QgsExpressionContextScope* scope = QgsExpressionContextUtils::layerScope( mLayer );
QVariant height = scope->variable( "qgis_25d_height" );
QVariant angle = scope->variable( "qgis_25d_angle" );
mHeightWidget->setField( height.isNull() ? "10" : height.toString() );
mAngleWidget->setValue( angle.isNull() ? 70 : angle.toDouble() );
mWallColorButton->setColor( mRenderer->wallColor() );
mRoofColorButton->setColor( mRenderer->roofColor() );
mShadowColorButton->setColor( mRenderer->shadowColor() );
mShadowEnabledWidget->setEnabled( mRenderer->shadowEnabled() );
mShadowSizeWidget->setValue( mRenderer->shadowSpread() );
mWallExpositionShading->setChecked( mRenderer->wallShadingEnabled() );
connect( mAngleWidget, SIGNAL( valueChanged( int ) ), this, SLOT( updateRenderer() ) );
connect( mHeightWidget, SIGNAL( fieldChanged( QString ) ), this, SLOT( updateRenderer() ) );
@ -57,6 +65,7 @@ Qgs25DRendererWidget::Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* s
connect( mShadowColorButton, SIGNAL( colorChanged( QColor ) ), this, SLOT( updateRenderer() ) );
connect( mShadowEnabledWidget, SIGNAL( toggled( bool ) ), this, SLOT( updateRenderer() ) );
connect( mShadowSizeWidget, SIGNAL( valueChanged( double ) ), this, SLOT( updateRenderer() ) );
connect( mWallExpositionShading, SIGNAL( toggled( bool ) ), this, SLOT( updateRenderer() ) );
}
QgsFeatureRendererV2* Qgs25DRendererWidget::renderer()
@ -66,13 +75,20 @@ QgsFeatureRendererV2* Qgs25DRendererWidget::renderer()
void Qgs25DRendererWidget::updateRenderer()
{
mRenderer->setHeight( QgsDataDefined( mHeightWidget->currentText() ) );
mRenderer->setAngle( mAngleWidget->value() );
mRenderer->setRoofColor( mRoofColorButton->color() );
mRenderer->setWallColor( mWallColorButton->color() );
mRenderer->setShadowColor( mShadowColorButton->color() );
mRenderer->setShadowEnabled( mShadowEnabledWidget->isChecked() );
mRenderer->setShadowSpread( mShadowSizeWidget->value() );
mRenderer->setWallShadingEnabled( mWallExpositionShading->isChecked() );
}
void Qgs25DRendererWidget::apply()
{
QgsExpressionContextUtils::setLayerVariable( mLayer, "qgis_25d_height", mHeightWidget->currentText() );
QgsExpressionContextUtils::setLayerVariable( mLayer, "qgis_25d_angle", mAngleWidget->value() );
emit layerVariablesChanged();
}
QgsRendererV2Widget* Qgs25DRendererWidget::create( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer )

View File

@ -45,6 +45,8 @@ class GUI_EXPORT Qgs25DRendererWidget : public QgsRendererV2Widget, Ui::Qgs25DRe
void updateRenderer();
private:
void apply() override;
Qgs25DRenderer* mRenderer;
};

View File

@ -223,6 +223,7 @@ void QgsRendererV2PropertiesDialog::rendererChanged()
if ( mMapCanvas )
mActiveWidget->setMapCanvas( mMapCanvas );
changeOrderBy( mActiveWidget->renderer()->orderBy() );
connect( mActiveWidget, SIGNAL( layerVariablesChanged() ), this, SIGNAL( layerVariablesChanged() ) );
}
}
else
@ -239,6 +240,8 @@ void QgsRendererV2PropertiesDialog::apply()
return;
}
mActiveWidget->applyChanges();
QgsFeatureRendererV2* renderer = mActiveWidget->renderer();
if ( renderer )
{

View File

@ -47,6 +47,14 @@ class GUI_EXPORT QgsRendererV2PropertiesDialog : public QDialog, private Ui::Qgs
*/
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();

View File

@ -261,11 +261,16 @@ void QgsRendererV2Widget::setMapCanvas( QgsMapCanvas *canvas )
mMapCanvas = canvas;
}
const QgsMapCanvas*QgsRendererV2Widget::mapCanvas() const
const QgsMapCanvas* QgsRendererV2Widget::mapCanvas() const
{
return mMapCanvas;
}
void QgsRendererV2Widget::applyChanges()
{
apply();
}
////////////
@ -580,3 +585,8 @@ void QgsDataDefinedWidthDialog::setDataDefined( QgsSymbolV2 *symbol, const QgsDa
{
static_cast<QgsLineSymbolV2*>( symbol )->setDataDefinedWidth( dd );
}
void QgsRendererV2Widget::apply()
{
}

View File

@ -69,6 +69,19 @@ class GUI_EXPORT QgsRendererV2Widget : public QWidget
*/
const QgsVectorLayer* vectorLayer() const { return mLayer; }
/**
* 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:
QgsVectorLayer* mLayer;
QgsStyleV2* mStyle;
@ -100,6 +113,13 @@ class GUI_EXPORT QgsRendererV2Widget : public 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();
};

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>602</width>
<height>395</height>
<width>657</width>
<height>587</height>
</rect>
</property>
<property name="windowTitle">
@ -62,7 +62,7 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="mShadowEnabledWidget">
<property name="title">
<string>Shadow</string>
@ -98,6 +98,13 @@
</layout>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="mWallExpositionShading">
<property name="text">
<string>Shade walls based on exposition</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
@ -114,6 +121,16 @@
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_7">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Advanced Styling&lt;/span&gt;&lt;br/&gt;This page helps to configure the 2.5D effect as easily as possible with some basic parameters.&lt;/p&gt;&lt;p&gt;Once you have finished the basic styling, you can can convert this to another renderer (single, categorized, graduated) and fine-tune the appearance to your liking.&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Overlay problems&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Features are rendered based on their distance to the camera. It is sometimes possible that parts of a feature are in front of another feature by mistake. This happens if any part of the overlapped feature is closer to the camera than the overlapping feature.&lt;/p&gt;&lt;p&gt;In such cases you can avoid rendering problems by cutting the feature in front into smaller pieces.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>

View File

@ -101,5 +101,21 @@ class TestQgsSymbolExpressionVariables(TestCase):
self.assertTrue(result)
def testSymbolColor(self):
# Create rulebased style
sym1 = QgsFillSymbolV2.createSimple({'color': '#ff0000'})
renderer = QgsSingleSymbolRendererV2(sym1)
renderer.symbols()[0].symbolLayers()[0].setDataDefinedProperty('color', 'set_color_part( @symbol_color, \'value\', "Value" * 4)')
self.layer.setRendererV2(renderer)
# Setup rendering check
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(self.mapsettings)
renderchecker.setControlName('expected_symbol_color_variable')
result = renderchecker.runTest('symbol_color_variable')
self.assertTrue(result)
if __name__ == '__main__':
unittest.main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB