mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
[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:
parent
aca49992a9
commit
8d72f13a57
@ -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
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -35,4 +35,8 @@ class Qgs25DRendererWidget : QgsRendererV2Widget
|
||||
Qgs25DRendererWidget( QgsVectorLayer* layer, QgsStyleV2* style, QgsFeatureRendererV2* renderer );
|
||||
|
||||
QgsFeatureRendererV2* renderer();
|
||||
|
||||
private:
|
||||
void apply();
|
||||
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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 )
|
||||
|
@ -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" ) );
|
||||
|
@ -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
|
||||
|
@ -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 )
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
||||
|
@ -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 )
|
||||
|
@ -45,6 +45,8 @@ class GUI_EXPORT Qgs25DRendererWidget : public QgsRendererV2Widget, Ui::Qgs25DRe
|
||||
void updateRenderer();
|
||||
|
||||
private:
|
||||
void apply() override;
|
||||
|
||||
Qgs25DRenderer* mRenderer;
|
||||
};
|
||||
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -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><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></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
|
@ -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()
|
||||
|
BIN
tests/testdata/control_images/expected_symbol_color_variable/expected_symbol_color_variable.png
vendored
Normal file
BIN
tests/testdata/control_images/expected_symbol_color_variable/expected_symbol_color_variable.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 MiB |
Loading…
x
Reference in New Issue
Block a user