From 8d72f13a5750f9f5d499bc182c9531730e06a515 Mon Sep 17 00:00:00 2001 From: Matthias Kuhn Date: Wed, 20 Jan 2016 08:21:32 +0100 Subject: [PATCH] [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 --- python/core/qgsexpressioncontext.sip | 12 ++++ python/core/symbology-ng/qgs25drenderer.sip | 28 +++------ .../gui/symbology-ng/qgs25drendererwidget.sip | 4 ++ .../qgsrendererv2propertiesdialog.sip | 8 +++ .../gui/symbology-ng/qgsrendererv2widget.sip | 20 ++++++ src/app/qgslabelinggui.cpp | 4 +- src/app/qgsvectorlayerproperties.cpp | 4 +- src/core/qgsexpression.cpp | 3 + src/core/qgsexpressioncontext.cpp | 20 ++++++ src/core/qgsexpressioncontext.h | 13 ++++ src/core/qgsvectorlayerlabelprovider.cpp | 27 +++++--- src/core/qgsvectorlayerlabelprovider.h | 6 +- src/core/qgsvectorlayerrenderer.cpp | 35 +++++++++-- src/core/symbology-ng/qgs25drenderer.cpp | 58 ++++++++---------- src/core/symbology-ng/qgs25drenderer.h | 31 +++------- src/core/symbology-ng/qgssymbolv2.cpp | 3 +- src/gui/symbology-ng/qgs25drendererwidget.cpp | 24 ++++++-- src/gui/symbology-ng/qgs25drendererwidget.h | 2 + .../qgsrendererv2propertiesdialog.cpp | 3 + .../qgsrendererv2propertiesdialog.h | 8 +++ src/gui/symbology-ng/qgsrendererv2widget.cpp | 12 +++- src/gui/symbology-ng/qgsrendererv2widget.h | 20 ++++++ .../symbollayer/qgs25drendererwidgetbase.ui | 23 ++++++- .../test_qgssymbolexpressionvariables.py | 16 +++++ .../expected_symbol_color_variable.png | Bin 0 -> 3570126 bytes 25 files changed, 287 insertions(+), 97 deletions(-) create mode 100644 tests/testdata/control_images/expected_symbol_color_variable/expected_symbol_color_variable.png diff --git a/python/core/qgsexpressioncontext.sip b/python/core/qgsexpressioncontext.sip index e7cac65329d..65dfc75e43b 100644 --- a/python/core/qgsexpressioncontext.sip +++ b/python/core/qgsexpressioncontext.sip @@ -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 diff --git a/python/core/symbology-ng/qgs25drenderer.sip b/python/core/symbology-ng/qgs25drenderer.sip index 5d59acda7c9..979b8d10474 100644 --- a/python/core/symbology-ng/qgs25drenderer.sip +++ b/python/core/symbology-ng/qgs25drenderer.sip @@ -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 */ diff --git a/python/gui/symbology-ng/qgs25drendererwidget.sip b/python/gui/symbology-ng/qgs25drendererwidget.sip index 8387606d43e..0a6a9bb65e8 100644 --- a/python/gui/symbology-ng/qgs25drendererwidget.sip +++ b/python/gui/symbology-ng/qgs25drendererwidget.sip @@ -35,4 +35,8 @@ class Qgs25DRendererWidget : QgsRendererV2Widget Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer ); QgsFeatureRendererV2* renderer(); + + private: + void apply(); + }; diff --git a/python/gui/symbology-ng/qgsrendererv2propertiesdialog.sip b/python/gui/symbology-ng/qgsrendererv2propertiesdialog.sip index bdbd3fa523a..b2e9ada041b 100644 --- a/python/gui/symbology-ng/qgsrendererv2propertiesdialog.sip +++ b/python/gui/symbology-ng/qgsrendererv2propertiesdialog.sip @@ -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(); diff --git a/python/gui/symbology-ng/qgsrendererv2widget.sip b/python/gui/symbology-ng/qgsrendererv2widget.sip index e21c69977a1..e5c71f3c679 100644 --- a/python/gui/symbology-ng/qgsrendererv2widget.sip +++ b/python/gui/symbology-ng/qgsrendererv2widget.sip @@ -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(); + }; diff --git a/src/app/qgslabelinggui.cpp b/src/app/qgslabelinggui.cpp index fb2a85115b1..631b2bcab3a 100644 --- a/src/app/qgslabelinggui.cpp +++ b/src/app/qgslabelinggui.cpp @@ -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; } diff --git a/src/app/qgsvectorlayerproperties.cpp b/src/app/qgsvectorlayerproperties.cpp index 6d4e74ad6db..c1702d51ac7 100644 --- a/src/app/qgsvectorlayerproperties.cpp +++ b/src/app/qgsvectorlayerproperties.cpp @@ -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 { diff --git a/src/core/qgsexpression.cpp b/src/core/qgsexpression.cpp index 587d03a8ea2..d4e8dbacfca 100644 --- a/src/core/qgsexpression.cpp +++ b/src/core/qgsexpression.cpp @@ -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 ) diff --git a/src/core/qgsexpressioncontext.cpp b/src/core/qgsexpressioncontext.cpp index cd341335993..44786e832de 100644 --- a/src/core/qgsexpressioncontext.cpp +++ b/src/core/qgsexpressioncontext.cpp @@ -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" ) ); diff --git a/src/core/qgsexpressioncontext.h b/src/core/qgsexpressioncontext.h index 26d7800cea1..38082cf0516 100644 --- a/src/core/qgsexpressioncontext.h +++ b/src/core/qgsexpressioncontext.h @@ -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 diff --git a/src/core/qgsvectorlayerlabelprovider.cpp b/src/core/qgsvectorlayerlabelprovider.cpp index dca31288af2..476219b0af4 100644 --- a/src/core/qgsvectorlayerlabelprovider.cpp +++ b/src/core/qgsvectorlayerlabelprovider.cpp @@ -281,19 +281,33 @@ QList QgsVectorLayerLabelProvider::labelFeatures( QgsRenderCon request.setSubsetOfAttributes( attrNames, mFields ); QgsFeatureIterator fit = mSource->getFeatures( request ); + QgsExpressionContextScope* symbolScope = nullptr; QgsFeature fet; while ( fit.nextFeature( fet ) ) { QScopedPointer 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 ) diff --git a/src/core/qgsvectorlayerlabelprovider.h b/src/core/qgsvectorlayerlabelprovider.h index 2742ec21834..3ad6049c531 100644 --- a/src/core/qgsvectorlayerlabelprovider.h +++ b/src/core/qgsvectorlayerlabelprovider.h @@ -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 diff --git a/src/core/qgsvectorlayerrenderer.cpp b/src/core/qgsvectorlayerrenderer.cpp index 285dc7eae1e..86bf58b26b2 100644 --- a/src/core/qgsvectorlayerrenderer.cpp +++ b/src/core/qgsvectorlayerrenderer.cpp @@ -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 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 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 ); diff --git a/src/core/symbology-ng/qgs25drenderer.cpp b/src/core/symbology-ng/qgs25drenderer.cpp index ac0a3d2f18f..c349b9d6dec 100644 --- a/src/core/symbology-ng/qgs25drenderer.cpp +++ b/src/core/symbology-ng/qgs25drenderer.cpp @@ -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( 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(); diff --git a/src/core/symbology-ng/qgs25drenderer.h b/src/core/symbology-ng/qgs25drenderer.h index b59d41f78d8..cda3e5ca325 100644 --- a/src/core/symbology-ng/qgs25drenderer.h +++ b/src/core/symbology-ng/qgs25drenderer.h @@ -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 mSymbol; - - QgsDataDefined mHeight; - int mAngle; }; #endif // QGS25DRENDERER_H diff --git a/src/core/symbology-ng/qgssymbolv2.cpp b/src/core/symbology-ng/qgssymbolv2.cpp index f5306726699..5a38ff7655a 100644 --- a/src/core/symbology-ng/qgssymbolv2.cpp +++ b/src/core/symbology-ng/qgssymbolv2.cpp @@ -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 ); diff --git a/src/gui/symbology-ng/qgs25drendererwidget.cpp b/src/gui/symbology-ng/qgs25drendererwidget.cpp index b7b9e14013b..0d334930b3f 100644 --- a/src/gui/symbology-ng/qgs25drendererwidget.cpp +++ b/src/gui/symbology-ng/qgs25drendererwidget.cpp @@ -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 ) diff --git a/src/gui/symbology-ng/qgs25drendererwidget.h b/src/gui/symbology-ng/qgs25drendererwidget.h index d8c31b6dc9e..4ea8ec59f54 100644 --- a/src/gui/symbology-ng/qgs25drendererwidget.h +++ b/src/gui/symbology-ng/qgs25drendererwidget.h @@ -45,6 +45,8 @@ class GUI_EXPORT Qgs25DRendererWidget : public QgsRendererV2Widget, Ui::Qgs25DRe void updateRenderer(); private: + void apply() override; + Qgs25DRenderer* mRenderer; }; diff --git a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp index e9ad9af1340..0b3019ded9d 100644 --- a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp +++ b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.cpp @@ -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 ) { diff --git a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.h b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.h index 2e146402aa9..4418092be5c 100644 --- a/src/gui/symbology-ng/qgsrendererv2propertiesdialog.h +++ b/src/gui/symbology-ng/qgsrendererv2propertiesdialog.h @@ -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(); diff --git a/src/gui/symbology-ng/qgsrendererv2widget.cpp b/src/gui/symbology-ng/qgsrendererv2widget.cpp index ec98b9f0b2e..03e7e65ab12 100644 --- a/src/gui/symbology-ng/qgsrendererv2widget.cpp +++ b/src/gui/symbology-ng/qgsrendererv2widget.cpp @@ -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( symbol )->setDataDefinedWidth( dd ); } + +void QgsRendererV2Widget::apply() +{ + +} diff --git a/src/gui/symbology-ng/qgsrendererv2widget.h b/src/gui/symbology-ng/qgsrendererv2widget.h index a98fe3ad322..19eeb0687cf 100644 --- a/src/gui/symbology-ng/qgsrendererv2widget.h +++ b/src/gui/symbology-ng/qgsrendererv2widget.h @@ -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(); + }; diff --git a/src/ui/symbollayer/qgs25drendererwidgetbase.ui b/src/ui/symbollayer/qgs25drendererwidgetbase.ui index 4775467c385..911cb676b89 100644 --- a/src/ui/symbollayer/qgs25drendererwidgetbase.ui +++ b/src/ui/symbollayer/qgs25drendererwidgetbase.ui @@ -6,8 +6,8 @@ 0 0 - 602 - 395 + 657 + 587 @@ -62,7 +62,7 @@ - + Shadow @@ -98,6 +98,13 @@ + + + + Shade walls based on exposition + + + @@ -114,6 +121,16 @@ + + + + <html><head/><body><p><span style=" font-weight:600;">Advanced Styling</span><br/>This page helps to configure the 2.5D effect as easily as possible with some basic parameters.</p><p>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.</p><p><span style=" font-weight:600;">Overlay problems</span></p><p>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.</p><p>In such cases you can avoid rendering problems by cutting the feature in front into smaller pieces.</p></body></html> + + + true + + + diff --git a/tests/src/python/test_qgssymbolexpressionvariables.py b/tests/src/python/test_qgssymbolexpressionvariables.py index b036e92af46..43811269bd4 100644 --- a/tests/src/python/test_qgssymbolexpressionvariables.py +++ b/tests/src/python/test_qgssymbolexpressionvariables.py @@ -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() diff --git a/tests/testdata/control_images/expected_symbol_color_variable/expected_symbol_color_variable.png b/tests/testdata/control_images/expected_symbol_color_variable/expected_symbol_color_variable.png new file mode 100644 index 0000000000000000000000000000000000000000..20ff9a9420983be6278b4f2501a66227a5497b97 GIT binary patch literal 3570126 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

^o?sJ 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